Pourquoi RecyclerView n'a-t-il pas onItemClickListener ()?

966

J'explorais RecyclerViewet j'ai été surpris de voir que cela RecyclerViewn'a pas onItemClickListener().

J'ai deux questions.

Question principale

Je veux savoir pourquoi Google a supprimé onItemClickListener()?

Y a-t-il un problème de performances ou autre chose?

Question secondaire

J'ai résolu mon problème en écrivant onClickdans mon RecyclerView.Adapter:

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
    }

    @Override
    public void onClick(View v) {

    }
}

Est-ce correct / existe-t-il une meilleure façon?

Tarun Varshney
la source
31
Pour que votre code fonctionne, vous devez ajouter itemLayoutView.setOnClickListener (this); dans le constructeur ViewHolder.
whitneyland
3
vous avez posé une belle question ... de nombreux développeurs ont le même doute sur RecyclerView et ListView.
venkat
17
Question: Pourquoi RecyclerView n'a pas OnItemClickListner ()? Toutes les réponses ci-dessous sauf 2: Comment ajouter un clic pour RecyclerView
Manohar Reddy
2
Je souhaite que quelqu'un prenne le temps de répondre auQUESTION
Ojonugwa Jude Ochalifu
1
Je pense qu'ils l'ont supprimé en raison de performances et de problèmes de mémoire. Mais regardez la réponse acceptée ... cela produira les mêmes problèmes. Je pense que vous avez fait une erreur en l'acceptant et maintenant d'autres personnes font des erreurs à cause de vous :).
Alex

Réponses:

1208

tl; dr 2016 Utilisez RxJava et un PublishSubject pour exposer un observable pour les clics.

public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    String[] mDataset = { "Data", "In", "Adapter" };

    private final PublishSubject<String> onClickSubject = PublishSubject.create();

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final String element = mDataset[position];

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               onClickSubject.onNext(element);
            }
        });
    }

    public Observable<String> getPositionClicks(){
        return onClickSubject.asObservable();
    }
}

Message d'origine:

Depuis l'introduction de ListView, onItemClickListenera été problématique. Au moment où vous avez un écouteur de clics pour l'un des éléments internes, le rappel ne sera pas déclenché, mais il n'a pas été notifié ou bien documenté (le cas échéant), il y avait donc beaucoup de confusion et de SO questions à ce sujet.

Étant donné que RecyclerViewcela va un peu plus loin et n'a pas de concept de ligne / colonne, mais plutôt un nombre arbitrairement disposé d'enfants, ils ont délégué le onClick à chacun d'eux, ou à la mise en œuvre du programmeur.

RecyclerviewNe pensez pas comme un ListViewremplacement 1: 1 mais plutôt comme un composant plus flexible pour les cas d'utilisation complexes. Et comme vous le dites, votre solution est ce que Google attend de vous. Vous avez maintenant un adaptateur qui peut déléguer onClick à une interface transmise au constructeur, qui est le modèle correct pour ListViewet Recyclerview.

public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {

    public TextView txtViewTitle;
    public ImageView imgViewIcon;
    public IMyViewHolderClicks mListener;

    public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
        super(itemLayoutView);
        mListener = listener;
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
        imgViewIcon.setOnClickListener(this);
        itemLayoutView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v instanceof ImageView){
           mListener.onTomato((ImageView)v);
        } else {
           mListener.onPotato(v);
        }
    }

    public static interface IMyViewHolderClicks {
        public void onPotato(View caller);
        public void onTomato(ImageView callerImage);
    }

}

puis sur votre adaptateur

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

   String[] mDataset = { "Data" };

   @Override
   public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout, parent, false);

       MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() { 
           public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); };
           public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); }
        });
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager) 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Get element from your dataset at this position 
        // Replace the contents of the view with that element 
        // Clear the ones that won't be used
        holder.txtViewTitle.setText(mDataset[position]);
    } 

    // Return the size of your dataset (invoked by the layout manager) 
    @Override 
    public int getItemCount() { 
        return mDataset.length;
    } 
  ...

Examinez maintenant ce dernier morceau de code: onCreateViewHolder(ViewGroup parent, int viewType)la signature suggère déjà différents types de vue. Pour chacun d'eux, vous aurez également besoin d'un viewholder différent, et par la suite chacun d'eux peut avoir un ensemble de clics différent. Ou vous pouvez simplement créer une vue générique qui prend n'importe quelle vue et une onClickListeneret s'applique en conséquence. Ou déléguez un niveau à l'orchestrateur afin que plusieurs fragments / activités aient la même liste avec un comportement de clic différent. Encore une fois, toute flexibilité est de votre côté.

C'est un composant vraiment nécessaire et assez proche de ce qu'étaient nos implémentations et améliorations internes ListViewjusqu'à présent. C'est bien que Google le reconnaisse enfin.

MLProgrammer-CiM
la source
78
RecyclerView.ViewHolder a une méthode getPosition (). Avec quelques ajustements à ce code, vous pouvez transmettre la position à l'auditeur.
se_bastiaan
13
Il a complètement raté le onBindViewHolder(holder, position), qui est l'endroit où les informations sont censées être remplies.
MLProgrammer-CiM
26
@se_bastiaan getPosition()est obsolète "Cette méthode est obsolète car sa signification est ambiguë en raison de la gestion asynchrone * des mises à jour de l'adaptateur. Veuillez utiliser {@link #getLayoutPosition ()} ou * {@link #getAdapterPosition ()} selon votre cas d'utilisation. "
upenpat
8
Vous avez plusieurs options. Mettre la position dans un champ int à l'intérieur du viewholder, le mettre à jour pendant la liaison, puis le renvoyer au clic en est un.
MLProgrammer-CiM
8
@ MLProgrammer-CiM "Vous avez plusieurs options. Mettre la position dans un champ int à l'intérieur du viewholder, la mettre à jour pendant la liaison, puis la renvoyer au clic en est une." Le ViewHolder a déjà cette valeur et est déjà défini. Il suffit d'appelerviewHolder.getPosition();
Chad Bingham
104

Pourquoi le RecyclerView n'a pas onItemClickListener

C'est RecyclerViewune boîte à outils, contrairement à l'ancienne, ListViewelle a moins de fonctionnalités et plus de flexibilité. Ce onItemClickListenern'est pas la seule fonctionnalité supprimée de ListView. Mais il a beaucoup d'auditeurs et de méthode pour l'étendre à votre guise, il est bien plus puissant entre de bonnes mains;).

À mon avis, la fonctionnalité la plus complexe supprimée RecyclerViewest le Fast Scroll . La plupart des autres fonctionnalités peuvent être facilement réimplémentées.

Si vous voulez savoir quelles autres fonctionnalités intéressantes RecyclerViewajoutées, lisez cette réponse à une autre question.

Efficace en mémoire - solution intégrée pour onItemClickListener

Cette solution a été proposée par Hugo Visser , un Android GDE, juste après RecyclerViewsa sortie. Il a mis à votre disposition une classe sans licence pour simplement déposer votre code et l'utiliser.

Il met en valeur une partie de la polyvalence introduite avec RecyclerViewen faisant usage de RecyclerView.OnChildAttachStateChangeListener.

Edit 2019 : version kotlin par moi, java one, de Hugo Visser, gardée ci-dessous

Kotlin / Java

Créez un fichier values/ids.xmlet mettez-le dedans:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="item_click_support" type="id" />
</resources>

puis ajoutez le code ci-dessous à votre source

Kotlin

Usage:

recyclerView.onItemClick { recyclerView, position, v ->
    // do it
}

(il prend également en charge les longs clics sur les objets et voir ci-dessous pour une autre fonctionnalité que j'ai ajoutée).

implémentation (mon adaptation au code Java de Hugo Visser):

typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit
typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean

class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {

    private var onItemClickListener: OnRecyclerViewItemClickListener? = null
    private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null

    private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
        override fun onChildViewAttachedToWindow(view: View) {
            // every time a new child view is attached add click listeners to it
            val holder = this@ItemClickSupport.recyclerView.getChildViewHolder(view)
                    .takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder

            if (onItemClickListener != null && holder?.isClickable != false) {
                view.setOnClickListener(onClickListener)
            }
            if (onItemLongClickListener != null && holder?.isLongClickable != false) {
                view.setOnLongClickListener(onLongClickListener)
            }
        }

        override fun onChildViewDetachedFromWindow(view: View) {

        }
    }

    init {
        // the ID must be declared in XML, used to avoid
        // replacing the ItemClickSupport without removing
        // the old one from the RecyclerView
        this.recyclerView.setTag(R.id.item_click_support, this)
        this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
    }

    companion object {
        fun addTo(view: RecyclerView): ItemClickSupport {
            // if there's already an ItemClickSupport attached
            // to this RecyclerView do not replace it, use it
            var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
            if (support == null) {
                support = ItemClickSupport(view)
            }
            return support
        }

        fun removeFrom(view: RecyclerView): ItemClickSupport? {
            val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
            support?.detach(view)
            return support
        }
    }

    private val onClickListener = View.OnClickListener { v ->
        val listener = onItemClickListener ?: return@OnClickListener
        // ask the RecyclerView for the viewHolder of this view.
        // then use it to get the position for the adapter
        val holder = this.recyclerView.getChildViewHolder(v)
        listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private val onLongClickListener = View.OnLongClickListener { v ->
        val listener = onItemLongClickListener ?: return@OnLongClickListener false
        val holder = this.recyclerView.getChildViewHolder(v)
        return@OnLongClickListener listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private fun detach(view: RecyclerView) {
        view.removeOnChildAttachStateChangeListener(attachListener)
        view.setTag(R.id.item_click_support, null)
    }

    fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
        onItemClickListener = listener
        return this
    }

    fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
        onItemLongClickListener = listener
        return this
    }

}

/** Give click-ability and long-click-ability control to the ViewHolder */
interface ItemClickSupportViewHolder {
    val isClickable: Boolean get() = true
    val isLongClickable: Boolean get() = true
}

// Extension function
fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)

fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)

fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
    addItemClickSupport { onItemClick(onClick) }
}
fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
    addItemClickSupport { onItemLongClick(onLongClick) }
}

(voir ci-dessous, vous devez également ajouter un fichier XML)

Fonction bonus de la version Kotlin

Parfois, vous ne voulez pas que tous les éléments de RecyclerView soient cliquables.

Pour gérer cela, j'ai introduit l' ItemClickSupportViewHolderinterface que vous pouvez utiliser sur votre ViewHolderpour contrôler quel élément est cliquable.

Exemple:

class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
    override val isClickable: Boolean get() = false
    override val isLongClickable: Boolean get() = false
}

Java

Usage:

ItemClickSupport.addTo(mRecyclerView)
        .setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
    @Override
    public void onItemClicked(RecyclerView recyclerView, int position, View v) {
        // do it
    }
});

(il prend également en charge le clic long sur un élément)

Mise en œuvre (commentaires ajoutés par moi):

public class ItemClickSupport {
    private final RecyclerView mRecyclerView;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;
    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                // ask the RecyclerView for the viewHolder of this view.
                // then use it to get the position for the adapter
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
        }
    };
    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if (mOnItemLongClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
            return false;
        }
    };
    private RecyclerView.OnChildAttachStateChangeListener mAttachListener
            = new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(View view) {
            // every time a new child view is attached add click listeners to it
            if (mOnItemClickListener != null) {
                view.setOnClickListener(mOnClickListener);
            }
            if (mOnItemLongClickListener != null) {
                view.setOnLongClickListener(mOnLongClickListener);
            }
        }

        @Override
        public void onChildViewDetachedFromWindow(View view) {

        }
    };

    private ItemClickSupport(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        // the ID must be declared in XML, used to avoid
        // replacing the ItemClickSupport without removing
        // the old one from the RecyclerView
        mRecyclerView.setTag(R.id.item_click_support, this);
        mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
    }

    public static ItemClickSupport addTo(RecyclerView view) {
        // if there's already an ItemClickSupport attached
        // to this RecyclerView do not replace it, use it
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support == null) {
            support = new ItemClickSupport(view);
        }
        return support;
    }

    public static ItemClickSupport removeFrom(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support != null) {
            support.detach(view);
        }
        return support;
    }

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
        return this;
    }

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
        return this;
    }

    private void detach(RecyclerView view) {
        view.removeOnChildAttachStateChangeListener(mAttachListener);
        view.setTag(R.id.item_click_support, null);
    }

    public interface OnItemClickListener {

        void onItemClicked(RecyclerView recyclerView, int position, View v);
    }

    public interface OnItemLongClickListener {

        boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
    }
}

Comment ça marche (pourquoi c'est efficace)

Cette classe fonctionne en attachant un RecyclerView.OnChildAttachStateChangeListenerau RecyclerView. Cet écouteur est averti chaque fois qu'un enfant est attaché ou détaché du RecyclerView. Le code l'utilise pour ajouter un écouteur tap / long click à la vue. Cet auditeur demande le RecyclerViewpour RecyclerView.ViewHolderqui contient la position.

C'est plus efficace que les autres solutions car cela évite de créer plusieurs écouteurs pour chaque vue et continue de les détruire et de les créer pendant le RecyclerViewdéfilement.

Vous pouvez également adapter le code pour vous rendre le support lui-même si vous en avez besoin de plus.

Remarque finale

Gardez à l'esprit qu'il est COMPLÈTEMENT bien de le gérer dans votre adaptateur en définissant sur chaque vue de votre liste un écouteur de clic, comme les autres réponses proposées.

Ce n'est tout simplement pas la chose la plus efficace à faire (vous créez un nouvel écouteur chaque fois que vous réutilisez une vue) mais cela fonctionne et dans la plupart des cas, ce n'est pas un problème.

C'est également un peu contre la séparation des préoccupations car ce n'est pas vraiment le travail de l'adaptateur de déléguer les événements de clic.

Daniele Segato
la source
addOnChildAttachStateChangeListener dans le constructeur et ne jamais le supprimer?
Saba
@Saba, vous n'êtes pas censé utiliser directement le constructeur, mais par des méthodes statiques addTo()et removeFrom(). L'instance est associée à la balise recyclerview et meurt avec elle ou lorsqu'elle est supprimée manuellement.
Daniele Segato
Tellement de code quand tout ce que vous avez à faire est de définir un écouteur au clic onCreateViewHolderet c'est tout.
EpicPandaForce
Votre argument est fluide, il ne s'agit pas de la quantité de code mais du principe d'efficacité et de responsabilité et la quantité de code n'est pas une bonne indication de bonne / mauvaise solution, surtout lorsque ce code est réutilisable et efficace. Une fois que vous avez mis ce code dans votre projet (que vous pouvez considérer comme une inclusion de bibliothèque), tout ce dont vous avez besoin est une ligne sur votre RecyclerView.
Daniele Segato
@DanieleSegato Merci beaucoup pour cela!
M. Octodood
90

J'aime cette façon et je l'utilise

À l'intérieur

public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Mettre

View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());

Et créez cette classe où vous le voulez

class MyOnClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
       int itemPosition = recyclerView.indexOfChild(v);
       Log.e("Clicked and Position is ",String.valueOf(itemPosition));
    }
}

J'ai déjà lu qu'il y avait une meilleure façon mais j'aime que cette façon soit facile et pas compliquée.

Abdulaziz Noor
la source
23
devrait probablement MyOnClickListenerdevenir une variable d'instance, car vous en new
créerez
14
recyclerView.getChildPosition (v); est désormais utilisé recyclerView. indexOfChild (v);
Qadir Hussain
4
Vous n'avez pas de problèmes lors du défilement? Lors de mes tests, l'itemPosition semble dépendre du recyclage des vues, donc ça va mal par rapport à ma liste complète dans l'adaptateur.
Simon
16
Comment entrer recyclerViewdans MyOnClickListener?
guo
3
Au lieu d'utiliser: int itemPosition = recyclerView.indexOfChild (v); vous devez utiliser: RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder (v); int itemPosition = holder.getAdapterPosition ();
Gordon Freeman
35

Android Recyclerview With onItemClickListener, Why we cant try this works like like ListViewonly.

Source: Lien

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

private OnItemClickListener mListener;
public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

  }
}

Et définissez ceci sur RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new            LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new   RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Handle item click
                    Log.e("@@@@@",""+position);
                }
            })
    );
Guruprasad
la source
7
Pas élégant pour être honnête.
vishal dharankar
@vishaldharankar pourquoi pas? Je pense que c'est le meilleur moyen d'éviter les conflits de changement de focus après utilisationSwipeRefreshLayout
NickUnuchek
Ceci est totalement inutile, vous pouvez utiliser un View.OnClickListener au lieu d'intercepter le toucher.
EpicPandaForce
23

Grâce à @marmor, j'ai mis à jour ma réponse.

Je pense que c'est une bonne solution pour gérer le onClick () dans le constructeur de la classe ViewHolder et le transmettre à la classe parente via l' interface OnItemClickListener .

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{

private LayoutInflater layoutInflater;
private List<MyObject> items;
private AdapterView.OnItemClickListener onItemClickListener;

public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) {
    layoutInflater = LayoutInflater.from(context);
    this.items = items;
    this.onItemClickListener = onItemClickListener;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false);
    return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    MyObject item = items.get(position);
}

public MyObject getItem(int position) {
    return items.get(position);
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView title;
    private ImageView avatar;

    public ViewHolder(View itemView) {
        super(itemView);
        title = itemView.findViewById(R.id.title);
        avatar = itemView.findViewById(R.id.avatar);

        title.setOnClickListener(this);
        avatar.setOnClickListener(this);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //passing the clicked position to the parent class
        onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
    }
}
}

Utilisation de l'adaptateur dans d'autres classes:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

private void init(Context context) {
    //passing this fragment as OnItemClickListener to the adapter
    adapter = new MyAdapter(context, this, items);
    recycleview.setAdapter(adapter);
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //you can get the clicked item from the adapter using its position
    MyObject item = adapter.getItem(position);

    //you can also find out which view was clicked
    switch (view.getId()) {
        case R.id.title:
            //title view was clicked
            break;
        case R.id.avatar:
            //avatar view was clicked
            break;
        default:
            //the whole row was clicked
    }
}

}
Shayan_Aryan
la source
34
Cela créera et récupérera des tonnes d'objets lors du défilement, vous devriez plutôt créer un OnClickListener et le réutiliser
marmor
2
Est également onBindViewHolder()appelé plusieurs fois pour la même vue lors du défilement. Même la création d'un single OnClickListenern'aidera pas car vous serez réinitialisé à OnClickListenerchaque onBindViewHolder()appel. Voir plutôt la solution @ MLProgrammer-CiM.
vovahost
Vous pouvez également améliorer la solution @ MLProgrammer-CiM et au lieu de créer un objet écouteur pour chaque vue de cette ligne, MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....vous pouvez créer un écouteur unique pour toutes les vues.
vovahost
21

Les gars utilisent ce code dans votre activité principale. Méthode très efficace

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);            
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);

Voici votre classe d'adaptateur.

public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {
        private ArrayList<User> mDataSet;
        OnCardClickListner onCardClickListner;


        public UsersAdapter(ArrayList<User> mDataSet) {
            this.mDataSet = mDataSet;
        }

        @Override
        public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout, parent, false);
            UserViewHolder userViewHolder = new UserViewHolder(v);
            return userViewHolder;
        }

        @Override
        public void onBindViewHolder(UserViewHolder holder, final int position) {
            holder.name_entry.setText(mDataSet.get(position).getUser_name());
            holder.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCardClickListner.OnCardClicked(v, position);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mDataSet.size();
        }

        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
        }


        public static class UserViewHolder extends RecyclerView.ViewHolder {
            CardView cardView;
            TextView name_entry;

            public UserViewHolder(View itemView) {
                super(itemView);
                cardView = (CardView) itemView.findViewById(R.id.user_layout);
                name_entry = (TextView) itemView.findViewById(R.id.name_entry);
             }
        }

        public interface OnCardClickListner {
            void OnCardClicked(View view, int position);
        }

        public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
            this.onCardClickListner = onCardClickListner;
        }
    }

Après cela, vous obtiendrez cette méthode de remplacement dans votre activité.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }
waqas ali
la source
5
Je me demande si l'utilisateur faisant défiler de haut en bas, OnClickListener va créer et recycler encore et encore, c'est efficace?
Vinh.TV
Pour obtenir la position mise à jour de la carte, cette chose mineure peut être compromise.
waqas ali
salut ami sympa son charme de travail, j'ai une activité requise avec les catégories et les produits initialement, si l'utilisateur sélectionne l'icône de catégorie, je dois afficher les catégories dans la boîte de dialogue ou l'activité si l'utilisateur sélectionne une catégorie basée sur laquelle charger des données filtrées sur la première activité s'il vous plaît aidez-moi comment résoudre
Harsha
1er: Vous devez ajouter une méthode nommée OnProductClicked dans l'interface OnCardClickListner comme cette interface publique OnCardClickListner {void OnCardClicked (View view, int position); void OnProductClicked (vue Vue, position int); } 2ème: Ensuite, implémentez l'écouteur de clic sur l'icône de votre produit comme ceci. holder.yourProductIcon.setOnClickListener (nouveau View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, position);}});
waqas ali
Je ne parviens pas à remplacer OnCardClicked. Comment les gens votent-ils pour cette réponse?
The_Martian
19

> En quoi RecyclerView est-il différent de Listview?

Une différence est qu'il ya LayoutManagerclasse avec RecyclerView par lequel vous pouvez gérer votre RecyclerViewcomme-

Défilement horizontal ou vertical parLinearLayoutManager

GridLayout par GridLayoutManager

GridLayout échelonné par StaggeredGridLayoutManager

Comme pour le défilement horizontal pour RecyclerView-

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);
Tarun Varshney
la source
15

Faisant suite une excellente solution RxJava de MLProgrammer-CiM

Consommer / observer les clics

Consumer<String> mClickConsumer = new Consumer<String>() {
        @Override
        public void accept(@NonNull String element) throws Exception {
            Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show();
        }
    };

ReactiveAdapter rxAdapter = new ReactiveAdapter();
rxAdapter.getPositionClicks().subscribe(mClickConsumer);

RxJava 2. +

Modifiez le tl; dr d'origine comme:

public Observable<String> getPositionClicks(){
    return onClickSubject;
}

PublishSubject#asObservable()a été éliminé. Il suffit de retourner le PublishSubjectqui est un Observable.

boulanger
la source
Où obtenez-vous mClickConsumer sur la deuxième ligne, veuillez expliquer un peu. rxAdapter.getPositionClicks (). subscribe (mClickConsumer);
bluetoothfx
14

Comment mettre tout cela ensemble exemple ...

  • gestion onClick ()
  • Curseur - RecyclerView
  • Types de ViewHolder

    public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> {
    
    private static final String TAG = OrderListCursorAdapter.class.getSimpleName();
    private static final int ID_VIEW_HOLDER_ACTUAL = 0;
    private static final int ID_VIEW_HOLDER = 1;
    
    public OrderListCursorAdapter(Context context, Cursor cursor) {
        super(context, cursor);
    }
    
    public static class ViewHolderActual extends ViewHolder {
        private static final String TAG = ViewHolderActual.class.getSimpleName();
        protected IViewHolderClick listener;
        protected Button button;
    
        public ViewHolderActual(View v, IViewHolderClick listener) {
            super(v, listener);
            this.listener = listener;
            button = (Button) v.findViewById(R.id.orderList_item_button);
            button.setOnClickListener(this);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        @Override
        public void onClick(View view) {
            if (view instanceof Button) {
                listener.onButtonClick((Button) view, getPosition(), this);
            } else {
                super.onClick(view);
            }
        }
    
        public interface IViewHolderClick extends ViewHolder.IViewHolderClick {
            public void onButtonClick(Button button, int position, ViewHolder viewHolder);
        }
    }
    
    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private static final String TAG = ViewHolder.class.getSimpleName();
        protected long orderId;
        protected IViewHolderClick listener;
        protected TextView vAddressStart;
        protected TextView vAddressEnd;
        protected TextView vStatus;
    
        public ViewHolder(View v, IViewHolderClick listener) {
            super(v);
            this.listener = listener;
            v.setOnClickListener(this);
    
            vAddressStart = (TextView) v.findViewById(R.id.addressStart);
            vAddressEnd = (TextView) v.findViewById(R.id.addressEnd);
            vStatus = (TextView) v.findViewById(R.id.status);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        public long getOrderId() {
            return orderId;
        }
    
        @Override
        public void onClick(View view) {
            listener.onCardClick(view, getPosition(), this);
        }
    
        public interface IViewHolderClick {
            public void onCardClick(View view, int position, ViewHolder viewHolder);
        }
    }
    
    @Override
    public int getItemViewType(int position) {
        return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER;
    }
    
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")");
    
        ViewHolder result;
    
        switch (viewType) {
            case ID_VIEW_HOLDER_ACTUAL: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false);
                result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
    
                    @Override
                    public void onButtonClick(Button button, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(button.getContext(), OrderMapActivity.class);
                        intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        button.getContext().startActivity(intent);
                    }
                });
                break;
            }
            case ID_VIEW_HOLDER:
            default: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
                result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
                });
                break;
            }
        }
    
        Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result);
        return result;
    }
    
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
        Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")");
        final OrderData orderData = new OrderData(cursor);
        viewHolder.initFromData(orderData);
    }
    }
Wooff
la source
11

Pour autant que je comprends la réponse MLProgrammer-CiM , il est simplement possible de le faire:

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    private ImageView image;
    private TextView title;
    private TextView price;

    public MyViewHolder(View itemView) {
        super(itemView);
        image = (ImageView)itemView.findViewById(R.id.horizontal_list_image);
        title = (TextView)itemView.findViewById(R.id.horizontal_list_title);
        price = (TextView)itemView.findViewById(R.id.horizontal_list_price);
        image.setOnClickListener(this);
        title.setOnClickListener(this);
        price.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show();
    }
}
Tomasz Mularczyk
la source
9

Après avoir lu la réponse de @ MLProgrammer-CiM , voici mon code:

class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

    @Bind(R.id.card_item_normal)
    CardView cardView;

    public NormalViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
        cardView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v instanceof CardView) {
            // use getAdapterPosition() instead of getLayoutPosition()
            int itemPosition = getAdapterPosition();
            removeItem(itemPosition);
        }
    }
}
tchad
la source
Méfiez-vous qui getAdapterPositionpeut revenir -1pour "aucune position".
EpicPandaForce
9

J'ai fait de cette façon, c'est très simple:

Ajoutez simplement 1 ligne pour la position Clicker RecyclerView :

int position = getLayoutPosition()

Code complet pour la classe ViewHolder :

private class ChildViewHolder extends RecyclerView.ViewHolder {
        public ImageView imageView;
        public TextView txtView;

        public ChildViewHolder(View itemView) {
            super(itemView);
            imageView= (ImageView)itemView.findViewById(R.id.imageView);
            txtView= (TextView) itemView.findViewById(R.id.txtView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition()));
                }
            });
        }
    }

J'espère que cela vous aidera.

Hiren Patel
la source
Vous devez utiliser à la getAdapterPositionplace de getLayoutPosition. Protégez-vous -1.
EpicPandaForce
7

J'utilise cette méthode pour démarrer une intention à partir de RecyclerView:

@Override
 public void onBindViewHolder(ViewHolder viewHolder, int i) {

    final MyClass myClass = mList.get(i);
    viewHolder.txtViewTitle.setText(myclass.name);
   ...
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
       public void onClick(View v){
             Intent detailIntent = new Intent(mContext, type.class);                                                            
             detailIntent.putExtra("MyClass", myclass);
             mContext.startActivity(detailIntent);
       }
}
);
tmac12
la source
Ce n'est pas un bon moyen. Parce que onBindViewHolder(...)n'est pas appelé après les notify()méthodes. Ainsi, vous pouvez obtenir une position de clic incorrecte dans certaines situations.
Alex Kisel
5

RecyclerView n'en a pas onItemClickListenercar RecyclerView est responsable du recyclage des vues (surprise!), C'est donc la responsabilité de la vue qui est recyclée de gérer les événements de clic qu'elle reçoit.

Cela rend en fait beaucoup plus facile à utiliser, surtout si vous aviez des éléments sur lesquels vous pouvez cliquer à plusieurs endroits.


Quoi qu'il en soit, la détection d'un clic sur un élément RecyclerView est très simple. Il vous suffit de définir une interface (si vous n'utilisez pas Kotlin, auquel cas vous passez juste un lambda):

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private final Clicks clicks;

    public MyAdapter(Clicks clicks) {
        this.clicks = clicks;
    }

    private List<MyObject> items = Collections.emptyList();

    public void updateData(List<MyObject> items) {
        this.items = items;
        notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
    }

    public interface Clicks {
        void onItemSelected(MyObject myObject, int position);
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private MyObject myObject;    

        public MyViewHolder(View view) {
            super(view);
            // bind views
            view.setOnClickListener((v) -> {
                int adapterPosition = getAdapterPosition();
                if(adapterPosition >= 0) {
                    clicks.onItemSelected(myObject, adapterPosition);
                }
            });
        }

        public void bind(MyObject myObject) {
            this.myObject = myObject;
            // bind data to views
        }
    }
}

Même code dans Kotlin:

class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
    private var items: List<MyObject> = Collections.emptyList()

    fun updateData(items: List<MyObject>) {
        this.items = items
        notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
    }

    inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
        private lateinit var myObject: MyObject

        init {
            // binds views
            myView.onClick {
                val adapterPosition = getAdapterPosition()
                if(adapterPosition >= 0) {
                    itemClicks.invoke(myObject, adapterPosition)
                }
            }
        }

        fun bind(myObject: MyObject) {
            this.myObject = myObject
            // bind data to views
        }
    }
}

Ce que vous n'avez PAS besoin de faire:

1.) vous n'avez pas besoin d'intercepter manuellement les événements tactiles

2.) vous n'avez pas besoin de jouer avec les auditeurs de changement d'état

3.) vous n'avez pas besoin de PublishSubject / PublishRelay de RxJava

Utilisez simplement un écouteur de clics.

EpicPandaForce
la source
4

Voir mon approche à ce sujet:

Déclarez d'abord une interface comme celle-ci:

/**
 * Interface used for delegating item click events in a {@link android.support.v7.widget.RecyclerView}
 * Created by Alex on 11/28/2015.
 */
  public interface OnRecyclerItemClickListener<T> {

     /**
      * Called when a click occurred inside a recyclerView item view
      * @param view that was clicked
      * @param position of the clicked view
      * @param item the concrete data that is displayed through the clicked view
      */
      void onItemClick(View view, int position, T item);
   }

Créez ensuite l'adaptateur:

public class CustomRecyclerAdapter extends RecyclerView.Adapter {      

    private class InternalClickListener implements View.OnClickListener{

      @Override
      public void onClick(View v) {
        if(mRecyclerView != null && mItemClickListener != null){
            // find the position of the item that was clicked
            int position = mRecyclerView.getChildAdapterPosition(v);
            Data data = getItem(position);
            // notify the main listener
            mItemClickListener.onItemClick(v, position, data);
        }
    }
}

private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;    
private InternalClickListener mInternalClickListener;


/**
 *
 * @param itemClickListener used to trigger an item click event
 */
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){        
    mItemClickListener = itemClickListener;
    mInternalClickListener = new InternalClickListener();
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);

    v.setOnClickListener(mInternalClickListener);

    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // do your binding here
}

@Override
public int getItemCount() {
    return mDataSet.size();
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);

    mRecyclerView = recyclerView;
}

@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
    super.onDetachedFromRecyclerView(recyclerView);

    mRecyclerView = null;
}

public Data getItem(int position){
    return mDataset.get(position);
}
}

Et maintenant, voyons comment intégrer cela à partir d'un fragment:

public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
   private RecyclerView mRecyclerView;

   @Override
   public void onItemClick(View view, int position, Data item) {
     // do something
   }

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      return inflater.inflate(R.layout.test_fragment, container, false);
   }

   @Override
   public void onViewCreated(View view, Bundle savedInstanceState) {
      mRecyclerView = view.findViewById(idOfTheRecycler);
      mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
   }
Cirque d'Alexandru
la source
4

Si vous souhaitez ajouter onClick () à la vue enfant des éléments, par exemple, un bouton dans l'élément, j'ai constaté que vous pouvez le faire facilement dans onCreateViewHolder () de votre propre RecyclerView.Adapter comme ceci:

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater
                    .from(parent.getContext())
                    .inflate(R.layout.cell, null);

            Button btn = (Button) v.findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //do it
                }
            });

            return new MyViewHolder(v);
        }

je ne sais pas si c'est un bon moyen, mais ça marche bien. Si quelqu'un a une meilleure idée, très heureux de me le dire et de corriger ma réponse! :)

cryyyyy
la source
4

Voici un moyen de l'implémenter assez facilement si vous avez une liste de POJO et que vous souhaitez en récupérer un en un clic depuis l'extérieur de l'adaptateur.

Dans votre adaptateur, créez un écouteur pour les événements de clic et une méthode pour le définir:

public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> {
...
private List<MyPojo> mMyPojos;
private static OnItemClickListener mOnItemClickListener;

...
public interface OnItemClickListener {
    public void onItemClick(MyPojo pojo);
}

...
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
    mOnItemClickListener = onItemClickListener;
}
...

}

Dans votre ViewHolder, implémentez onClickListener et créez un membre de classe pour stocker temporairement le POJO que la vue présente, de cette façon (c'est un exemple, créer un setter serait mieux):

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    public MyPojo mCurrentPojo;
    ...
    public ViewHolder(View view) {
        super(v);
        ...
        view.setOnClickListener(this); //You could set this on part of the layout too
    }

    ...
    @Override
    public void onClick(View view) {
        if(mOnItemClickListener != null && mCurrentPojo != null){
            mOnItemClickListener.onItemClick(mCurrentPojo);
        }
    }

De retour dans votre adaptateur, définissez le POJO actuel lorsque le ViewHolder est lié (ou à null si la vue actuelle n'en a pas):

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    final MyPojo currentPojo = mMyPojos.get(position); 
    holder.mCurrentPojo = currentPojo;
    ...

Voilà, maintenant vous pouvez l'utiliser comme ceci à partir de votre fragment / activité:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });
Simon
la source
vous essayez d'implémenter Interface comme s'il s'agissait d'une méthode Object. Cela ne fonctionnera pas
Akshay
Mise à jour: Ce dont vous avez besoin estmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay
@Akshay Désolé mais je pense que mon code est correct. L'avez-vous essayé? Tous les auditeurs sur Android suivent le même principe, et vous devez instancier l'interface avec un 'nouveau' (ex: stackoverflow.com/questions/4709870/… )
Simon
L'auditeur ne doit pas être statique.
EpicPandaForce
4

Oui, vous pouvez

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {

    //inflate the view 

    View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);

    ViewHolder holder = new ViewHolder(view);

    //here we can set onClicklistener
    view.setOnClickListener(new  View.OnClickListeener(){
        public void onClick(View v)
        {
            //action
        }
    });

return holder;
Ashok Reddy M
la source
3

Ici, vous pouvez gérer plusieurs onclick voir le code ci-dessous et il est très efficace

    public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> {

    private Context context;
    List<News> newsList;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public RVNewsAdapter(List<News> newsList, Context context) {
        this.newsList = newsList;
        this.context = context;
    }

    public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {

        ImageView img_main;
        TextView tv_title;
        Button bt_facebook, bt_twitter, bt_share, bt_comment;


        public FeedHolder(View itemView) {
            super(itemView);

            img_main = (ImageView) itemView.findViewById(R.id.img_main);
            tv_title = (TextView) itemView.findViewById(R.id.tv_title);
            bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook);
            bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter);
            bt_share = (Button) itemView.findViewById(R.id.bt_share);
            bt_comment = (Button) itemView.findViewById(R.id.bt_comment);

            img_main.setOnClickListener(this);
            bt_facebook.setOnClickListener(this);
            bt_twitter.setOnClickListener(this);
            bt_comment.setOnClickListener(this);
            bt_share.setOnClickListener(this);

        }


        @Override
        public void onClick(View v) {

            if (v.getId() == bt_comment.getId()) {

                Toast.makeText(v.getContext(), "Comment  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_facebook.getId()) {

                Toast.makeText(v.getContext(), "Facebook  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_twitter.getId()) {

                Toast.makeText(v.getContext(), "Twitter  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_share.getId()) {

                Toast.makeText(v.getContext(), "share  " , Toast.LENGTH_SHORT).show();

            }
            else {
                Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    @Override
    public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false);
        FeedHolder feedHolder = new FeedHolder(view);

        return feedHolder;
    }

    @Override
    public void onBindViewHolder(FeedHolder holder, int position) {

        holder.tv_title.setText(newsList.get(position).getTitle());


        // Here you apply the animation when the view is bound
        setAnimation(holder.img_main, position);
    }

    @Override
    public int getItemCount() {
        return newsList.size();
    }


    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position) {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition) {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }


}
Arpit Patel
la source
Il voulait dire le itemView.
Davideas
3

Modifié mon commentaire ...

public class MyViewHolder extends RecyclerView.ViewHolder {

        private Context mContext;

        public MyViewHolder(View itemView) {
            super(itemView);

            mContext = itemView.getContext();

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    int itemPosition = getLayoutPosition();
                    Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show();

                }
            });
        }
Jae-Min Kim
la source
1
C'est une suggestion, mais mieux si vous supprimez cette réponse, à moins que beaucoup de gens votent négativement. Cette solution est définitivement fausse, elle fonctionne mais est une mauvaise programmation. Veuillez lire certains blogs pour RecyclerView et étudier certaines bibliothèques d'adaptateurs, il y en a déjà beaucoup dans Github, comme FlexibleAdapter, FastAdapter et ainsi de suite ...
Davideas
1
Vous répondez est relativement nouveau d'une longue liste de réponses avec un vote élevé et au bas de la page, c'est pourquoi il a toujours 0 votes. Par ailleurs, dans le nouveau code, vous devez utiliser getAdapterPosition().
Davideas
Il vous suffit de lire le javaDoc des 2 méthodes.
Davideas
3

Vérifiez celui-ci dans lequel j'ai mis en œuvre toutes les choses d'une manière appropriée

Classe RecyclerViewHolder

public class RecyclerViewHolder extends RecyclerView.ViewHolder  {

    //view holder is for girdview as we used in the listView
    public ImageView imageView,imageView2;
    public RecyclerViewHolder(View itemView) {
        super(itemView);
        this.imageView=(ImageView)itemView.findViewById(R.id.image);
    }

}

Adaptateur

public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {

    //RecyclerView will extend to recayclerview Adapter
    private ArrayList<ModelClass> arrayList;
    private Context context;
    private static RecyclerViewClickListener itemListener;
    //constructor of the RecyclerView Adapter
    RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
        this.context=context;
        this.arrayList=arrayList;
        this.itemListener=itemListener;
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //this method will inflate the custom layout and return as viewHolder
        LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
        ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
        RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);

        return listHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder holder, final int position) {

        final ModelClass modelClass=arrayList.get(position);
        //holder
        RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
        //convert the drawable image into bitmap
        Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
        //set the image into imageView
        mainHolder.imageView.setImageBitmap(image);
        //to handle on click event when clicked on the recyclerview item and
        // get it through the RecyclerViewHolder class we have defined the views there
        mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //get the position of the image which is clicked
             itemListener.recyclerViewListClicked(v,position);
            }
        });

    }

    @Override
    public int getItemCount() {
        return (null!=arrayList?arrayList.size():0);
    }
}

L'interface

public interface RecyclerViewClickListener {

    //this is method to handle the event when clicked on the image in Recyclerview
    public void recyclerViewListClicked(View v,int position);
}

//and to call this method in activity
RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this);
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();


    @Override
    public void  recyclerViewListClicked(View v,int position){

        imageView.setImageResource(wallpaperImages[position]);

    }
Mudassir Khan
la source
2

Cela a fonctionné pour moi:

@Override
public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) {
    ----
    ----
    ----
    // Set setOnClickListener(holder);
}


@Override
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ----
    ----
    ----

    @Override
    public void onClick(View view) {
        // Use to get the item clicked getAdapterPosition()
    }
}
Naren
la source
Il est pas bon réglage de façon setOnClickListener(holder)à onBindViewHolder(). Parce que onBindViewHolder (...) n'est pas appelé après les méthodes notify (). Ainsi, vous pouvez obtenir une position de clic incorrecte dans certaines situations.
Alex Kisel
Vous devez créer l'écouteur de clics à l'intérieur onCreateViewHolder.
EpicPandaForce
2

Accédez au mainViewde rowLayout(cell)vous RecyclerViewet dans votre OnBindViewHolderécriture ce code:

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        Movie movie = moviesList.get(position);
        holder.mainView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                System.out.println("pos " + position);
            }
        });
    }
Liridon Sadiku
la source
1

Au lieu d'implémenter l'interface View.OnClickListener à l'intérieur du support de vue ou de créer et d'interfacer et d'implémenter une interface dans votre activité .. J'ai utilisé ce code pour la mise en œuvre d'OnClickListener.

public static class SimpleStringRecyclerViewAdapter
            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {

        // Your initializations goes here...
        private List<String> mValues;

        public static class ViewHolder extends RecyclerView.ViewHolder {

            //create a variable mView
            public final View mView;

            /*All your row widgets goes here
            public final ImageView mImageView;
            public final TextView mTextView;*/

            public ViewHolder(View view) {
                super(view);
                //Initialize it here
                mView = view;

                /* your row widgets initializations goes here
                mImageView = (ImageView) view.findViewById(R.id.avatar);
                mTextView = (TextView) view.findViewById(android.R.id.text1);*/
            }
        }

        public String getValueAt(int position) {
            return mValues.get(position);
        }

        public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {

            mBackground = mTypedValue.resourceId;
            mValues = items;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item, parent, false);
            view.setBackgroundResource(mBackground);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            holder.mBoundString = mValues.get(position);
            holder.mTextView.setText(mValues.get(position));

            //Here it is simply write onItemClick listener here
            holder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Context context = v.getContext();
                    Intent intent = new Intent(context, ExampleActivity.class);

                    context.startActivity(intent);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mValues.size();
        }
    }
Manikanta
la source
2
Et vous pensez que la création d'un nouvel objet OnItemClickListener à chaque appel de méthode onBind () est une bonne approche?
Alexandru Circus
Bon travail. Fonctionne très bien!
sam
1

ça a marché pour moi. J'espère que cela vous aidera. La manière la plus simple.

Porte-vue intérieure

class GeneralViewHolder extends RecyclerView.ViewHolder {
    View cachedView = null;

    public GeneralViewHolder(View itemView) {
        super(itemView);
        cachedView = itemView;
    }

Dans OnBindViewHolder ()

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder;
            generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show();
                }
            });

Et faites-moi savoir, avez-vous des questions sur cette solution?

C'est Enam
la source
Vous devez définir le onClickListener onCreateViewHolderpour de meilleures performances, mais vous devez ensuite obtenir la position en utilisant getAdapterPosition()(et en -1
évitant
1
 main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
        {
            int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY()));

            switch (position)
            {
                case 0:
                {
                    wifi(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 1:
                {
                    sound(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 2:
                {
                    bluetooth(position);
                    adapter2.notifyDataSetChanged();
                }
                break;


            }
            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e)
        {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    });
dileep krishnan
la source
en utilisant cela, vous pouvez obtenir des résultats exacts ..................
dileep krishnan
La question est "Pourquoi RecyclerView n'a-t-il pas onItemClickListener ()?" PAS "Comment implémenter onItemClickListener () dans RecyclerView?" .
Shashanth
0

utiliser PlaceHolderView

@Layout(R.layout.item_view_1)
public class View1{

    @View(R.id.txt)
    public TextView txt;

    @Resolve
    public void onResolved() {
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }

    @Click(R.id.btn)
    public void onClick(){
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }
}
Janishar Ali
la source
0

J'ai écrit une bibliothèque pour gérer l'événement de clic d'élément d'affichage du recycleur Android. Vous pouvez trouver un didacticiel complet sur https://github.com/ChathuraHettiarachchi/RecycleClick

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
            @Override
            public void onItemClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
            }
        });

ou pour manipuler un élément, appuyez longuement sur, vous pouvez utiliser

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });
Chathura Jayanath
la source
Monsieur, j'ai essayé votre bibliothèque mais vous rencontrez des problèmes avec Maven?
Abhijeet
0

l'animation recyclerview n'a pas été testée, l'autre est normale. Je pense qu'il a été optimisé au maximum. L'interface a d'autres utilisations, vous pouvez temporairement l'ignorer.

public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface {
    public final String TAG = getClass().getSimpleName();

    protected final Activity mActivity;
    protected final LayoutInflater mInflater;
    protected ItemClickInterface<?, Integer> mListener;

    public BaseAdapterRV(Activity activity) {
        mActivity = activity;
        mInflater = LayoutInflater.from(mActivity);
    }

    @Override
    public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
        return onCreateViewHolder(parent, viewType, mInflater);
    }

    @Override
    public final void onBindViewHolder(VH holder, int position) {
        holder.itemView.setTag(R.id.tag_view_click, position);
        //创建点击事件
        holder.itemView.setOnClickListener(mListener);
        holder.itemView.setOnLongClickListener(mListener);
        onBindVH(holder, position);
    }


    ///////////////////////////////////////////////////////////////////////////
    // 以下是增加的方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确
     * 里面也有onItemLongClick
     */
    public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) {
        mListener = listener;
        notifyDataSetChanged();
    }

    @NonNull
    protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater);

    protected abstract void onBindVH(VH holder, int position);

}

Ceci est l'interface

/**
 * OnItemClickListener的接口
 * 见子类实现{@link OnItemClickListener}{@link OnItemItemClickListener}
 */
public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener {

    void onItemClick(DATA1 data1, DATA2 data2);

    boolean onItemLongClick(DATA1 data1, DATA2 data2);
}

Ceci est une classe abstraite

public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> {
    @Override
    public void onClick(View v) {
        onItemClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onLongClick(View v) {
        return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onItemLongClick(View view, DATA data) {
        return false;
    }
}

Vous n'en avez besoin que

    mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() {
        @Override
        public void onItemClick(View view, Integer integer) {

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });
user8944115
la source
BaseViewHolder en tant que RecyclerView.ViewHolder
user8944115
R.id.tag_view_click a besoin que vous déclariez dans le répertoire des valeurs <item type = "id" name = "tag_view_click" />
user8944115
-1

Exemple avec getTag () / setTag () et écouteur à simple clic pour l'adaptateur entier:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    String[] mDataset = { "One", "Two", "Three" };

    private final View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Object pos = v.getTag(R.id.TAG_POSITION);
            if(pos instanceof Integer) {
                // here is your position in the dataset that was clicked
                int position = (Integer) pos;

                ...
            }
        }
    };

    class MyViewHolder extends RecyclerView.ViewHolder {
        View mItemView;
        TextView mTextView; // as example
            ...

        MyViewHolder(View itemLayoutView){
            super(itemLayoutView);

            mItemView = itemLayoutView;
            mTextView = itemLayoutView.findViewById(R.id.my_text_view_id);
            ....
            itemLayoutView.setOnClickListener(mClickListener);
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // you could use getTag() here for to get previous position 
        // that used the holder and for example cancel prev async requests 
        // to load images for the old position or so.
        //
        // setTag() new position that use the holder, so then user 
        // will click on the itemView - you will be able to get 
        // the position by getTag()
        holder.mItemView.setTag(R.id.TAG_POSITION, position);

        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);
    }

    @Override
    public @NonNull MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View item_layout = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.my_recyclerview_item_layout, parent, false);
        ....
        return new MyViewHolder(item_layout);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

La TAG_POSITION définie dans le fichier tags.xml comme

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Alex
la source