Dlaczego RecyclerView nie ma onItemClickListener ()?


965

Badałem RecyclerViewi byłem zaskoczony, widząc, że RecyclerViewnie ma onItemClickListener().

Mam dwa pytania.

Główne pytanie

Chcę wiedzieć, dlaczego Google usunęło onItemClickListener()?

Czy występuje problem z wydajnością lub coś innego?

Pytanie drugie

Rozwiązałem swój problem pisząc onClickw 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) {

    }
}

Czy to w porządku / czy jest jakiś lepszy sposób?


31
Aby kod działał, musisz dodać itemLayoutView.setOnClickListener (this); w konstruktorze ViewHolder.
whitneyland,

3
zadałeś ładne pytanie ... wielu programistów ma takie same wątpliwości co do RecyclerView i ListView.
venkat

17
Pytanie: Dlaczego RecyclerView nie ma OnItemClickListner ()? Wszystkie odpowiedzi poniżej, z wyjątkiem 2: Jak dodać kliknięcie RecyclerView
Manohar Reddy

2
QUESTION
Żałuję, że

1
Myślę, że usunęli go z powodu problemów z wydajnością i pamięcią śmieci. Ale spójrz na przyjętą odpowiedź .... spowoduje to te same problemy. Myślę, że popełniłeś błąd, akceptując to, a teraz inni ludzie popełniają błędy przez ciebie :).
Alex

Odpowiedzi:


1207

tl; dr 2016 Użyj RxJava i PublishSubject, aby wyświetlić Observable dla kliknięć.

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

Oryginalny post:

Od momentu wprowadzenia ListView, onItemClickListenerjest problematyczne. W momencie, gdy masz odbiornik kliknięć dla któregokolwiek z elementów wewnętrznych, wywołanie zwrotne nie zostanie uruchomione, ale nie zostanie powiadomione ani dobrze udokumentowane (jeśli w ogóle), więc było wiele zamieszania i SO pytań na ten temat.

Biorąc pod uwagę, że RecyclerViewidzie o krok dalej i nie ma koncepcji wiersza / kolumny, ale raczej arbitralnie rozmieszczoną liczbę dzieci, delegowali onClick do każdego z nich lub do implementacji programisty.

Myśleć Recyclerviewnie jako ListView1: 1 wymiany, ale raczej jako bardziej elastyczny komponent do skomplikowanych przypadków użycia. I jak mówisz, Twoje rozwiązanie jest tym, czego Google oczekuje od ciebie. Teraz masz adapter, który może delegować onClick do interfejsu przekazanego konstruktorowi, który jest prawidłowym wzorcem zarówno dla, jak ListViewi dla 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);
    }

}

a następnie na adapterze

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

Teraz spójrz na ten ostatni fragment kodu: onCreateViewHolder(ViewGroup parent, int viewType)podpis sugeruje już różne typy widoków. Dla każdego z nich będziesz również potrzebować innego widoku, a następnie każdy z nich może mieć inny zestaw kliknięć. Możesz też po prostu utworzyć ogólny widok, który przyjmuje dowolny widok i jeden onClickListeneri stosuje się odpowiednio. Lub przekaż jeden poziom do koordynatora, aby kilka fragmentów / działań miało tę samą listę z różnymi zachowaniami klikania. Ponownie, cała elastyczność jest po twojej stronie.

Jest to naprawdę potrzebny komponent i dość blisko naszych dotychczasowych wewnętrznych wdrożeń i ulepszeń ListView. Dobrze, że Google w końcu to potwierdza.


78
RecyclerView.ViewHolder ma metodę getPosition (). Po wprowadzeniu pewnych poprawek do tego kodu możesz przekazać pozycję słuchaczowi.
se_bastiaan

13
Całkowicie tęsknił za miejscem onBindViewHolder(holder, position), w którym informacje powinny być wypełnione.
MLProgrammer-CiM

26
@se_bastiaan getPosition()jest przestarzałe "Ta metoda jest przestarzała, ponieważ jej znaczenie jest niejednoznaczne ze względu na asynchroniczną * obsługę aktualizacji adaptera. Użyj {@link #getLayoutPosition ()} lub * {@link #getAdapterPosition ()} w zależności od przypadku użycia. „
upenpat

8
Masz kilka opcji. Umieszczenie pozycji w polu int w widoku, zaktualizuj ją podczas wiązania, a następnie zwróć ją po kliknięciu.
MLProgrammer-CiM

8
@ MLProgrammer-CiM „Masz kilka opcji. Umieszczenie pozycji w polu int w widoku, zaktualizuj ją podczas wiązania, a następnie zwróć ją po kliknięciu.” ViewHolder ma już tę wartość i jest już ustawiony. Wystarczy zadzwonićviewHolder.getPosition();
Chad Bingham,

104

Dlaczego RecyclerView nie ma onItemClickListener

RecyclerViewTo zestaw narzędzi, w przeciwieństwie do starego ListViewma mniej build funkcji i większą elastyczność. To onItemClickListenernie jedyna funkcja usuwana z ListView. Ale ma wielu słuchaczy i metodę, aby rozszerzyć go na twoje upodobania, jest o wiele potężniejszy w odpowiednich rękach;).

Moim zdaniem najbardziej złożoną funkcją usuniętą RecyclerViewjest Szybki przewijanie . Większość innych funkcji można łatwo ponownie wdrożyć.

Jeśli chcesz wiedzieć, jakie inne fajne funkcje zostały RecyclerViewdodane, przeczytaj odpowiedź na inne pytanie.

Wydajna pamięć - rozwiązanie rozwijane dla onItemClickListener

To rozwiązanie zostało zaproponowane przez Hugo Vissera , Androida GDE, zaraz po RecyclerViewwydaniu. Udostępnił ci klasę bez licencji, abyś mógł po prostu wpisać swój kod i go użyć.

Pokazuje niektóre z wszechstronności wprowadzonej RecyclerViewdzięki wykorzystaniu RecyclerView.OnChildAttachStateChangeListener.

Edytuj 2019 : moja wersja kotlin, java one od Hugo Visser, trzymana poniżej

Kotlin / Java

Utwórz plik values/ids.xmli umieść w nim:

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

następnie dodaj poniższy kod do swojego źródła

Kotlin

Stosowanie:

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

(obsługuje także długie klikanie elementów i zobacz poniżej inną funkcję, którą dodałem).

implementacja (moja adaptacja do kodu Java 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) }
}

(patrz poniżej, musisz również dodać plik XML)

Funkcja bonusowa wersji Kotlin

Czasami nie chcesz, aby wszystkie elementy RecyclerView były klikalne.

Aby sobie z tym poradzić, wprowadziłem ItemClickSupportViewHolderinterfejs, którego możesz używać ViewHolderdo kontrolowania, który element można kliknąć.

Przykład:

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

Jawa

Stosowanie:

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

(obsługuje również długie kliknięcie przedmiotu)

Realizacja (komentarze dodane przeze mnie):

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

Jak to działa (dlaczego jest wydajne)

Ta klasa działa poprzez dołączenie a RecyclerView.OnChildAttachStateChangeListenerdo RecyclerView. Ten słuchacz jest powiadamiany za każdym razem, gdy dziecko jest przyłączane lub odłączane od RecyclerView. Kod używa tego, aby dołączyć do widoku detektora stuknięcie / długie kliknięcie. Ten słuchacz pyta RecyclerViewo to, RecyclerView.ViewHolderktóra zawiera pozycję.

Jest to bardziej wydajne niż inne rozwiązania, ponieważ pozwala uniknąć tworzenia wielu detektorów dla każdego widoku i ciągle je niszczy i tworzy podczas RecyclerViewprzewijania.

Możesz również dostosować kod, aby zwrócić samego posiadacza, jeśli potrzebujesz więcej.

Uwaga końcowa

Należy pamiętać, że ZUPEŁNIE dobrze jest obsługiwać go w adapterze, ustawiając w każdym widoku listy odbiornik kliknięć, podobnie jak inne proponowane odpowiedzi.

To po prostu nie jest najbardziej wydajna czynność (tworzysz nowego nasłuchującego za każdym razem, gdy ponownie użyjesz widoku), ale działa, a w większości przypadków nie stanowi to problemu.

Jest to również trochę sprzeczne z rozdzielaniem problemów, ponieważ delegowanie zdarzeń kliknięcia nie jest tak naprawdę zadaniem adaptera.


addOnChildAttachStateChangeListener w konstruktorze i nigdy go nie usuwa?
Saba

@Saba nie należy używać konstruktora bezpośrednio, ale metodami statycznymi addTo()i removeFrom(). Instancja jest powiązana z tagiem recyclinglerview i umiera wraz z nim lub po ręcznym usunięciu.
Daniele Segato

Tyle kodu, kiedy wszystko, co musisz zrobić, to ustawić nasłuchiwanie po kliknięciu onCreateViewHolderi to wszystko.
EpicPandaForce

Twój argument jest płynny, nie chodzi o ilość kodu, ale o zasadę wydajności i odpowiedzialności, a ilość kodu nie jest doskonałym wskaźnikiem dobrego / złego rozwiązania, szczególnie gdy kod ten jest wielokrotnego użytku i wydajny. Gdy umieścisz ten kod w swoim projekcie (który możesz uznać za włączenie do biblioteki), wszystko czego potrzebujesz to 1-linijka na twoim RecyclerView.
Daniele Segato

@DanieleSegato Dziękuję bardzo za to!
Pan Octodood

90

Podoba mi się w ten sposób i używam go

Wewnątrz

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

Położyć

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

I stwórz tę klasę gdziekolwiek chcesz

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

Przeczytałem wcześniej, że istnieje lepszy sposób, ale podoba mi się, że ten sposób jest łatwy i nieskomplikowany.


23
powinien prawdopodobnie przekształcić się MyOnClickListenerw instancję zmienną, ponieważ będziesz tworzył nową instancję za każdym razem za pomocą słowa newkluczowego
użytkownik2968401

14
recyclerView.getChildPosition (v); jest depricated teraz użyj recyclinglerView. indexOfChild (v);
Qadir Hussain

4
Nie masz problemów podczas przewijania? Podczas moich testów itemPosition wydaje się zależeć od recyklingu widoków, więc idzie źle w porównaniu do mojej pełnej listy w adapterze.
Simon

16
Jak dostać się recyclerViewdo MyOnClickListener?
guo

3
Zamiast używać: int itemPosition = recyclerView.indexOfChild (v); powinieneś użyć: RecyclerView.ViewHolder holder = recyclinglerView.getChildViewHolder (v); int itemPosition = holder.getAdapterPosition ();
Gordon Freeman,

35

Android Recyclerview With onItemClickListener, Dlaczego nie możemy spróbować, to działa jak ListViewtylko.

Źródło: 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) {

  }
}

I ustaw to na 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
Szczerze mówiąc, nie jest elegancki.
vishal dharankar

@vishaldharankar dlaczego nie? Myślę, że to najlepszy sposób na uniknięcie konfliktu zmiany ostrości po użyciuSwipeRefreshLayout
NickUnuchek

Jest to całkowicie niepotrzebne, możesz użyć View.OnClickListener zamiast przechwytywać dotyk.
EpicPandaForce

23

Dzięki @marmor zaktualizowałem swoją odpowiedź.

Myślę, że to dobre rozwiązanie do obsługi onClick () w konstruktorze klasy ViewHolder i przekazania go do klasy nadrzędnej poprzez interfejs 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());
    }
}
}

Zastosowanie adaptera w innych klasach:

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
Spowoduje to utworzenie i odebranie ton obiektów podczas przewijania, zamiast tego należy utworzyć jeden OnClickListener i ponownie go użyć
marmor

2
onBindViewHolder()Jest również nazywany wiele razy dla tego samego widoku podczas przewijania. Nawet utworzenie jednego OnClickListenernie pomoże, ponieważ będziesz resetowany za OnClickListenerkażdym razem, gdy onBindViewHolder()zostanie wywołane. Zobacz zamiast tego @ MLProgrammer-CiM rozwiązanie.
vovahost

Możesz także ulepszyć rozwiązanie @ MLProgrammer-CiM i zamiast tworzyć obiekt detektora dla każdego widoku w tym wierszu MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ...., możesz utworzyć jeden detektor dla wszystkich widoków.
vovahost

21

Faceci używają tego kodu w głównej działalności. Bardzo skuteczna metoda

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

Oto twoja klasa adaptera.

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

Następnie otrzymasz tę metodę zastępowania w swojej aktywności.

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

5
Zastanawiam się, czy użytkownik przewija w górę i w dół, OnClickListener będzie tworzył i będzie przetwarzał w kółko, czy to skutecznie?
Vinh.TV

Aby uzyskać zaktualizowaną pozycję karty, ta drobna sprawa może być zagrożona.
waqas ali

cześć, przyjacielu, miło działa, mam wymaganą aktywność z kategoriami i produktami na początku, jeśli użytkownik wybiera ikonę kategorii, muszę wyświetlić kategorie w oknie dialogowym lub aktywność, jeśli użytkownik wybierze dowolną kategorię, na której ładowane są przefiltrowane dane do pierwszej aktywności, proszę o pomoc jak rozwiązać
Harsha

1-cia: Musisz dodać metodę o nazwie OnProductClicked w interfejsie OnCardClickListner, podobnie jak ten publiczny interfejs OnCardClickListner {void OnCardClicked (Widok widoku, pozycja wewnętrzna); void OnProductClicked (Zobacz widok, pozycja wewnętrzna); } 2nd: Następnie zaimplementuj detektor kliknięć na ikonie produktu w ten sposób. holder.yourProductIcon.setOnClickListener (nowy View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, position);}});
waqas ali

Nie mogę zastąpić OnCardClicked. Jak ludzie popierają tę odpowiedź?
The_Martian

19

> Czym RecyclerView różni się od widoku listy?

Jedną różnicą jest to, że istnieje LayoutManagerklasa z RecyclerView, dzięki której możesz zarządzać swoimi RecyclerViewpodobnymi-

Przewijanie w poziomie lub w pionie wedługLinearLayoutManager

GridLayout przez GridLayoutManager

Przesunięty GridLayout przez StaggeredGridLayoutManager

Podobnie jak w przypadku przewijania w poziomie dla RecyclerView-

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

15

Kontynuując doskonałe rozwiązanie RxJava MLProgrammer-CIM

Zużyj / Obserwuj kliknięcia

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

Zmodyfikuj oryginalny tl; dr jako:

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

PublishSubject#asObservable()zostało usunięte. Zwróć tylko to, PublishSubjectco jest Observable.


Skąd bierzesz mClickConsumer w drugim wierszu, jeśli trochę wyjaśnię. rxAdapter.getPositionClicks (). subscribe (mClickConsumer);
bluetoothfx

14

Jak złożyć to wszystko przykład ...

  • Obsługa onClick ()
  • Kursor - RecyclerView
  • Typy 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

O ile rozumiem odpowiedź MLProgrammer-CiM , po prostu można to zrobić:

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

Po przeczytaniu odpowiedzi @ MLProgrammer-CiM , oto mój kod:

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

Uważaj, aby getAdapterPositionzwrócić -1„brak pozycji”.
EpicPandaForce

9

Zrobiłem w ten sposób, to bardzo proste:

Wystarczy dodać 1 linię dla klikniętego RecyclerView pozycji :

int position = getLayoutPosition()

Pełny kod dla klasy 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()));
                }
            });
        }
    }

Mam nadzieję, że to ci pomoże.


Powinieneś użyć getAdapterPositionzamiast getLayoutPosition. Strzeż się -1.
EpicPandaForce

7

Używam tej metody, aby uruchomić zamiar z 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);
       }
}
);

To nie jest dobry sposób. Ponieważ onBindViewHolder(...)nie jest wywoływany po notify()metodach. W niektórych sytuacjach możesz uzyskać niewłaściwą pozycję kliknięcia.
Alex Kisel

5

RecyclerView nie ma, onItemClickListenerponieważ RecyclerView jest odpowiedzialny za recykling widoków (niespodzianka!), Więc jest odpowiedzialny za widok, który jest przetwarzany, aby obsłużyć otrzymywane zdarzenia kliknięcia.

To w rzeczywistości znacznie ułatwia korzystanie, zwłaszcza jeśli masz przedmioty, które można kliknąć w wielu miejscach.


W każdym razie wykrycie kliknięcia elementu RecyclerView jest bardzo łatwe. Wszystko, co musisz zrobić, to zdefiniować interfejs (jeśli nie używasz Kotlina, w takim przypadku po prostu przekazujesz 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
        }
    }
}

Ten sam kod w Kotlinie:

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

Rzecz, której NIE musisz robić:

1.) nie musisz ręcznie przechwytywać zdarzeń dotykowych

2.) nie musisz zadzierać ze słuchaczami dołączającymi zmiany stanu dziecka

3.) nie potrzebujesz PublishSubject / PublishRelay z RxJava

Wystarczy użyć detektora kliknięć.


4

Zobacz moje podejście do tego:

Najpierw zadeklaruj taki interfejs:

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

Następnie utwórz adapter:

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

A teraz zobaczmy, jak zintegrować to z fragmentu:

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

Jeśli chcesz dodać onClick () do podrzędnego widoku elementów, na przykład przycisku w elemencie, odkryłem, że możesz to łatwo zrobić w onCreateViewHolder () własnego RecyclerView.Adapter tak:

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

nie wiem, czy to dobry sposób, ale działa dobrze. Jeśli ktoś ma lepszy pomysł, bardzo miło mi powiedzieć i poprawić moją odpowiedź! :)


4

Oto sposób na dość łatwą implementację, jeśli masz listę POJO i chcesz pobrać jedno po kliknięciu spoza adaptera.

W adapterze utwórz detektor dla zdarzeń kliknięcia i metodę ustawienia:

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

}

W swoim ViewHolder zaimplementuj onClickListener i utwórz członka klasy, aby tymczasowo przechowywać POJO, który widok przedstawia w ten sposób (jest to przykład, lepiej byłoby utworzyć seter):

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

Z powrotem w adapterze ustaw bieżące POJO, gdy ViewHolder jest powiązany (lub zerowy, jeśli bieżący widok go nie ma):

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

To wszystko, teraz możesz użyć tego w następujący sposób ze swojego fragmentu / aktywności:

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

próbujesz zaimplementować interfejs tak, jakby był to metoda obiektowa. To nie zadziała
Akshay,

Aktualizacja: PotrzebujeszmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay,

@Akshay Przepraszamy, ale myślę, że mój kod jest poprawny. Próbowałeś? Wszyscy nasłuchujący na Androidzie przestrzegają tej samej zasady i musisz utworzyć interfejs z „nowym” (np. Stackoverflow.com/questions/4709870/... )
Simon,

Słuchacz nie powinien być statyczny.
EpicPandaForce

4

tak, możesz

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

Tutaj możesz obsługiwać wiele kliknięć, patrz poniższy kod i jest to bardzo wydajne

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


}

Miał na myśli itemView.
Davideas,

3

Zmodyfikowałem mój komentarz ...

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
To sugestia, ale lepiej, jeśli usuniesz tę odpowiedź, chyba że wiele osób zagłosuje negatywnie. To rozwiązanie jest zdecydowanie złe, działa, ale źle programuje. Przeczytaj kilka blogów dotyczących RecyclerView i zapoznaj się z bibliotekami adapterów, w Github jest już wiele ważnych, takich jak FlexibleAdapter, FastAdapter itd.
Davideas,

1
Twoja odpowiedź jest stosunkowo nowa z długiej listy wysoko głosowanych odpowiedzi i na dole strony, dlatego wciąż ma 0 głosów. Nawiasem mówiąc, w nowym kodzie powinieneś użyć getAdapterPosition().
Davideas

Musisz tylko przeczytać javaDoc z 2 metod.
Davideas

3

Sprawdź ten, w którym zaimplementowałem wszystkie rzeczy we właściwy sposób

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

}

Adapter

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

Interfejs

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

To działało dla mnie:

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

Ustawienie to dobry sposób nie jest setOnClickListener(holder)w onBindViewHolder(). Ponieważ onBindViewHolder (...) nie jest wywoływany po metod powiadomień (). W niektórych sytuacjach możesz uzyskać niewłaściwą pozycję kliknięcia.
Alex Kisel

Powinieneś utworzyć wewnątrz detektor kliknięć onCreateViewHolder.
EpicPandaForce

2

Uzyskaj dostęp do mainViewi rowLayout(cell)dla siebie RecyclerViewi OnBindViewHoldernapisz ten kod:

    @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

Zamiast implementować interfejs View.OnClickListener wewnątrz uchwytu widoku lub tworzyć interfejs i implementować interfejs w twojej działalności. Użyłem tego kodu do prostej implementacji 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
I uważasz, że tworzenie nowego obiektu OnItemClickListener przy każdym wywołaniu metody onBind () jest dobrym podejściem?
Alexandru Circus

Dobra robota. Działa świetnie!
sam

1

zadziałało dla mnie. Mam nadzieję, że to pomoże. Najprostszy sposób.

Uchwyt do widoku wnętrza

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

Daj mi znać, czy masz jakieś pytania dotyczące tego rozwiązania?


Powinieneś ustawić onClickListener w onCreateViewHolder celu uzyskania lepszej wydajności, ale potem musisz uzyskać pozycję za pomocą getAdapterPosition()(i strzec się -1)
EpicPandaForce

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

        }
    });

korzystając z tego możesz uzyskać dokładne wyniki ...............
dileep krishnan

Pytanie brzmi: „Dlaczego RecyclerView nie ma onItemClickListener ()?” NIE „Jak zaimplementować onItemClickListener () w RecyclerView?” .
Shashanth

0

użyj 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

Napisałem bibliotekę do obsługi zdarzenia kliknięcia elementu wyświetlającego recykling androida. Cały samouczek można znaleźć w 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
            }
        });

lub do obsługi długiego naciśnięcia elementu, którego możesz użyć

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

Sir, próbowałem twojej biblioteki, ale napotykasz problem z maven?
Abhijeet,

0

animacja recyclinglerview nie została przetestowana, druga jest normalna. Myślę, że został zoptymalizowany do maksimum. Interfejs ma inne zastosowania, które możesz tymczasowo zignorować.

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

}

To jest interfejs

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

To jest klasa abstrakcyjna

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

Potrzebujesz tylko tego

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

        }

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

BaseViewHolder jako RecyclerView.ViewHolder
user8944115

R.id.tag_view_click wymaga zadeklarowania w katalogu wartości <item type = "id" name = "tag_view_click" />
user8944115

-1

Przykład z getTag () / setTag () i detektorem pojedynczego kliknięcia dla całego adaptera:

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 zdefiniowany w pliku tags.xml jako

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.