Perché RecyclerView non ha onItemClickListener ()?


965

Stavo esplorando RecyclerViewe sono stato sorpreso di vedere che RecyclerViewnon ha onItemClickListener().

Ho due domande.

Domanda principale

Voglio sapere perché Google ha rimosso onItemClickListener()?

C'è un problema di prestazioni o qualcos'altro?

Domanda secondaria

Ho risolto il mio problema scrivendo onClicknel mio 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) {

    }
}

Va bene / esiste un modo migliore?


31
Perché il codice funzioni, è necessario aggiungere itemLayoutView.setOnClickListener (questo); nel costruttore ViewHolder.
Whitneyland,

3
hai fatto una bella domanda ... molti sviluppatori hanno lo stesso dubbio su RecyclerView e ListView.
venkat,

17
Domanda: Perché RecyclerView non ha OnItemClickListner ()? Tutte le risposte di seguito tranne 2: Come aggiungere onclick per RecyclerView
Manohar Reddy

2
Vorrei che qualcuno si fosse davvero preso il tempo di rispondere alQUESTION
Ojonugwa Jude Ochalifu il

1
Penso che l'hanno rimosso a causa di problemi di prestazioni e memoria inutile. Ma guarda la risposta accettata ... produrrà gli stessi problemi. Penso che tu abbia commesso un errore accettandolo e ora altri ppl fanno errori a causa tua :).
Alex,

Risposte:


1207

tl; dr 2016 Usa RxJava e un oggetto PublishSoggject per esporre un osservabile per i clic.

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();
    }
}

Posta originale:

Dall'introduzione di ListView,onItemClickListener è stato problematico. Nel momento in cui si dispone di un listener di clic per uno qualsiasi degli elementi interni, il callback non verrebbe attivato, ma non è stato notificato o ben documentato (se non del tutto), quindi c'era molta confusione e quindi domande al riguardo.

Dato che RecyclerViewfa un ulteriore passo avanti e non ha il concetto di una riga / colonna, ma piuttosto una quantità arbitrariamente strutturata di bambini, hanno delegato onClick a ciascuno di essi o all'implementazione del programmatore.

Pensa Recyclerviewnon come un ListViewsostituto 1: 1 ma piuttosto come un componente più flessibile per casi d'uso complessi. E come dici tu, la tua soluzione è ciò che Google si aspettava da te. Ora hai un adattatore che può delegare onClick a un'interfaccia passata sul costruttore, che è il modello corretto per entrambi ListViewe 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);
    }

}

e quindi sul tuo adattatore

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;
    } 
  ...

Ora guarda l'ultimo pezzo di codice: onCreateViewHolder(ViewGroup parent, int viewType)la firma suggerisce già diversi tipi di vista. Per ognuno di essi avrai bisogno anche di un diverso visualizzatore e successivamente ognuno di essi può avere un diverso set di clic. Oppure puoi semplicemente creare un visualizzatore generico che prenda qualsiasi vista e uno onClickListenere si applichi di conseguenza. O delegare un livello all'orchestrator in modo che diversi frammenti / attività abbiano lo stesso elenco con comportamenti di clic diversi. Ancora una volta, tutta la flessibilità è dalla tua parte.

È un componente davvero necessario e abbastanza vicino a ciò che le nostre implementazioni interne e miglioramenti ListViewerano fino ad ora. È positivo che Google lo riconosca finalmente.


78
RecyclerView.ViewHolder ha un metodo getPosition (). Con alcune modifiche a questo codice, è possibile passare la posizione all'ascoltatore.
se_bastiaan,

13
Ha completamente perso il onBindViewHolder(holder, position), che è dove si suppone che le informazioni siano riempite.
MLProgrammer-CiM

26
@se_bastiaan getPosition()è obsoleto "Questo metodo è obsoleto perché il suo significato è ambiguo a causa della gestione asincrona * degli aggiornamenti dell'adattatore. Utilizzare {@link #getLayoutPosition ()} o * {@link #getAdapterPosition ()} a seconda del caso d'uso. "
Upenpat,

8
Hai diverse opzioni. Mettere la posizione in un campo int all'interno del viewholder, aggiornarla durante l'associazione, quindi restituirla al clic è una.
MLProgrammer-CiM,

8
@ MLProgrammer-CiM "Hai diverse opzioni. Mettere la posizione in un campo int all'interno del viewholder, aggiornarla durante l'associazione, quindi restituirla al clic è una." ViewHolder ha già questo valore ed è già impostato. Basta chiamareviewHolder.getPosition();
Chad Bingham il

104

Perché RecyclerView non ha onItemClickListener

Si RecyclerViewtratta di una cassetta degli attrezzi, a differenza del vecchio ListViewche ha meno funzionalità integrate e maggiore flessibilità. Non onItemClickListenerè l'unica funzione rimossa da ListView. Ma ha molti ascoltatori e metodi per estenderlo a proprio piacimento, è molto più potente nelle mani giuste;).

A mio avviso, la funzione più complessa rimossa RecyclerViewè lo scorrimento veloce . La maggior parte delle altre funzionalità può essere facilmente implementata nuovamente.

Se vuoi sapere quali altre interessanti funzioni RecyclerViewaggiunte leggi questa risposta a un'altra domanda.

Memoria efficiente - soluzione drop-in per onItemClickListener

Questa soluzione è stata proposta da Hugo Visser , un GDE per Android, subito dopo il RecyclerViewrilascio. Ha reso disponibile una lezione senza licenza per inserire semplicemente il codice e utilizzarlo.

Presenta parte della versatilità introdotta RecyclerViewfacendo uso di RecyclerView.OnChildAttachStateChangeListener.

Modifica 2019 : versione kotlin da parte mia, java one, di Hugo Visser, conservata di seguito

Kotlin / Java

Crea un file values/ids.xmle inseriscilo:

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

quindi aggiungi il codice qui sotto alla tua fonte

Kotlin

Uso:

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

(supporta anche il clic lungo dell'elemento e vedi sotto per un'altra funzione che ho aggiunto).

implementazione (il mio adattamento al codice Java di 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) }
}

(vedi sotto devi anche aggiungere un file XML)

Funzione bonus della versione di Kotlin

A volte non si desidera fare clic su tutti gli elementi di RecyclerView.

Per gestirlo ho introdotto l' ItemClickSupportViewHolderinterfaccia che puoi usare sul tuo ViewHolderper controllare quale elemento è cliccabile.

Esempio:

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

Giava

Uso:

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

(supporta anche il clic lungo dell'elemento)

Implementazione (commenti aggiunti da me):

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);
    }
}

Come funziona (perché è efficiente)

Questa classe funziona collegando a RecyclerView.OnChildAttachStateChangeListenera RecyclerView. Questo ascoltatore viene avvisato ogni volta che un bambino viene collegato o disconnesso da RecyclerView. Il codice lo utilizza per aggiungere un listener di tocco / clic lungo alla vista. Che ascoltatore chiedere al RecyclerViewper il RecyclerView.ViewHolderquale contiene la posizione.

Questo è più efficiente di altre soluzioni perché evita di creare più listener per ogni vista e continua a distruggerli e crearli mentre RecyclerViewsi scorre.

Puoi anche adattare il codice per restituirti il ​​titolare stesso se ne hai bisogno.

Osservazione finale

Tieni presente che è COMPLETAMENTE bene gestirlo nel tuo adattatore impostando su ogni vista del tuo elenco un listener di clic, come altre risposte proposte.

Non è solo la cosa più efficiente da fare (crei un nuovo ascoltatore ogni volta che riutilizzi una vista) ma funziona e nella maggior parte dei casi non è un problema.

È anche un po 'contrario alla separazione delle preoccupazioni perché non è in realtà il compito dell'adapter delegare gli eventi di clic.


addOnChildAttachStateChangeListener nel costruttore e non rimuoverlo mai?
Saba,

@Saba non dovresti usare il costruttore direttamente ma attraverso metodi statici addTo()e removeFrom(). L'istanza è associata al tag recyclerview e muore con essa o quando viene rimossa manualmente.
Daniele Segato

Tanto codice quando tutto ciò che dovresti fare è impostare un listener al clic onCreateViewHoldere il gioco è fatto.
EpicPandaForce,

La tua argomentazione è scorrevole, non riguarda la quantità di codice ma il principio di efficienza e responsabilità e la quantità di codice non è una grande indicazione della soluzione buona / cattiva, specialmente quando quel codice è riutilizzabile ed efficiente. Una volta che hai inserito quel codice nel tuo progetto (che puoi considerare come un'inclusione nella libreria) tutto ciò che ti serve è un 1-liner sul tuo RecyclerView.
Daniele Segato,

@DanieleSegato Grazie mille per questo!
Mr. Octodood

90

Mi piace in questo modo e lo sto usando

Dentro

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

Mettere

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

E crea questa classe ovunque tu voglia

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));
    }
}

Ho già letto che esiste un modo migliore, ma mi piace che sia facile e non complicato.


23
probabilmente dovrebbe trasformarsi MyOnClickListenerin variabile di istanza, perché ne new
creerai

14
recyclerView.getChildPosition (v); è deprecato ora usa recyclerView. indexOfChild (v);
Qadir Hussain,

4
Non hai problemi durante lo scorrimento? Nei miei test, itemPosition sembra dipendere dal riciclo delle viste, quindi va storto rispetto al mio elenco completo nell'adattatore.
Simon

16
Come accedere recyclerViewa MyOnClickListener?
guo

3
Invece di usare: int itemPosition = recyclerView.indexOfChild (v); dovresti usare: RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder (v); int itemPosition = holder.getAdapterPosition ();
Gordon Freeman,

35

Recyclerview Android Con onItemClickListener, Perché non possiamo provare questo funziona ListViewsolo come .

Fonte: Link

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) {

  }
}

E impostalo su 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);
                }
            })
    );

7
Non elegante per essere onesto.
vishal dharankar,

@vishaldharankar perché no? Penso che sia il modo migliore per evitare il cambiamento del focus of focus dopo l'usoSwipeRefreshLayout
NickUnuchek,

Questo è del tutto inutile, puoi usare un View.OnClickListener invece di intercettare il tocco.
EpicPandaForce,

23

Grazie a @marmor, ho aggiornato la mia risposta.

Penso che sia una buona soluzione per gestire onClick () nel costruttore della classe ViewHolder e passarlo alla classe genitore tramite l' interfaccia 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());
    }
}
}

Utilizzo dell'adattatore in altre classi:

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
    }
}

}

34
Questo creerà e raccoglierà immondizia tonnellate di oggetti durante lo scorrimento, dovresti invece creare un OnClickListener e riutilizzarlo
marmor

2
Inoltre, onBindViewHolder()viene chiamato più volte per la stessa vista durante lo scorrimento. Anche la creazione di un singolo OnClickListenernon sarà di aiuto, poiché ripristinerai OnClickListenerogni volta che onBindViewHolder()viene chiamato. Vedi invece la soluzione @ MLProgrammer-CiM.
vovahost

Puoi anche migliorare la soluzione @ MLProgrammer-CiM e invece di creare un oggetto listener per ogni vista in questa riga MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....puoi creare un singolo listener per tutte le viste.
vovahost

21

I ragazzi usano questo codice nella tua attività principale. Metodo molto efficiente

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

Ecco la tua classe Adapter.

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;
        }
    }

Successivamente, otterrai questo metodo di sostituzione nella tua attività.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }

5
Mi chiedo se l'utente scorrendo su e giù, OnClickListener creerà e ricicli ancora e ancora, è efficace?
Vinh.TV,

Per ottenere la posizione aggiornata della carta, questa cosa minore può essere compromessa.
waqas ali

ciao amico bello il suo fascino funzionante, ho un'attività richiesta con categorie e prodotti inizialmente, se l'utente seleziona l'icona della categoria devo visualizzare le categorie nella finestra di dialogo o l'attività se l'utente seleziona qualsiasi categoria in base a cui caricare i dati filtrati sulla prima attività per favore aiutatemi come risolvere
Harsha

1 °: è necessario aggiungere un metodo chiamato OnProductClicked nell'interfaccia OnCardClickListner come questa interfaccia pubblica OnCardClickListner {void OnCardClicked (View view, int position); void OnProductClicked (Visualizza vista, posizione int); } 2 °: quindi implementare il listener dei clic sull'icona del prodotto in questo modo. holder.yourProductIcon.setOnClickListener (new View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, position);}});
waqas ali,

Non riesco a ignorare OnCardClicked. In che modo le persone votano questa risposta?
The_Martian

19

> In che modo RecyclerView è diverso da Listview?

Una differenza è che esiste una LayoutManagerclasse con RecyclerView in base alla quale puoi gestire il tuo RecyclerViewlike-

Scorrimento orizzontale o verticale diLinearLayoutManager

GridLayout di GridLayoutManager

Griglia sfalsata StaggeredGridLayoutManager

Come per lo scorrimento orizzontale per RecyclerView-

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);

15

A seguire l'eccellente soluzione RxJava di MLProgrammer-CiM

Consuma / osserva i clic

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. +

Modifica l'originale tl; dr come:

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

PublishSubject#asObservable()è stato rimosso. Basta restituire il PublishSubjectquale è un Observable.


Dove stai ottenendo mClickConsumer sulla seconda riga, se per favore spiegami un po '. . rxAdapter.getPositionClicks () sottoscrivere (mClickConsumer);
bluetoothfx,

14

Come mettere tutto insieme esempio ...

  • gestione onClick ()
  • Cursore - RecyclerView
  • Tipi di 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);
    }
    }

11

Per quanto ho capito la risposta MLProgrammer-CiM , è semplicemente possibile farlo:

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();
    }
}

9

Dopo aver letto la risposta di @ MLProgrammer-CiM , ecco il mio codice:

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);
        }
    }
}

Attenzione che getAdapterPositionpuò tornare -1per "nessuna posizione".
EpicPandaForce,

9

L'ho fatto in questo modo, è molto semplice:

Basta aggiungere 1 riga per la posizione di RecyclerView cliccato :

int position = getLayoutPosition()

Codice completo per 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()));
                }
            });
        }
    }

Spero che questo ti possa aiutare.


Dovresti usare getAdapterPositioninvece di getLayoutPosition. Guardia contro -1.
EpicPandaForce,

7

Uso questo metodo per avviare un Intent da 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);
       }
}
);

Non è un buon modo. Perché onBindViewHolder(...)non viene chiamato dopo i notify()metodi. Pertanto, in alcune situazioni è possibile ottenere una posizione dei clic errata.
Alex Kisel,

5

RecyclerView non ne ha una onItemClickListenerperché RecyclerView è responsabile del riciclo delle viste (sorpresa!), Quindi è responsabilità della vista che viene riciclata per gestire gli eventi di clic che riceve.

Questo in realtà rende molto più facile da usare, soprattutto se si disponevano di elementi su cui è possibile fare clic in più punti.


Ad ogni modo, rilevare un clic su un oggetto RecyclerView è molto semplice. Tutto quello che devi fare è definire un'interfaccia (se non stai usando Kotlin, nel qual caso devi solo passare 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
        }
    }
}

Stesso codice in 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
        }
    }
}

Cosa NON FARE devi fare:

1.) non è necessario intercettare manualmente gli eventi touch

2.) non è necessario scherzare con gli ascoltatori di modifica dello stato degli allegati secondari

3.) Non hai bisogno di PublishSubject / PublishRelay di RxJava

Basta usare un listener di clic.


4

Vedi il mio approccio su questo:

Per prima cosa dichiarare un'interfaccia come questa:

/**
 * 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);
   }

Quindi creare l'adattatore:

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);
}
}

E ora vediamo come integrare questo da un frammento:

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));
   }

4

Se vuoi aggiungere onClick () alla vista figlio degli elementi, ad esempio un pulsante nell'elemento, ho scoperto che puoi farlo facilmente in onCreateViewHolder () del tuo RecyclerView.Adapter proprio in questo modo:

        @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);
        }

non so se sia un buon modo, ma funziona bene. Se qualcuno ha un'idea migliore, molto felice di dirmi e correggere la mia risposta! :)


4

Ecco un modo per implementarlo abbastanza facilmente se hai un elenco di POJO e desideri recuperarne uno a clic dall'esterno dell'adattatore.

Nel tuo adattatore, crea un listener per gli eventi click e un metodo per impostarlo:

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;
}
...

}

Nel ViewHolder, implementa onClickListener e crea un membro della classe per archiviare temporaneamente il POJO che la vista presenta, in questo modo (questo è un esempio, creare un setter sarebbe meglio):

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);
        }
    }

Di nuovo nel tuo adattatore, imposta il POJO corrente quando ViewHolder è associato (o su null se la vista corrente non ne ha uno):

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

Ecco fatto, ora puoi usarlo così dal tuo frammento / attività:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });

stai tentando di implementare l'interfaccia come se fosse un metodo Object. Questo non funzionerà
Akshay,

Aggiornamento: quello che ti serve èmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay,

@Akshay Siamo spiacenti ma penso che il mio codice sia corretto. Hai provato? Tutti gli ascoltatori su Android seguono lo stesso principio ed è necessario creare un'istanza dell'interfaccia con un "nuovo" (ad es. Stackoverflow.com/questions/4709870/… )
Simon,

Il listener non dovrebbe essere statico.
EpicPandaForce,

4

si, puoi

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;

3

Qui puoi gestire più onclick vedi sotto il codice ed è molto efficiente

    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;
        }
    }


}

Intendeva il itemView.
Davideas,

3

Modificato il mio commento ...

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();

                }
            });
        }

1
È un suggerimento, ma è meglio se elimini questa risposta, a meno che molte persone votino negativamente. Questa soluzione è definitivamente sbagliata, funziona ma è una cattiva programmazione. Si prega di leggere alcuni blog per RecyclerView e studiare alcune librerie Adapter, ce ne sono già molte valide in Github, come FlexibleAdapter, FastAdapter e così via ...
Davideas,

1
La risposta è relativamente nuova di una lunga lista di risposte votate e in fondo alla pagina, ecco perché ha ancora 0 voti. A proposito, nel nuovo codice, dovresti usare getAdapterPosition().
Davideas,

Devi solo leggere il javaDoc dei 2 metodi.
Davideas,

3

Controlla questo in cui ho implementato tutte le cose in modo corretto

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);
    }

}

Adattatore

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'interfaccia

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]);

    }

2

Questo ha funzionato per me:

@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()
    }
}

Non è buon setting modo setOnClickListener(holder)in onBindViewHolder(). Perché onBindViewHolder (...) non viene chiamato dopo i metodi notification (). Pertanto, in alcune situazioni è possibile ottenere una posizione dei clic errata.
Alex Kisel,

È necessario creare il listener di clic all'interno onCreateViewHolder .
EpicPandaForce,

2

Accedi al mainViewdi rowLayout(cell)per te RecyclerViewe nel tuo OnBindViewHolderscrivere questo codice:

    @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);
            }
        });
    }

1

Invece di implementare l'interfaccia View.OnClickListener all'interno del view view o creare e interfacciare e implementare l'interfaccia nella tua attività .. Ho usato questo codice per una semplice implementazione di 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();
        }
    }

2
E pensi che la creazione di un nuovo oggetto OnItemClickListener ad ogni chiamata del metodo onBind () sia un buon approccio?
Alexandru Circus,

Buon lavoro. Funziona alla grande!
sam,

1

ha funzionato per me. Spero che possa aiutare. Il modo più semplice.

Inside View Holder

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

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

Inside 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();
                }
            });

Fammi sapere, hai qualche domanda su questa soluzione?


Dovresti impostare onClickListener onCreateViewHolderper prestazioni migliori, ma poi devi ottenere la posizione usando getAdapterPosition()(e -1
guardarti

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) {

        }
    });

usando questo puoi ottenere risultati esatti ..................
dileep krishnan,

La domanda è "Perché RecyclerView non ha onItemClickListener ()?" NON "Come implementare onItemClickListener () in RecyclerView?" .
Shashanth,

0

usa 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));
    }
}

0

Ho scritto una libreria per gestire l'evento clic dell'elemento vista riciclatore Android. Puoi trovare l'intero tutorial in 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
            }
        });

o per gestire la pressione lunga dell'elemento che puoi usare

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });

Signore, ho provato la tua biblioteca ma stai affrontando un problema con Maven?
Abhijeet,

0

l'animazione di recyclerview non è stata testata, l'altra è normale. Penso che sia stato ottimizzato al massimo. L'interfaccia ha altri usi, è possibile ignorarli temporaneamente.

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);

}

Questa è l'interfaccia

/**
 * 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);
}

Questa è una classe astratta

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;
    }
}

Ne hai solo bisogno

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

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });

BaseViewHolder come RecyclerView.ViewHolder
user8944115

R.id.tag_view_click ha bisogno che tu dichiari nella directory dei valori <item type = "id" name = "tag_view_click" />
user8944115

-1

Esempio con getTag () / setTag () e listener a singolo clic per l'intero adattatore:

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;
    }
}

TAG_POSITION definito nel file tags.xml come

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.