Warum hat RecyclerView nicht onItemClickListener ()?


965

Ich habe nachgeforscht RecyclerViewund war überrascht zu sehen, dass RecyclerViewdies nicht der Fall ist onItemClickListener().

Ich habe zwei Fragen.

Hauptfrage

Ich möchte wissen, warum Google entfernt hat onItemClickListener().

Gibt es ein Leistungsproblem oder etwas anderes?

Sekundäre Frage

Ich habe mein Problem gelöst, indem ich Folgendes geschrieben onClickhabe 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) {

    }
}

Ist das in Ordnung / gibt es einen besseren Weg?


31
Damit Ihr Code funktioniert, müssen Sie itemLayoutView.setOnClickListener (this) hinzufügen. im ViewHolder-Konstruktor.
Whitneyland

3
Sie haben eine nette Frage gestellt ... Viele Entwickler haben die gleichen Zweifel an RecyclerView und ListView.
Venkat

17
Frage: Warum hat RecyclerView OnItemClickListner () nicht? Alle Antworten außer 2: So fügen Sie einen Klick für RecyclerView hinzu
Manohar Reddy

2
Ich wünschte, jemand hätte sich tatsächlich die Zeit genommen, um auf denQUESTION
Ojonugwa Jude Ochalifu

1
Ich denke, sie haben es aufgrund von Leistungs- und Speicherproblemen entfernt. Aber schauen Sie sich die akzeptierte Antwort an ... sie wird die gleichen Probleme verursachen. Ich denke, du hast Fehler gemacht, indem du es akzeptiert hast, und jetzt machen andere Leute Fehler wegen dir :).
Alex

Antworten:


1207

tl; dr 2016 Verwenden Sie RxJava und ein PublishSubject, um ein Observable für die Klicks verfügbar zu machen.

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

Ursprünglicher Beitrag:

Seit der Einführung von ListView, onItemClickListenerhat sich als problematisch gewesen. In dem Moment, in dem Sie einen Klick-Listener für eines der internen Elemente haben, wird der Rückruf nicht ausgelöst, aber er wurde nicht benachrichtigt oder (wenn überhaupt) gut dokumentiert, sodass es viel Verwirrung und SO-Fragen gab.

Da RecyclerViewdies einen Schritt weiter geht und kein Konzept für eine Zeile / Spalte, sondern eine willkürlich festgelegte Anzahl von untergeordneten Elementen enthält, haben sie den onClick an jeden von ihnen oder an die Programmiererimplementierung delegiert.

Stellen Sie sich das Recyclerviewnicht als ListView1: 1-Ersatz vor, sondern als flexiblere Komponente für komplexe Anwendungsfälle. Und wie Sie sagen, ist Ihre Lösung genau das, was Google von Ihnen erwartet hat. Jetzt haben Sie einen Adapter, der onClick an eine Schnittstelle delegieren kann, die an den Konstruktor übergeben wird. Dies ist das richtige Muster für ListViewund 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);
    }

}

und dann auf Ihrem Adapter

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

Schauen Sie sich nun den letzten Code an: onCreateViewHolder(ViewGroup parent, int viewType)Die Signatur schlägt bereits verschiedene Ansichtstypen vor. Für jeden von ihnen benötigen Sie auch einen anderen Ansichtsinhaber, und anschließend kann jeder von ihnen einen anderen Satz von Klicks haben. Oder Sie können einfach einen generischen Ansichtsinhaber erstellen, der eine beliebige Ansicht und eine Ansicht übernimmt onClickListenerund entsprechend angewendet wird . Oder delegieren Sie eine Ebene an den Orchestrator, damit mehrere Fragmente / Aktivitäten dieselbe Liste mit unterschiedlichem Klickverhalten haben. Auch hier ist jede Flexibilität auf Ihrer Seite.

Es ist eine wirklich notwendige Komponente und ziemlich nah an dem, was unsere internen Implementierungen und Verbesserungen bisher ListViewwaren. Es ist gut, dass Google es endlich anerkennt.


78
RecyclerView.ViewHolder verfügt über eine getPosition () -Methode. Mit einigen Anpassungen an diesem Code können Sie die Position an den Listener übergeben.
se_bastiaan

13
Er hat das komplett verpasst onBindViewHolder(holder, position), wo die Infos ausgefüllt werden sollen.
MLProgrammer-CiM

26
@se_bastiaan getPosition()ist veraltet "Diese Methode ist veraltet, da ihre Bedeutung aufgrund der asynchronen * Behandlung von Adapteraktualisierungen nicht eindeutig ist. Verwenden Sie je nach Anwendungsfall {@link #getLayoutPosition ()} oder * {@link #getAdapterPosition ()}. ""
Upenpat

8
Sie haben mehrere Möglichkeiten. Wenn Sie die Position in ein int-Feld innerhalb des Ansichtshalters einfügen, aktualisieren Sie sie während des Bindens und geben Sie sie beim Klicken zurück.
MLProgrammer-CiM

8
@ MLProgrammer-CiM "Sie haben mehrere Möglichkeiten. Fügen Sie die Position in ein int-Feld innerhalb des Ansichtshalters ein, aktualisieren Sie sie während der Bindung und geben Sie sie beim Klicken zurück." Der ViewHolder hat diesen Wert bereits und ist bereits festgelegt. Rufen Sie einfach anviewHolder.getPosition();
Chad Bingham

104

Warum der RecyclerView keine hat onItemClickListener

Das RecyclerViewist eine Toolbox, im Gegensatz zum alten ListViewhat es weniger eingebaute Funktionen und mehr Flexibilität. Das onItemClickListenerist 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 RecyclerViewist 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 RecyclerViewhinzugefü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 RecyclerViewverö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 RecyclerViewdurch 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.xmlund 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 ItemClickSupportViewHolderBenutzeroberfläche eingeführt, über die Sie ViewHoldersteuern 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.OnChildAttachStateChangeListeneran 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 RecyclerViewnach dem, RecyclerView.ViewHolderder 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 RecyclerViewBildlaufs 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.


addOnChildAttachStateChangeListener im Konstruktor und nie entfernen?
Saba

@Saba du sollst den Konstruktor nicht direkt verwenden, sondern durch statische Methoden addTo()und removeFrom(). Die Instanz ist dem recyclerview-Tag zugeordnet und stirbt damit oder wenn es manuell entfernt wird.
Daniele Segato

So viel Code, wenn Sie nur einen On-Click-Listener einstellen müssen, onCreateViewHolderund das war's.
EpicPandaForce

Ihr Argument ist fließend, es geht nicht um die Menge des Codes, sondern um das Prinzip der Effizienz und Verantwortung, und die Menge des Codes ist kein guter Hinweis auf eine gute / schlechte Lösung, insbesondere wenn dieser Code wiederverwendbar und effizient ist. Sobald Sie diesen Code in Ihr Projekt eingefügt haben (den Sie als Bibliothekseinschluss betrachten können), benötigen Sie lediglich einen 1-Liner auf Ihrem RecyclerView.
Daniele Segato

@ DanieleSegato Vielen Dank dafür!
Mr. Octodood

90

Ich mag diesen Weg und ich benutze ihn

Innerhalb

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

Stellen

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

Und erstellen Sie diese Klasse, wo immer Sie möchten

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

Ich habe vorher gelesen, dass es einen besseren Weg gibt, aber ich mag diesen Weg ist einfach und nicht kompliziert.


23
sollte wahrscheinlich MyOnClickListenerin Instanzvariable machen, weil Sie jedes Mal eine neue Instanz davon mit dem newSchlüsselwort
erstellen

14
recyclerView.getChildPosition (v); ist beraubt jetzt verwenden recyclerView. indexOfChild (v);
Qadir Hussain

4
Haben Sie keine Probleme beim Scrollen? Bei meinen Tests scheint die itemPosition vom Recycling der Ansichten abzuhängen, daher geht sie im Vergleich zu meiner vollständigen Liste im Adapter schief.
Simon

16
Wie komme ich recyclerViewin MyOnClickListener?
Guo

3
Anstelle von: int itemPosition = recyclerView.indexOfChild (v); Sie sollten verwenden: RecyclerView.ViewHolder Inhaber = recyclerView.getChildViewHolder (v); int itemPosition = Inhaber.getAdapterPosition ();
Gordon Freeman

35

Android Recyclerview With onItemClickListener, Warum wir es nicht versuchen können, funktioniert ListViewnur so.

Quelle: Link

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

private OnItemClickListener mListener;
public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

  }
}

Und setzen Sie dies auf RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new            LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new   RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Handle item click
                    Log.e("@@@@@",""+position);
                }
            })
    );

7
Nein elegant, um ehrlich zu sein.
Vishal Dharankar

@vishaldharankar warum nicht? Ich denke, dass es der beste Weg ist, um Konflikte mit SwipeRefreshLayout
Fokusänderungen

Dies ist völlig unnötig. Sie können einen View.OnClickListener verwenden, anstatt Berührungen abzufangen.
EpicPandaForce

23

Dank @marmor habe ich meine Antwort aktualisiert.

Ich denke, es ist eine gute Lösung, mit onClick () im ViewHolder- Klassenkonstruktor umzugehen und es über die OnItemClickListener- Schnittstelle an die übergeordnete Klasse zu übergeben .

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

Verwendung des Adapters in anderen Klassen:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

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

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

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

}

34
Dies wird beim Scrollen Tonnen von Objekten erstellen und Müll sammeln. Sie sollten stattdessen einen OnClickListener erstellen und ihn wiederverwenden
marmor

2
Wird onBindViewHolder()beim Scrollen auch mehrmals für dieselbe Ansicht aufgerufen. Selbst das Erstellen einer Single OnClickListenerhilft nicht weiter, da Sie die OnClickListenerjedes Mal zurücksetzen, wenn Sie onBindViewHolder()aufgerufen werden. Siehe stattdessen @ MLProgrammer-CiM-Lösung.
Vovahost

Sie können auch die @ MLProgrammer-CiM-Lösung verbessern. Anstatt für jede Ansicht in dieser Zeile ein Listener-Objekt zu MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....erstellen, können Sie für alle Ansichten einen einzelnen Listener erstellen.
Vovahost

21

Jungs verwenden diesen Code in Ihrer Hauptaktivität. Sehr effiziente Methode

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

Hier ist Ihre Adapterklasse.

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

Danach erhalten Sie diese Überschreibungsmethode in Ihrer Aktivität.

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

5
Ich frage mich, ob Benutzer, die nach oben und unten scrollen, OnClickListener immer wieder erstellen und recyceln werden, es effizient?
Vinh.TV

Um die aktualisierte Position der Karte zu erhalten, kann diese Kleinigkeit kompromittiert werden.
Waqas Ali

Hallo Freund, schön, sein Arbeitscharme. Ich habe anfangs eine Anforderungsaktivität mit Kategorien und Produkten. Wenn der Benutzer ein Kategoriesymbol auswählt, muss ich Kategorien im Dialogfeld oder in der Aktivität anzeigen. Wenn der Benutzer eine Kategorie auswählt, die gefilterte Daten in die erste Aktivität lädt, helfen Sie mir bitte wie zu lösen
Harsha

1. Sie müssen eine Methode namens OnProductClicked in der Schnittstelle OnCardClickListner wie diese öffentliche Schnittstelle hinzufügen. OnCardClickListner {void OnCardClicked (Ansichtsansicht, int-Position); void OnProductClicked (Ansichtsansicht, int-Position); } 2. Implementieren Sie dann den Klick-Listener wie folgt auf Ihrem Produktsymbol. Inhaber.IhrProductIcon.setOnClickListener (neuer View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, Position);}});
Waqas Ali

Ich kann OnCardClicked nicht überschreiben. Wie stimmen die Leute dieser Antwort zu?
The_Martian

19

> Wie unterscheidet sich RecyclerView von Listview?

Ein Unterschied besteht darin, dass es LayoutManagerin RecyclerView eine Klasse gibt, mit der Sie Ihre RecyclerViewLike- verwalten können .

Horizontales oder vertikales Scrollen durchLinearLayoutManager

GridLayout von GridLayoutManager

Gestaffeltes GridLayout von StaggeredGridLayoutManager

Wie beim horizontalen Scrollen für RecyclerView-

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

15

Weiterverfolgung der hervorragenden RxJava-Lösung von MLProgrammer-CiM

Klicks verbrauchen / beobachten

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

Ändern Sie das Original wie folgt :

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

PublishSubject#asObservable()wurde entfernt. Geben Sie einfach das zurück, PublishSubjectwas ein ist Observable.


Woher bekommen Sie mClickConsumer in der zweiten Zeile, wenn Sie dies bitte ein wenig erklären. rxAdapter.getPositionClicks (). subscribe (mClickConsumer);
Bluetoothfx

14

Wie man alles zusammensetzt Beispiel ...

  • onClick () -Handhabung
  • Cursor - RecyclerView
  • ViewHolder-Typen

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

11

Soweit ich die MLProgrammer-CiM- Antwort verstehe , ist es einfach möglich, dies einfach zu tun:

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

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


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

9

Nachdem ich die Antwort von @ MLProgrammer-CiM gelesen habe , ist hier mein Code:

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

Hüten Sie sich vor , dass die getAdapterPositionDose Rückkehr -1für „keine Position“.
EpicPandaForce

9

Ich habe es so gemacht, es ist sehr einfach:

Fügen Sie einfach 1 Zeile für die angeklickte RecyclerView-Position hinzu :

int position = getLayoutPosition()

Vollständiger Code für die ViewHolder- Klasse:

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

Hoffe das wird dir helfen.


Sie sollten getAdapterPositionanstelle von verwenden getLayoutPosition. Schutz vor -1.
EpicPandaForce

7

Ich verwende diese Methode, um eine Absicht von RecyclerView aus zu starten:

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

Es ist kein guter Weg. Weil onBindViewHolder(...)nicht nach notify()Methoden aufgerufen wird . In einigen Situationen kann es daher zu einer falschen Klickposition kommen.
Alex Kisel

5

RecyclerView hat keine, onItemClickListenerda RecyclerView für das Recycling von Ansichten verantwortlich ist (Überraschung!), Daher liegt es in der Verantwortung der Ansicht, die recycelt wird, um die empfangenen Klickereignisse zu verarbeiten.

Dies erleichtert die Verwendung erheblich, insbesondere wenn Sie Elemente hatten, auf die an mehreren Stellen geklickt werden kann.


Das Erkennen eines Klicks auf ein RecyclerView-Element ist jedoch sehr einfach. Sie müssen lediglich eine Schnittstelle definieren (wenn Sie Kotlin nicht verwenden, übergeben Sie in diesem Fall nur ein 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
        }
    }
}

Gleicher Code in 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
        }
    }
}

Was Sie NICHT tun müssen:

1.) Sie müssen Berührungsereignisse nicht manuell abfangen

2.) Sie müssen nicht mit Listenern für untergeordnete Statusänderungen herumspielen

3.) Sie benötigen PublishSubject / PublishRelay von RxJava nicht

Verwenden Sie einfach einen Klick-Listener.


4

Siehe meinen Ansatz dazu:

Deklarieren Sie zuerst eine Schnittstelle wie folgt:

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

Dann erstellen Sie den Adapter:

public class CustomRecyclerAdapter extends RecyclerView.Adapter {      

    private class InternalClickListener implements View.OnClickListener{

      @Override
      public void onClick(View v) {
        if(mRecyclerView != null && mItemClickListener != null){
            // find the position of the item that was clicked
            int position = mRecyclerView.getChildAdapterPosition(v);
            Data data = getItem(position);
            // notify the main listener
            mItemClickListener.onItemClick(v, position, data);
        }
    }
}

private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;    
private InternalClickListener mInternalClickListener;


/**
 *
 * @param itemClickListener used to trigger an item click event
 */
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){        
    mItemClickListener = itemClickListener;
    mInternalClickListener = new InternalClickListener();
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);

    v.setOnClickListener(mInternalClickListener);

    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // do your binding here
}

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

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

    mRecyclerView = recyclerView;
}

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

    mRecyclerView = null;
}

public Data getItem(int position){
    return mDataset.get(position);
}
}

Und jetzt wollen wir sehen, wie man dies aus einem Fragment integriert:

public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
   private RecyclerView mRecyclerView;

   @Override
   public void onItemClick(View view, int position, Data item) {
     // do something
   }

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      return inflater.inflate(R.layout.test_fragment, container, false);
   }

   @Override
   public void onViewCreated(View view, Bundle savedInstanceState) {
      mRecyclerView = view.findViewById(idOfTheRecycler);
      mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
   }

4

Wenn Sie der untergeordneten Ansicht von Elementen onClick () hinzufügen möchten, z. B. eine Schaltfläche in Element, habe ich festgestellt, dass Sie dies in onCreateViewHolder () Ihres eigenen RecyclerView.Adapter wie folgt problemlos tun können:

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

Ich weiß nicht, ob es ein guter Weg ist, aber es funktioniert gut. Wenn jemand eine bessere Idee hat, bin ich sehr froh, es mir zu sagen und meine Antwort zu korrigieren! :) :)


4

Hier ist eine Möglichkeit, es ganz einfach zu implementieren, wenn Sie eine Liste von POJOs haben und eine beim Klicken von außerhalb des Adapters abrufen möchten.

Erstellen Sie in Ihrem Adapter einen Listener für die Klickereignisse und eine Methode zum Festlegen:

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

}}

Implementieren Sie in Ihrem ViewHolder onClickListener und erstellen Sie ein Klassenmitglied, um das von der Ansicht präsentierte POJO vorübergehend auf diese Weise zu speichern (dies ist ein Beispiel, bei dem das Erstellen eines Setters besser wäre):

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

Setzen Sie in Ihrem Adapter das aktuelle POJO, wenn der ViewHolder gebunden ist (oder auf null, wenn die aktuelle Ansicht keine hat):

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

Das war's, jetzt kannst du es so aus deinem Fragment / deiner Aktivität verwenden:

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

Sie versuchen, Interface so zu implementieren, als wäre es eine Object-Methode. Dies wird nicht funktionieren
Akshay

Update: Was Sie brauchen istmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay

@Akshay Sorry, aber ich denke mein Code ist korrekt. Hast Du es versucht? Alle Hörer auf Android folgen dem gleichen Prinzip, und Sie müssen die Benutzeroberfläche mit einem "neuen" (z. B. stackoverflow.com/questions/4709870/… ) instanziieren
Simon

Der Listener sollte nicht statisch sein.
EpicPandaForce

4

Ja, du kannst

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {

    //inflate the view 

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

    ViewHolder holder = new ViewHolder(view);

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

return holder;

3

Hier können Sie mit mehreren Onclicks umgehen (siehe unten) und es ist sehr effizient

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


}

Er meinte das itemView.
Davideas

3

Mein Kommentar wurde geändert ...

public class MyViewHolder extends RecyclerView.ViewHolder {

        private Context mContext;

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

            mContext = itemView.getContext();

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

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

                }
            });
        }

1
Es ist ein Vorschlag, aber besser, wenn Sie diese Antwort löschen, es sei denn, viele Leute stimmen negativ ab. Diese Lösung ist definitiv falsch, sie funktioniert, ist aber schlecht programmiert. Bitte lesen Sie einige Blogs für RecyclerView und studieren Sie einige Adapterbibliotheken. Es gibt bereits viele gültige in Github, wie FlexibleAdapter, FastAdapter und so weiter ...
Davideas

1
Ihre Antwort ist relativ neu in einer langen Liste von Antworten mit hohen Stimmen und am Ende der Seite hat sie immer noch 0 Stimmen. Übrigens sollten Sie im neuen Code verwenden getAdapterPosition().
Davideas

Sie müssen nur das javaDoc der beiden Methoden lesen.
Davideas

3

Überprüfen Sie dieses, in dem ich alle Dinge richtig implementiert habe

RecyclerViewHolder-Klasse

public class RecyclerViewHolder extends RecyclerView.ViewHolder  {

    //view holder is for girdview as we used in the listView
    public ImageView imageView,imageView2;
    public RecyclerViewHolder(View itemView) {
        super(itemView);
        this.imageView=(ImageView)itemView.findViewById(R.id.image);
    }

}

Adapter

public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {

    //RecyclerView will extend to recayclerview Adapter
    private ArrayList<ModelClass> arrayList;
    private Context context;
    private static RecyclerViewClickListener itemListener;
    //constructor of the RecyclerView Adapter
    RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
        this.context=context;
        this.arrayList=arrayList;
        this.itemListener=itemListener;
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //this method will inflate the custom layout and return as viewHolder
        LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
        ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
        RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);

        return listHolder;
    }

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

        final ModelClass modelClass=arrayList.get(position);
        //holder
        RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
        //convert the drawable image into bitmap
        Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
        //set the image into imageView
        mainHolder.imageView.setImageBitmap(image);
        //to handle on click event when clicked on the recyclerview item and
        // get it through the RecyclerViewHolder class we have defined the views there
        mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //get the position of the image which is clicked
             itemListener.recyclerViewListClicked(v,position);
            }
        });

    }

    @Override
    public int getItemCount() {
        return (null!=arrayList?arrayList.size():0);
    }
}

Die Schnittstelle

public interface RecyclerViewClickListener {

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

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


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

        imageView.setImageResource(wallpaperImages[position]);

    }

2

Das hat bei mir funktioniert:

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

Es ist nicht gut Weg Einstellung setOnClickListener(holder)in onBindViewHolder(). Weil onBindViewHolder (...) nach notify () -Methoden nicht aufgerufen wird. In einigen Situationen kann es daher zu einer falschen Klickposition kommen.
Alex Kisel

Sie sollten den Klick-Listener im Inneren erstellen onCreateViewHolder.
EpicPandaForce

2

Greifen Sie auf das mainViewvon rowLayout(cell)für Sie zu RecyclerViewund OnBindViewHolderschreiben Sie in Ihren Code:

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

1

Anstatt die Schnittstelle View.OnClickListener im Ansichtshalter zu implementieren oder eine Schnittstelle in Ihrer Aktivität zu erstellen und zu verbinden und zu implementieren. Ich habe diesen Code für die einfache Implementierung von OnClickListener verwendet.

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

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

        public static class ViewHolder extends RecyclerView.ViewHolder {

            //create a variable mView
            public final View mView;

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

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

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

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

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

            mBackground = mTypedValue.resourceId;
            mValues = items;
        }

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

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

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

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

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

2
Und Sie denken, dass das Erstellen eines neuen OnItemClickListener-Objekts bei jedem onBind () -Methodenaufruf ein guter Ansatz ist?
Alexandru Circus

Gut gemacht. Funktioniert super!
Sam

1

es hat bei mir funktioniert. Hoffe es wird helfen. Einfachster Weg.

Innenansichtshalter

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

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

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

Und lassen Sie mich wissen, haben Sie Fragen zu dieser Lösung?


Sie sollten den onClickListener onCreateViewHolderfür eine bessere Leistung aktivieren , aber dann müssen Sie die Position mit getAdapterPosition()(und Schutz vor -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) {

        }
    });

mit diesem u können Sie genaue Ergebnisse erhalten ..................
dileep krishnan

Die Frage lautet: "Warum hat RecyclerView nicht onItemClickListener ()?" NICHT "Wie implementiere ich onItemClickListener () in RecyclerView?" .
Shashanth

0

Verwenden Sie PlaceHolderView

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

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

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

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

0

Ich habe eine Bibliothek geschrieben, um das Klickereignis für Elemente des Android Recycler-Ansichts zu behandeln. Das gesamte Tutorial finden Sie unter 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
            }
        });

oder um lange Druckvorgänge zu handhaben, die Sie verwenden können

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

Sir, ich habe Ihre Bibliothek ausprobiert, habe aber Probleme mit Maven?
Abhijeet

0

Die Recyclerview-Animation wurde nicht getestet, die andere ist normal. Ich denke, es wurde maximal optimiert. Die Schnittstelle hat andere Verwendungszwecke, die Sie vorübergehend ignorieren können.

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

}

Dies ist die Schnittstelle

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

Dies ist eine abstrakte Klasse

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

Du brauchst es nur

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

        }

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

BaseViewHolder als RecyclerView.ViewHolder
user8944115

R.id.tag_view_click erfordert, dass Sie im Werteverzeichnis <item type = "id" name = "tag_view_click" />
user8944115

-1

Beispiel mit getTag () / setTag () und Single-Click-Listener für den gesamten Adapter:

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

Die in der Datei tags.xml definierte TAG_POSITION als

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.