ทำไม RecyclerView จึงไม่มี onItemClickListener
RecyclerViewเป็นกล่องเครื่องมือในทางตรงกันข้ามของเดิมListViewก็มีการสร้างน้อยกว่าในคุณสมบัติและความยืดหยุ่นมากขึ้น onItemClickListenerไม่ได้เป็นคุณลักษณะเฉพาะถูกลบออกจาก ListView แต่มีผู้ฟังจำนวนมากและวิธีการที่จะขยายไปยังความชอบของคุณมันมีพลังมากขึ้นในมือขวา;)
ในความคิดของคุณลักษณะที่ซับซ้อนมากที่สุดในการลบออกRecyclerViewเป็นจานด่วนเลื่อน คุณสมบัติอื่น ๆ ส่วนใหญ่สามารถนำมาใช้ใหม่ได้อย่างง่ายดาย
ถ้าคุณต้องการที่จะรู้ว่าสิ่งดีๆอื่น ๆ คุณสมบัติRecyclerViewเพิ่มอ่านนี้คำตอบของคำถามอื่น
หน่วยความจำมีประสิทธิภาพ - โซลูชันดร็อปอินสำหรับ onItemClickListener
โซลูชันนี้ได้รับการเสนอโดยHugo Visserซึ่งเป็น Android GDE หลังจากRecyclerViewได้รับการปล่อยตัว เขาสร้างคลาสที่ไม่มีลิขสิทธิ์ให้คุณเพียงวางรหัสและใช้งาน
มันแสดงบางส่วนของความเก่งกาจที่นำมาด้วยโดยการใช้RecyclerViewRecyclerView.OnChildAttachStateChangeListener
แก้ไข 2019 : รุ่น kotlin โดยฉันหนึ่ง java จาก Hugo Visser ด้านล่าง
Kotlin / Java
สร้างไฟล์values/ids.xmlและวางไว้ในมัน:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="item_click_support" type="id" />
</resources>
จากนั้นเพิ่มรหัสด้านล่างลงในแหล่งที่มาของคุณ
Kotlin
การใช้งาน:
recyclerView.onItemClick { recyclerView, position, v ->
    // do it
}
(นอกจากนี้ยังรองรับการคลิกไอเท็มแบบยาวและดูด้านล่างสำหรับคุณลักษณะอื่นที่ฉันได้เพิ่มไว้)
การใช้งาน (การปรับตัวของฉันกับรหัส 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) }
}
(ดูด้านล่างคุณต้องเพิ่มไฟล์ XML)
คุณสมบัติโบนัสของ Kotlin version
บางครั้งคุณไม่ต้องการให้รายการทั้งหมดของ RecyclerView สามารถคลิกได้
เพื่อจัดการเรื่องนี้ฉันได้แนะนำItemClickSupportViewHolderอินเทอร์เฟซที่คุณสามารถใช้ในViewHolderการควบคุมรายการที่สามารถคลิกได้
ตัวอย่าง:
class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
    override val isClickable: Boolean get() = false
    override val isLongClickable: Boolean get() = false
}
ชวา
การใช้งาน:
ItemClickSupport.addTo(mRecyclerView)
        .setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
    @Override
    public void onItemClicked(RecyclerView recyclerView, int position, View v) {
        // do it
    }
});
(นอกจากนี้ยังสนับสนุนการคลิกรายการยาว)
การใช้งาน (ความคิดเห็นที่เพิ่มโดยฉัน):
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);
    }
}
มันทำงานอย่างไร (ทำไมมันถึงมีประสิทธิภาพ)
ชั้นนี้ทำงานโดยการแนบไปRecyclerView.OnChildAttachStateChangeListener ฟังนี้จะได้รับแจ้งทุกครั้งที่เด็กถูกแนบมาหรือออกจากRecyclerView RecyclerViewรหัสนี้ใช้เพื่อต่อท้ายฟังแตะ / ยาวคลิกเพื่อดู ผู้ฟังนั้นถามถึงRecyclerViewสิ่งRecyclerView.ViewHolderที่บรรจุตำแหน่ง
นี่เป็นวิธีที่มีประสิทธิภาพมากกว่าแล้วเพราะมันหลีกเลี่ยงการสร้างผู้ฟังหลายคนสำหรับแต่ละมุมมองและทำลายและสร้างมันต่อไปในขณะที่RecyclerViewเลื่อนอยู่
คุณสามารถปรับรหัสเพื่อให้ผู้ถือกลับคืนได้เองหากคุณต้องการมากกว่านี้
คำพูดสุดท้าย
โปรดทราบว่ามันเป็นเรื่องปกติที่จะจัดการกับมันในอะแดปเตอร์ของคุณโดยการตั้งค่าในแต่ละมุมมองของรายการของคุณให้เป็น listener การคลิกเช่นเดียวกับคำตอบอื่น ๆ ที่เสนอ
ไม่ใช่สิ่งที่มีประสิทธิภาพที่สุดในการทำ (คุณสร้างผู้ฟังใหม่ทุกครั้งที่คุณใช้มุมมองใหม่) แต่ก็ใช้งานได้และในกรณีส่วนใหญ่มันไม่ใช่ปัญหา
นอกจากนี้ยังเป็นเรื่องเล็กน้อยที่จะแยกแยะความกังวลเพราะงานของอะแดปเตอร์ไม่ใช่ตัวแทนการคลิกเหตุการณ์