Warum der RecyclerView keine hat onItemClickListener
Das RecyclerView
ist eine Toolbox, im Gegensatz zum alten ListView
hat es weniger eingebaute Funktionen und mehr Flexibilität. Das onItemClickListener
ist nicht die einzige Funktion von Listview entfernt werden. Aber es hat viele Zuhörer und Methoden, um es nach Ihren Wünschen zu erweitern, es ist in den richtigen Händen weitaus mächtiger;).
Meiner Meinung nach RecyclerView
ist die schnellste Funktion die komplexeste Funktion, die entfernt wurde . Die meisten anderen Funktionen können problemlos erneut implementiert werden.
Wenn Sie wissen möchten, welche anderen coolen Funktionen RecyclerView
hinzugefügt wurden, lesen Sie diese Antwort auf eine andere Frage.
Speichereffizient - Drop-In-Lösung für onItemClickListener
Diese Lösung wurde vorgeschlagen von Hugo Visser , ein Android - GDE, direkt nach RecyclerView
veröffentlicht wurde. Er hat Ihnen eine lizenzfreie Klasse zur Verfügung gestellt, in der Sie einfach Ihren Code eingeben und verwenden können.
Es zeigt einige der Vielseitigkeit, die RecyclerView
durch die Nutzung von RecyclerView.OnChildAttachStateChangeListener
.
Edit 2019 : Kotlin-Version von mir, Java One, von Hugo Visser, unten aufbewahrt
Kotlin / Java
Erstellen Sie eine Datei values/ids.xml
und fügen Sie diese ein:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
Fügen Sie dann den folgenden Code zu Ihrer Quelle hinzu
Kotlin
Verwendungszweck:
recyclerView.onItemClick { recyclerView, position, v ->
// do it
}
(Es unterstützt auch lange Artikelklicks und siehe unten für eine weitere Funktion, die ich hinzugefügt habe).
Implementierung (meine Anpassung an Hugo Visser Java Code):
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) }
}
(siehe unten Sie müssen auch eine XML-Datei hinzufügen)
Bonusfunktion der Kotlin-Version
Manchmal möchten Sie nicht, dass alle Elemente der RecyclerView anklickbar sind.
Um dies zu handhaben, habe ich die ItemClickSupportViewHolder
Benutzeroberfläche eingeführt, über die Sie ViewHolder
steuern können, auf welches Element geklickt werden kann.
Beispiel:
class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
override val isClickable: Boolean get() = false
override val isLongClickable: Boolean get() = false
}
Java
Verwendungszweck:
ItemClickSupport.addTo(mRecyclerView)
.setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
(Es unterstützt auch lange Artikelklicks)
Implementierung (Kommentare von mir hinzugefügt):
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);
}
}
Wie es funktioniert (warum es effizient ist)
Diese Klasse funktioniert, indem ein RecyclerView.OnChildAttachStateChangeListener
an das angehängt wird RecyclerView
. Dieser Listener wird jedes Mal benachrichtigt, wenn ein Kind an das angeschlossen oder von diesem getrennt wird RecyclerView
. Der Code verwendet dies, um einen Tap / Long Click-Listener an die Ansicht anzuhängen. Dieser Zuhörer fragt RecyclerView
nach dem, RecyclerView.ViewHolder
der die Position enthält.
Dies ist effizienter als andere Lösungen, da nicht mehrere Listener für jede Ansicht erstellt werden und diese während des RecyclerView
Bildlaufs weiterhin zerstört und erstellt werden.
Sie können den Code auch anpassen, um den Halter selbst zurückzugeben, wenn Sie mehr benötigen.
Letzte Bemerkung
Denken Sie daran, dass es VOLLSTÄNDIG in Ordnung ist, dies in Ihrem Adapter zu handhaben, indem Sie in jeder Ansicht Ihrer Liste einen Klick-Listener festlegen, wie bei anderen vorgeschlagenen Antworten.
Es ist einfach nicht die effizienteste Sache (Sie erstellen jedes Mal einen neuen Listener, wenn Sie eine Ansicht wiederverwenden), aber es funktioniert und in den meisten Fällen ist es kein Problem.
Es ist auch ein bisschen gegen die Trennung von Bedenken, da es nicht wirklich die Aufgabe des Adapters ist, Klickereignisse zu delegieren.