RecyclerView neden hiç onItemClickListener
RecyclerView
Eski aksine, bir araç kutusu ListView
o özellikler ve daha fazla esneklik daha az yapı vardır. onItemClickListener
ListView çıkarıldıktan tek özelliği değildir. Ama onu beğeninize göre genişletmek için çok fazla dinleyici ve yöntem var, sağ elinde çok daha güçlü;).
Bence çıkarıldı en karmaşık özellik RecyclerView
olan Hızlı Kaydırma . Diğer özelliklerin çoğu kolayca yeniden uygulanabilir.
Başka hangi harika özelliklerin RecyclerView
eklendiğini bilmek istiyorsanız, bu yanıtı başka bir soruya okuyun .
Bellek verimliliği - onItemClickListener için bırakma çözümü
Bu çözüm , bir Android GDE olan Hugo Visser tarafından piyasaya sürüldükten hemen sonra önerildi . Kodunuzu bırakmanız ve kullanmanız için lisanssız bir sınıf hazırladı.RecyclerView
O ile tanıtıldı yönlülük bazı vitrin RecyclerView
yararlanarak RecyclerView.OnChildAttachStateChangeListener
.
Edit 2019 : kotlin benim tarafımdan, java one, Hugo Visser, aşağıda tutuldu
Kotlin / Java
Bir dosya oluşturun values/ids.xml
ve bunu içine koyun:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
sonra aşağıdaki kodu kaynağınıza ekleyin
Kotlin
Kullanımı:
recyclerView.onItemClick { recyclerView, position, v ->
// do it
}
(ayrıca eklediğim başka bir özellik için uzun öğe tıklamasını destekler ve aşağıya bakın).
uygulama (Hugo Visser Java koduna uyarlamam):
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) }
}
(aşağıya bakın ayrıca bir XML dosyası eklemeniz gerekir)
Kotlin versiyonunun bonus özelliği
Bazen RecyclerView öğesinin tüm öğelerinin tıklanabilir olmasını istemezsiniz.
Bunun üstesinden gelmek için hangi öğenin tıklanabilir olduğunu kontrol ItemClickSupportViewHolder
etmek ViewHolder
için kullanabileceğiniz arayüzü tanıttım.
Misal:
class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
override val isClickable: Boolean get() = false
override val isLongClickable: Boolean get() = false
}
Java
Kullanımı:
ItemClickSupport.addTo(mRecyclerView)
.setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
(ayrıca uzun öğe tıklama destekler)
Uygulama (benim tarafımdan eklenen yorumlar):
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);
}
}
Nasıl çalışır (neden etkilidir)
Bu sınıf, bir takılarak çalışır RecyclerView.OnChildAttachStateChangeListener
için RecyclerView
. Bu dinleyici, bir çocuk her takıldığında veya çocuktan ayrıldığında bildirilir RecyclerView
. Kod, görünüme bir dokunma / uzun tıklama dinleyicisi eklemek için bunu kullanır. Dinleyici RecyclerView
, RecyclerView.ViewHolder
hangisinin pozisyonu içerdiğini sor .
Bu, diğer çözümlerden daha verimlidir çünkü her görünüm için birden fazla dinleyici oluşturmaktan kaçınır ve RecyclerView
kaydırılırken onları yok etmeye ve oluşturmaya devam eder .
Kodu, daha fazlasına ihtiyacınız varsa sahibinin kendisini size geri vermesi için de uyarlayabilirsiniz.
Son açıklama
Listenizin her görünümünde önerilen diğer yanıtlar gibi bir tıklama dinleyici ayarlayarak adaptörünüzde işlemenin TAMAMEN iyi olduğunu unutmayın.
Yapılması gereken en etkili şey bu değil (her görünümü yeniden kullandığınızda yeni bir dinleyici oluşturuyorsunuz) ama işe yarıyor ve çoğu durumda bu bir sorun değil.
Ayrıca, endişelerin ayrılmasına karşı birazdır, çünkü aslında tıklama olaylarını devretmek için Adaptörün İşi değildir.