Mengapa RecyclerView tidak memiliki onItemClickListener ()?

965

Saya sedang menjelajah RecyclerViewdan saya terkejut melihat yang RecyclerViewtidak ada onItemClickListener().

Saya punya dua pertanyaan.

Pertanyaan utama

Saya ingin tahu mengapa Google dihapus onItemClickListener()?

Apakah ada masalah kinerja atau hal lain?

Pertanyaan Sekunder

Saya memecahkan masalah saya dengan menulis onClickdi 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) {

    }
}

Apakah ini ok / adakah cara yang lebih baik?

Tarun Varshney
sumber
31
Agar kode Anda berfungsi, Anda perlu menambahkan itemLayoutView.setOnClickListener (ini); di konstruktor ViewHolder.
Whitneyland
3
Anda mengajukan pertanyaan yang bagus ... banyak pengembang memiliki keraguan yang sama tentang RecyclerView dan ListView.
venkat
17
Pertanyaan: Mengapa RecyclerView tidak memiliki OnItemClickListner ()? Semua jawaban di bawah ini kecuali 2: Cara menambahkan onclick untuk RecyclerView
Manohar Reddy
2
Berharap seseorang benar-benar meluangkan waktu untuk menjawabQUESTION
Ojonugwa Jude Ochalifu
1
Saya pikir mereka menghapusnya karena masalah kinerja dan memori sampah. Tetapi lihat jawaban yang diterima .... itu akan menghasilkan masalah yang sama. Saya pikir Anda membuat kesalahan dengan menerimanya dan sekarang ppls lain melakukan kesalahan karena Anda :).
Alex

Jawaban:

1207

tl; dr 2016 Gunakan RxJava dan PublishSubject untuk mengekspos Observable untuk klik.

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

Pos Asli:

Sejak diperkenalkannya ListView, onItemClickListenertelah bermasalah. Saat Anda memiliki pendengar klik untuk salah satu elemen internal, callback tidak akan dipicu tetapi tidak diberitahukan atau didokumentasikan dengan baik (jika ada) sehingga ada banyak kebingungan dan pertanyaan SO tentang hal itu.

Mengingat hal RecyclerViewitu selangkah lebih maju dan tidak memiliki konsep baris / kolom, melainkan jumlah anak-anak yang ditata secara sewenang-wenang, mereka telah mendelegasikan onClick ke masing-masing dari mereka, atau untuk implementasi programmer.

Pikirkan Recyclerviewbukan sebagai ListViewpengganti 1: 1 tetapi lebih sebagai komponen yang lebih fleksibel untuk kasus penggunaan kompleks. Dan seperti yang Anda katakan, solusi Anda adalah apa yang diharapkan Google dari Anda. Sekarang Anda memiliki adaptor yang dapat mendelegasikan onClick ke antarmuka yang diteruskan pada konstruktor, yang merupakan pola yang benar untuk keduanya ListViewdan 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);
    }

}

dan kemudian pada adaptor Anda

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

Sekarang perhatikan potongan kode terakhir: onCreateViewHolder(ViewGroup parent, int viewType)tanda tangan sudah menyarankan jenis tampilan yang berbeda. Untuk masing-masing dari mereka Anda akan memerlukan penonton yang berbeda juga, dan selanjutnya masing-masing dari mereka dapat memiliki set klik berbeda. Atau Anda bisa membuat viewholder generik yang mengambil tampilan apa pun dan satu onClickListenerdan berlaku sesuai. Atau mendelegasikan satu tingkat ke orkestra sehingga beberapa fragmen / kegiatan memiliki daftar yang sama dengan perilaku klik yang berbeda. Sekali lagi, semua fleksibilitas ada di pihak Anda.

Ini adalah komponen yang sangat dibutuhkan dan cukup dekat dengan apa implementasi dan peningkatan internal kami ListViewsampai sekarang. Ada baiknya Google akhirnya mengakuinya.

MLProgrammer-CiM
sumber
78
RecyclerView.ViewHolder memiliki metode getPosition (). Dengan beberapa penyesuaian pada kode ini, Anda dapat meneruskan posisi ke pendengar.
se_bastiaan
13
Dia benar-benar merindukan onBindViewHolder(holder, position), di mana info seharusnya diisi.
MLProgrammer-CiM
26
@se_bastiaan getPosition()sudah tidak digunakan lagi "Metode ini sudah usang karena artinya ambigu karena penanganan async * pada pembaruan adaptor. Silakan gunakan {@link #getLayoutPosition ()} atau * {@link #getAdapterPosition ()} tergantung pada kasus penggunaan Anda. "
upenpat
8
Anda memiliki beberapa opsi. Menempatkan posisi di bidang int di dalam penampil, perbarui sambil mengikat, lalu kembalikan pada klik adalah salah satunya.
MLProgrammer-CiM
8
@ MLProgrammer-CiM "Anda memiliki beberapa opsi. Menempatkan posisi di bidang int di dalam pemirsa, perbarui sambil mengikat, lalu kembalikan pada klik adalah satu." ViewHolder sudah memiliki nilai ini, dan sudah disetel. Panggil sajaviewHolder.getPosition();
Chad Bingham
104

Mengapa RecyclerView tidak memiliki onItemClickListener

Itu RecyclerViewadalah kotak alat, berbeda dari yang lama ListViewia kurang memiliki fitur dan fleksibilitas lebih. Ini onItemClickListenerbukan satu-satunya fitur yang dihapus dari ListView. Tetapi memiliki banyak pendengar dan metode untuk memperluas sesuai dengan keinginan Anda, itu jauh lebih kuat di tangan kanan;).

Menurut pendapat saya fitur paling kompleks yang dihapus RecyclerViewadalah Fast Scroll . Sebagian besar fitur lainnya dapat dengan mudah diimplementasikan kembali.

Jika Anda ingin tahu fitur keren apa yang RecyclerViewditambahkan, baca jawaban ini untuk pertanyaan lain.

Memori efisien - solusi jatuhkan untuk onItemClickListener

Solusi ini telah diusulkan oleh Hugo Visser , sebuah GDE Android, tepat setelah RecyclerViewdirilis. Dia membuat kelas bebas lisensi tersedia bagi Anda untuk hanya memasukkan kode Anda dan menggunakannya.

Ini menampilkan beberapa fleksibilitas yang diperkenalkan dengan RecyclerViewmemanfaatkan RecyclerView.OnChildAttachStateChangeListener.

Sunting 2019 : versi kotlin oleh saya, java one, dari Hugo Visser, disimpan di bawah

Kotlin / Jawa

Buat file values/ids.xmldan letakkan ini di dalamnya:

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

kemudian tambahkan kode di bawah ini ke sumber Anda

Kotlin

Pemakaian:

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

(itu juga mendukung klik item panjang dan lihat di bawah untuk fitur lain yang saya tambahkan).

implementasi (adaptasi saya ke kode 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) }
}

(lihat di bawah ini, Anda juga perlu menambahkan file XML)

Fitur bonus dari versi Kotlin

Terkadang Anda tidak ingin semua item RecyclerView dapat diklik.

Untuk menangani ini, saya telah memperkenalkan ItemClickSupportViewHolderantarmuka yang dapat Anda gunakan ViewHolderuntuk mengontrol item mana yang dapat diklik.

Contoh:

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

Jawa

Pemakaian:

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

(itu juga mendukung klik item panjang)

Implementasi (komentar ditambahkan oleh saya):

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

Cara kerjanya (mengapa ini efisien)

Kelas ini berfungsi dengan melampirkan a RecyclerView.OnChildAttachStateChangeListenerke RecyclerView. Pendengar ini diberitahu setiap kali seorang anak dilampirkan atau dilepaskan dari RecyclerView. Kode menggunakan ini untuk menambahkan pendengar tap / klik panjang ke tampilan. Pendengar yang meminta RecyclerViewuntuk RecyclerView.ViewHolderyang berisi posisi.

Ini lebih efisien daripada solusi lain karena itu menghindari membuat beberapa pendengar untuk setiap tampilan dan terus menghancurkan dan menciptakan mereka saat RecyclerViewsedang digulir.

Anda juga dapat mengadaptasi kode untuk memberikan kembali dudukan itu sendiri jika Anda membutuhkan lebih banyak.

Komentar terakhir

Perlu diingat bahwa benar-benar baik-baik saja untuk menanganinya di adaptor Anda dengan menetapkan pada setiap tampilan daftar Anda pendengar klik, seperti jawaban lain yang diajukan.

Ini bukan hal yang paling efisien untuk dilakukan (Anda membuat pendengar baru setiap kali Anda menggunakan kembali tampilan) tetapi itu berhasil dan dalam kebanyakan kasus itu bukan masalah.

Ini juga agak menentang pemisahan kekhawatiran karena itu sebenarnya bukan Pekerjaan Adaptor untuk mendelegasikan kejadian klik.

Daniele Segato
sumber
addOnChildAttachStateChangeListener di konstruktor dan tidak pernah menghapusnya?
Saba
@ Sabah Anda tidak seharusnya menggunakan konstruktor secara langsung tetapi melalui metode statis addTo()dan removeFrom(). Mesin virtual dikaitkan dengan tag recyclerview dan mati bersamanya atau ketika dihapus secara manual.
Daniele Segato
Begitu banyak kode ketika semua yang perlu Anda lakukan adalah mengatur pendengar klik onCreateViewHolderdan itu saja.
EpicPandaForce
Argumen Anda dialirkan, ini bukan tentang jumlah kode tetapi prinsip efisiensi dan tanggung jawab dan jumlah kode bukanlah indikasi yang baik dari solusi baik / buruk, khususnya ketika kode tersebut dapat digunakan kembali dan efisien. Setelah Anda memasukkan kode itu ke dalam proyek Anda (yang dapat Anda pertimbangkan sebagai penyertaan perpustakaan) yang Anda butuhkan hanyalah 1-liner pada Anda RecyclerView.
Daniele Segato
@DanieleSegato Terima kasih banyak untuk ini!
Tn. Octodood
90

Saya suka cara ini dan saya menggunakannya

Dalam

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

Taruh

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

Dan buat kelas ini di mana saja Anda inginkan

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

Saya pernah membaca bahwa ada cara yang lebih baik tetapi saya suka cara ini mudah dan tidak rumit.

Abdulaziz Noor
sumber
23
mungkin harus menjadikan MyOnClickListenervariabel instan, karena Anda akan membuat instance baru setiap kali dengan newKata Kunci
user2968401
14
recyclerView.getChildPosition (v); dihilangkan sekarang gunakan recyclerView. indexOfChild (v);
Qadir Hussain
4
Apakah Anda tidak memiliki masalah saat menggulir? Pada pengujian saya, itemPosisi tampaknya bergantung pada daur ulang tampilan, sehingga tidak beres dibandingkan dengan daftar lengkap saya di adaptor.
Simon
16
Bagaimana cara masuk recyclerViewdi MyOnClickListener?
guo
3
Alih-alih menggunakan: int itemPosition = recyclerView.indexOfChild (v); Anda harus menggunakan: RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder (v); int itemPosition = holder.getAdapterPosition ();
Gordon Freeman
35

Android Recyclerview Dengan onItemClickListener, Mengapa kita tidak bisa mencoba ini bekerja seperti ListViewhanya.

Sumber: Tautan

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

  }
}

Dan Setel ini ke RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new            LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new   RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Handle item click
                    Log.e("@@@@@",""+position);
                }
            })
    );
Guruprasad
sumber
7
Tidak elegan untuk jujur.
vishal dharankar
@vishaldharankar mengapa tidak? Saya pikir itu adalah cara terbaik untuk menghindari konflik perubahan fokus setelah menggunakanSwipeRefreshLayout
NickUnuchek
Ini sama sekali tidak perlu, Anda dapat menggunakan View.OnClickListener alih-alih menyadap sentuhan.
EpicPandaForce
23

Berkat @marmor, saya memperbarui jawaban saya.

Saya pikir ini adalah solusi yang baik untuk menangani onClick () di konstruktor kelas ViewHolder dan meneruskannya ke kelas induk melalui antarmuka 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());
    }
}
}

Penggunaan adaptor di kelas lain:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

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

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

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

}
Shayan_Aryan
sumber
34
Ini akan membuat dan mengumpulkan-mengumpulkan banyak objek saat menggulir, Anda harus membuat satu OnClickListener dan menggunakannya kembali
marmor
2
Disebut onBindViewHolder()juga berkali-kali untuk tampilan yang sama saat menggulir. Bahkan membuat satu pun OnClickListenertidak akan membantu karena Anda akan mengatur ulang OnClickListenersetiap kali onBindViewHolder()dipanggil. Lihat sebaliknya @ MLProgrammer-CiM solution.
vovahost
Anda juga dapat meningkatkan solusi @ MLProgrammer-CiM dan alih-alih membuat objek pendengar untuk setiap Tampilan di baris ini, MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....Anda dapat membuat pendengar tunggal untuk semua tampilan.
vovahost
21

Cowok menggunakan kode ini dalam aktivitas utama Anda. Metode yang Sangat Efisien

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

Ini kelas adaptor Anda.

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

Setelah ini, Anda akan mendapatkan metode penggantian ini dalam aktivitas Anda.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }
waqas ali
sumber
5
Saya ingin tahu apakah pengguna menggulir ke atas & bawah, OnClickListener akan membuat & didaur ulang lagi & lagi, apakah ini efisien?
Vinh.TV
Untuk mendapatkan posisi kartu yang diperbarui, hal kecil ini dapat dikompromikan.
waqas ali
hai teman bagus pesona kerjanya, saya memiliki aktivitas persyaratan dengan kategori dan produk pada awalnya, jika pengguna memilih ikon kategori saya perlu menampilkan kategori di kotak dialog atau aktivitas jika pengguna memilih kategori berdasarkan thata memuat data yang difilter ke aktivitas pertama tolong bantu saya bagaimana mengatasi
Harsha
1st: Anda perlu menambahkan metode bernama OnProductClicked di antarmuka OnCardClickListner seperti antarmuka publik ini OnCardClickListner {void OnCardClicked (Lihat tampilan, posisi int); membatalkan OnProductClicked (Tampilan tampilan, posisi int); } Kedua: Kemudian terapkan pendengar klik pada ikon produk Anda seperti ini. holder.yourProductIcon.setOnClickListener (Tampilan baru.OnClickListener () {@Override public void onClick (Lihat v) {onCardClickListner.OnProductClicked (v, position);}});
waqas ali
Saya tidak dapat Mengesampingkan OnCardClicked. Bagaimana tanggapan orang-orang terhadap jawaban ini?
The_Martian
19

> Apa perbedaan RecyclerView dari Listview?

Satu perbedaan adalah bahwa ada LayoutManagerkelas dengan RecyclerView dimana Anda dapat mengatur RecyclerViewlike- Anda

Pengguliran horizontal atau vertikal olehLinearLayoutManager

GridLayout oleh GridLayoutManager

GridLayout terhuyung-huyung oleh StaggeredGridLayoutManager

Seperti untuk pengguliran horizontal untuk RecyclerView-

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

Menindaklanjuti solusi RxJava MLProgrammer-CiM yang sangat baik

Konsumsi / Amati klik

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

Ubah tl asli ; dr sebagai:

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

PublishSubject#asObservable()telah dihapus. Kembalikan PublishSubjectyang merupakan Observable.

Tukang roti
sumber
Di mana Anda mendapatkan mClickConsumer di baris kedua, jika Anda jelaskan sedikit. rxAdapter.getPositionClicks (). berlangganan (mClickConsumer);
bluetoothfx
14

Bagaimana menyatukan semuanya contoh ...

  • penanganan onClick ()
  • Kursor - RecyclerView
  • Jenis ViewHolder

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

Sejauh yang saya mengerti jawaban MLProgrammer-CiM , cukup mungkin untuk melakukan ini:

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

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


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

Setelah membaca jawaban @ MLProgrammer-CiM , berikut adalah kode saya:

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);
        }
    }
}
chad
sumber
Waspadalah yang getAdapterPositiondapat kembali -1untuk "tidak ada posisi".
EpicPandaForce
9

Saya telah melakukan ini, sangat sederhana:

Cukup tambahkan 1 Baris untuk posisi RecyclerView yang Diklik :

int position = getLayoutPosition()

Kode lengkap untuk kelas 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()));
                }
            });
        }
    }

Semoga ini bisa membantu Anda.

Hiren Patel
sumber
Anda harus menggunakan getAdapterPositionbukan getLayoutPosition. Lindungi dari -1.
EpicPandaForce
7

Saya menggunakan metode ini untuk memulai Intent dari RecyclerView:

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

    final MyClass myClass = mList.get(i);
    viewHolder.txtViewTitle.setText(myclass.name);
   ...
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
       public void onClick(View v){
             Intent detailIntent = new Intent(mContext, type.class);                                                            
             detailIntent.putExtra("MyClass", myclass);
             mContext.startActivity(detailIntent);
       }
}
);
tmac12
sumber
Itu bukan cara yang baik. Karena onBindViewHolder(...)tidak dipanggil setelah notify()metode. Jadi, Anda bisa mendapatkan posisi klik yang salah dalam beberapa situasi.
Alex Kisel
5

RecyclerView tidak memiliki onItemClickListenerkarena RecyclerView bertanggung jawab atas daur ulang tampilan (kejutan!), Jadi itu adalah tanggung jawab tampilan yang didaur ulang untuk menangani peristiwa klik yang diterimanya.

Ini sebenarnya membuatnya lebih mudah digunakan, terutama jika Anda memiliki item yang dapat diklik di banyak tempat.


Bagaimanapun, mendeteksi klik pada item RecyclerView sangat mudah. Yang perlu Anda lakukan adalah mendefinisikan antarmuka (jika Anda tidak menggunakan Kotlin, dalam hal ini Anda baru saja memasukkan 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
        }
    }
}

Kode yang sama di 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
        }
    }
}

Hal yang Anda TIDAK perlu melakukan:

1.) Anda tidak perlu mencegat acara sentuh secara manual

2.) Anda tidak perlu dipusingkan dengan pendengar perubahan status anak pasang

3.) Anda tidak perlu PublishSubject / PublishRelay dari RxJava

Cukup gunakan pendengar klik.

EpicPandaForce
sumber
4

Lihat pendekatan saya tentang ini:

Pertama mendeklarasikan antarmuka seperti ini:

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

Kemudian buat adaptor:

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

Dan sekarang mari kita lihat bagaimana mengintegrasikan ini dari sebuah fragmen:

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));
   }
Alexandru Circus
sumber
4

Jika Anda ingin menambahkan onClick () ke tampilan anak item, misalnya, tombol dalam item, saya menemukan bahwa Anda dapat melakukannya dengan mudah di onCreateViewHolder () dari RecyclerView Anda sendiri. Adject seperti ini:

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

saya tidak tahu apakah ini cara yang baik, tetapi itu bekerja dengan baik. Jika ada yang punya ide yang lebih baik, sangat senang untuk memberitahu saya dan memperbaiki jawaban saya! :)

cryyyyy
sumber
4

Berikut ini adalah cara untuk mengimplementasikannya dengan mudah jika Anda memiliki daftar POJO dan ingin mengambilnya di klik dari luar adaptor.

Di adaptor Anda, buat pendengar untuk acara klik dan metode untuk mengaturnya:

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

}

Di ViewHolder Anda, terapkan onClickListener dan buat anggota kelas untuk sementara menyimpan POJO yang disajikan, dengan cara itu (ini adalah contoh, membuat setter akan lebih baik):

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

Kembali ke adaptor Anda, atur POJO saat ini ketika ViewHolder terikat (atau menjadi nol jika tampilan saat ini tidak memilikinya):

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

Itu dia, sekarang Anda bisa menggunakannya seperti ini dari fragmen / aktivitas Anda:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });
Simon
sumber
Anda mencoba mengimplementasikan Antarmuka seolah-olah itu akan menjadi metode Obyek. Ini tidak akan berhasil
Akshay
Pembaruan: Yang Anda butuhkan adalahmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay
@ Akshay Maaf tapi saya pikir kode saya sudah benar. Apakah kamu sudah mencobanya? Semua pendengar di Android mengikuti prinsip yang sama, dan Anda harus membuat instance antarmuka dengan 'baru' (mis: stackoverflow.com/questions/4709870/… )
Simon
Pendengar tidak boleh statis.
EpicPandaForce
4

ya kamu bisa

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {

    //inflate the view 

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

    ViewHolder holder = new ViewHolder(view);

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

return holder;
Ashok Reddy M
sumber
3

Di sini Anda dapat menangani beberapa onclick lihat kode di bawah ini dan ini sangat efisien

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

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

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

    public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {

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


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

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

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

        }


        @Override
        public void onClick(View v) {

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

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

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

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

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

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

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

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

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

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

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

        return feedHolder;
    }

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

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


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

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


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


}
Arpit Patel
sumber
Dia berarti itemView.
Davideas
3

Ubah komentar saya ...

public class MyViewHolder extends RecyclerView.ViewHolder {

        private Context mContext;

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

            mContext = itemView.getContext();

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

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

                }
            });
        }
Jae-Min Kim
sumber
1
Itu saran, tetapi lebih baik jika Anda menghapus jawaban ini, kecuali banyak orang akan memberikan suara negatif. Solusi ini jelas salah, ini berfungsi tetapi pemrogramannya buruk. Silakan baca beberapa blog untuk RecyclerView dan pelajari beberapa pustaka Adaptor, sudah ada banyak yang valid di Github, seperti FlexibleAdapter, FastAdapter dan sebagainya ...
Davideas
1
Jawaban Anda relatif baru dari daftar panjang jawaban tinggi dan di bagian bawah halaman, itu sebabnya masih memiliki 0 suara. Ngomong-ngomong, dalam kode baru, Anda harus menggunakan getAdapterPosition().
Davideas
Anda hanya perlu membaca javaDoc dari 2 metode.
Davideas
3

Periksa ini di mana saya telah menerapkan semua hal dengan cara yang tepat

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

}

Adaptor

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

Antarmuka

public interface RecyclerViewClickListener {

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

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


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

        imageView.setImageResource(wallpaperImages[position]);

    }
Mudassir Khan
sumber
2

Ini bekerja untuk saya:

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


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

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

    @Override
    public void onClick(View view) {
        // Use to get the item clicked getAdapterPosition()
    }
}
Naren
sumber
Hal ini tidak baik cara pengaturan setOnClickListener(holder)di onBindViewHolder(). Karena onBindViewHolder (...) tidak dipanggil setelah metode notify (). Jadi, Anda bisa mendapatkan posisi klik yang salah dalam beberapa situasi.
Alex Kisel
Anda harus membuat pendengar klik di dalam onCreateViewHolder.
EpicPandaForce
2

Akses mainViewdari rowLayout(cell)untuk Anda RecyclerViewdan Anda OnBindViewHoldermenulis kode ini:

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

Alih-alih mengimplementasikan antarmuka View.OnClickListener di dalam pemegang tampilan atau membuat dan antarmuka dan mengimplementasikan antarmuka dalam aktivitas Anda .. Saya menggunakan kode ini untuk sederhana pada implementasi OnClickListener.

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

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

        public static class ViewHolder extends RecyclerView.ViewHolder {

            //create a variable mView
            public final View mView;

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

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

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

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

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

            mBackground = mTypedValue.resourceId;
            mValues = items;
        }

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

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

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

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

        @Override
        public int getItemCount() {
            return mValues.size();
        }
    }
Manikanta
sumber
2
Dan Anda pikir membuat objek OnItemClickListener baru di setiap pemanggilan metode onBind () adalah pendekatan yang baik?
Alexandru Circus
Kerja bagus. Bagus sekali!
sam
1

itu berhasil untuk saya. Semoga ini bisa membantu. Cara paling sederhana.

Di dalam View Holder

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

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

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

Dan beri tahu saya, apakah Anda memiliki pertanyaan tentang solusi ini?

Itu Enam
sumber
Anda harus mengatur onClickListener onCreateViewHolderuntuk kinerja yang lebih baik, tetapi kemudian Anda perlu mendapatkan posisi menggunakan getAdapterPosition()(dan berjaga-jaga terhadap -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) {

        }
    });
dileep krishnan
sumber
menggunakan ini kamu bisa mendapatkan hasil yang tepat ..................
dileep krishnan
Pertanyaannya adalah "Mengapa RecyclerView tidak memiliki onItemClickListener ()?" BUKAN "Bagaimana menerapkan onItemClickListener () di RecyclerView?" .
Shashanth
0

gunakan PlaceHolderView

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

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

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

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

Saya menulis perpustakaan untuk menangani tampilan klik item pendaur ulang android. Anda dapat menemukan seluruh tutorial di 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
            }
        });

atau untuk menangani item tekan lama dapat Anda gunakan

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });
Chathura Jayanath
sumber
Pak saya telah mencoba perpustakaan Anda tetapi menghadapi masalah dengan pakar?
Abhijeet
0

animasi recyclerview belum diuji, yang lain normal. Saya pikir itu telah dioptimalkan secara maksimal. Antarmuka memiliki kegunaan lain, untuk sementara waktu bisa Anda abaikan.

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

}

Ini adalah Interface

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

    void onItemClick(DATA1 data1, DATA2 data2);

    boolean onItemLongClick(DATA1 data1, DATA2 data2);
}

Ini adalah kelas abstrak

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

Anda hanya membutuhkannya

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

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });
pengguna8944115
sumber
BaseViewHolder sebagai RecyclerView.ViewHolder
user8944115
R.id.tag_view_click mengharuskan Anda mendeklarasikan di direktori nilai <item type = "id" name = "tag_view_click" />
user8944115
-1

Contoh dengan getTag () / setTag () dan pendengar sekali klik untuk seluruh adaptor:

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 didefinisikan dalam file tags.xml sebagai

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