Ottieni l'elemento cliccato e la sua posizione in RecyclerView


105

Sto sostituendo il mio ListViewcon RecyclerView, elenco che mostra ok, ma vorrei sapere come ottenere l'elemento cliccato e la sua posizione, simile al metodo OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id)che usiamo ListView.

Grazie per le idee!




modo più semplice per me: CustomSelectionCallback
ATES

Risposte:


177

In base al collegamento: Perché RecyclerView non dispone di onItemClickListener ()? e in che modo RecyclerView è diverso da Listview? , e anche l'idea generale di @ Duncan, do la mia soluzione qui:

  • Definire un'interfaccia RecyclerViewClickListenerper il passaggio del messaggio dall'adattatore a Activity/ Fragment:

    public interface RecyclerViewClickListener {
        public void recyclerViewListClicked(View v, int position);
    }
  • In Activity/ Fragmentimplementa l'interfaccia e passa anche il listener all'adattatore:

    @Override
    public void recyclerViewListClicked(View v, int position){... ...}
    
    //set up adapter and pass clicked listener this
    myAdapter = new MyRecyclerViewAdapter(context, this);
  • In Adaptere ViewHolder:

    public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
       ... ... 
       private Context context;
       private static RecyclerViewClickListener itemListener;
    
    
       public MyRecyclerViewAdapter(Context context, RecyclerViewClickListener itemListener) {
           this.context = context;
           this.itemListener = itemListener;
           ... ...
       }
    
    
       //ViewHolder class implement OnClickListener, 
       //set clicklistener to itemView and, 
       //send message back to Activity/Fragment 
       public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
           ... ...
           public ItemViewHolder(View convertView) {
               super(convertView);
               ... ...
               convertView.setOnClickListener(this);
           }
    
           @Override
           public void onClick(View v) {
               itemListener.recyclerViewListClicked(v, this.getPosition());     
    
           }
       }
    }

Dopo il test, funziona bene.


[ AGGIORNAMENTO ]

Poiché API 22, RecyclerView.ViewHoler.getPosition()è deprecato, quindi invece con getLayoutPosition().


22
usa il metodo getLayoutPosition () poiché getPosition () è sconsigliato ora.
Ankur Chaudhary

2
grazie per questo pezzo di codice :) solo un suggerimento: fare riferimento allo statico private static RecyclerViewClickListener itemListener;in modo statico nel costruttore.
David Artmann

1
@khurramengr Ho finito per aggiungere un ascoltatore in ogni elemento del metodo onBindViewHolder. Inoltre, il mio elemento dell'elenco non era tutto selezionabile (solo una parte dell'elemento).
Ferran Negre

1
In caso di non utilizzare un WeakReference qui: new MyRecyclerViewAdapter(context, this)? Ciò potrebbe causare perdite di memoria
RamwiseMatt

2
public void recyclerViewListClicked(View v, int position);Il modificatore publicè ridondante per i metodi di interfaccia
Joel Raju

35
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyViewHolder>{
    public Context context;
    public ArrayList<RvDataItem> dataItems;

    ...
    constructor
    overrides
    ...

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public Context context;

        public MyViewHolder(View itemView, Context context) {
            super(itemView);

            this.context = context;

            this.textView = (TextView)itemView.findViewById(R.id.textView);

            // on item click
            itemView.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    // get position
                    int pos = getAdapterPosition();

                    // check if item still exists
                    if(pos != RecyclerView.NO_POSITION){
                        RvDataItem clickedDataItem = dataItems.get(pos);
                        Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getName(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
}

4
Grazie. getAdapterPosition () è ciò di cui avevo bisogno.
Ayaz Alifov

1
Grazie per il code man !! Mi ha salvato la giornata. Non ero sicuro di dove mettere getAdapterPosition () e seguendo il tuo codice l'ho fatto funzionare ... Bello e facile :)
Vinay Vissh

Grazie mille amico, il tuo codice era esattamente quello che mi serviva! Uomo felice
Naveen Nirban Yadav

13

Ecco un esempio per impostare un listener di clic.

Adapter extends  RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {  ...  }

 public static class MessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView     tv_msg;
        public TextView     tv_date;
        public TextView     tv_sendTime;
        public ImageView    sharedFile;
        public ProgressBar sendingProgressBar;

        public MessageViewHolder(View v) {
            super(v);
            tv_msg =  (TextView) v.findViewById(R.id.tv_msg);
            tv_date = (TextView)  v.findViewById(R.id.tv_date);
            tv_sendTime = (TextView)  v.findViewById(R.id.tv_sendTime);
            sendingProgressBar = (ProgressBar) v.findViewById(R.id.sendingProgressBar);
            sharedFile = (ImageView) v.findViewById(R.id.sharedFile);

            sharedFile.setOnClickListener(this);

        }

        @Override
        public void onClick(View view) {
        int position  =   getAdapterPosition();


            switch (view.getId()){
                case R.id.sharedFile:


                    Log.w("", "Selected"+position);


                    break;
            }
        }

    }

Cos'è getAdapterPostion (); Qui?
TheDevMan

1
la posizione della vista cliccata. non è un metodo personalizzato.
Gilberto Ibarra

Questo sembra il modo più pulito per farlo, soprattutto perché sei quasi sempre interessato a ottenere dati utilizzando la posizione e potresti non avere accesso all'oggetto RecyclerView.
FrostRocket

Grazie mille perint position = getAdapterPosition();
Shylendra Madda

Tieni presente che la chiamata a getAdapterPosition mentre l'elenco viene aggiornato, ad esempio a causa della chiamata a notififyXYZ, restituirà -1
David

11

Inserisci questo codice nel punto in cui definisci la visualizzazione riciclatore in attività.

    rv_list.addOnItemTouchListener(
            new RecyclerItemClickListener(activity, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View v, int position) {

                    Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
                }
            })
    );

Quindi crea una classe separata e inserisci questo codice:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
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) {

    }
}

7

crea un file java con il codice sottostante

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.getChildLayoutPosition(childView));
        return true;
    }
    return false;
}

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

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

e usa semplicemente il listener sul tuo oggetto RecyclerView.

recyclerView.addOnItemTouchListener(  
new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
  @Override public void onItemClick(View view, int position) {
    // TODO Handle item click
  }
}));

RecyclerItemClickListenernon è una classe Android predefinita, dovresti fornire la libreria che stai utilizzando o la classe stessa.
Greg

@Greg grazie !! ho dimenticato di postare!
MazRoid

5

Se si desidera fare clic sull'evento Recycle-View dall'attività / frammento invece dell'adattatore, è possibile utilizzare anche la seguente scorciatoia.

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final TextView txtStatusChange = (TextView)v.findViewById(R.id.txt_key_status);
                txtStatusChange.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Log.e(TAG, "hello text " + txtStatusChange.getText().toString() + " TAG " + txtStatusChange.getTag().toString());
                        Util.showToast(CampaignLiveActivity.this,"hello");
                    }
                });
                return false;
            }
        });

Puoi anche usare altri modi lunghi come usare l'interfaccia


,, ciao, ma cosa succede se dobbiamo ottenere anche la posizione dell'elemento cliccato. ?
mfaisalhyder

Puoi semplicemente impostare la posizione dell'elemento in TAG tramite l'adattatore e quindi ottenere il tag come menzionato sopra txtStatusChange.getTag ().
ToString

@AmandeepRohila Affinché il codice funzioni, devo prima scorrere fino alla fine di un recyclerview orizzontale. Solo allora il pane tostato funziona. Non c'è un modo per farlo funzionare senza dover scorrere avanti e indietro prima?
iOSAndroidWindowsMobileAppsDev

4
recyclerViewObject.addOnItemTouchListener(
    new RecyclerItemClickListener(
        getContext(),
        recyclerViewObject,
        new RecyclerItemClickListener.OnItemClickListener() {
            @Override public void onItemClick(View view, int position) {
                // view is the clicked view (the one you wanted
                // position is its position in the adapter
            }
            @Override public void onLongItemClick(View view, int position) {
            }
        }
    )
);

Che stregoneria è questa? Funziona perfettamente.
Bolling

2

Usa il codice seguente: -

public class SergejAdapter extends RecyclerView.Adapter<SergejAdapter.MyViewHolder>{

...

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // here you use position
        int position = getAdapterPosition();
        ...

    }
}
}

1

RecyclerView non fornisce tale metodo.

Per gestire gli eventi di clic su RecyclerView ho finito per implementare onClickListener nel mio adattatore, quando si associa il ViewHolder: Nel mio ViewHolder mantengo un riferimento alla vista root (come puoi fare con le tue ImageViews, TextViews, ecc ...) e durante il binding il viewHolder ho impostato un tag su di esso con le informazioni necessarie per gestire il clic (come la posizione) e un clicklistener


3
sì, possiamo impostare l'ascoltatore di clic in ViewHolder, ma ho bisogno di restituire la visualizzazione dell'elemento cliccato e la posizione al mio frammento per un ulteriore processo
Xcihnegn

1

Ecco il modo più semplice e facile per trovare la posizione dell'elemento cliccato:

Ho anche affrontato lo stesso problema.

Volevo trovare la posizione dell'elemento cliccato / selezionato di RecyclerView () ed eseguire alcune operazioni specifiche su quel particolare elemento.

Il metodo getAdapterPosition () funziona come un incantesimo per questo tipo di cose. Ho trovato questo metodo dopo una giornata di lunghe ricerche e dopo aver provato numerosi altri metodi.

int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();

Non è necessario utilizzare alcun metodo aggiuntivo. Basta creare una variabile globale denominata "position" e inizializzarla con getAdapterPosition () in uno dei metodi principali dell'adattatore (classe o simile).

Ecco una breve documentazione da questo collegamento .

getAdapterPosition aggiunto nella versione 22.1.0 int getAdapterPosition () Restituisce la posizione dell'adattatore dell'elemento rappresentato da questo ViewHolder. Notare che questo potrebbe essere diverso da getLayoutPosition () se ci sono aggiornamenti dell'adattatore in sospeso ma non è ancora stato eseguito un nuovo passaggio di layout. RecyclerView non gestisce gli aggiornamenti dell'adattatore fino al successivo attraversamento del layout. Ciò potrebbe creare incoerenze temporanee tra ciò che l'utente vede sullo schermo e il contenuto dell'adattatore. Questa incoerenza non è importante poiché sarà inferiore a 16 ms, ma potrebbe essere un problema se si desidera utilizzare la posizione ViewHolder per accedere all'adattatore. A volte, potrebbe essere necessario ottenere la posizione esatta dell'adattatore per eseguire alcune azioni in risposta agli eventi dell'utente. In tal caso, è necessario utilizzare questo metodo che calcolerà la posizione dell'adattatore del ViewHolder.

Felice di aiutare. Sentiti libero di porre dubbi.


qual è il modo più semplice e più semplice per ottenere il valore dell'elemento cliccato di RecyclerView?
अक्षय परूळेकर

1

La mia semplice soluzione

Make a position holder:

    public class PositionHolder {

    private int position;

    public PositionHolder(int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }
}

Posiziona o inserisci i dati necessari per ottenere dall'attività.

Costruttore dell'adattatore:

public ItemsAdapter(Context context, List<Item> items, PositionHolder positionHolder){
        this.context = context;
        this.items = items;
        this.positionHolder = positionHolder;
}

In attività:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectedPosition = 0;

        positionHolder = new PositionHolder(selectedPosition);
        initView();
    }

In Adapter onClickLictener nell'elemento
in onBindViewHolder

holder.holderButton.setOnClickListener(v -> {
            positionHolder.setPosition(position);
            notifyDataSetChanged();
        });

Ora ogni volta che cambi posizione in RecyclerView, viene mantenuto nell'Holder (o forse dovrebbe essere chiamato Listener)

Spero che sia utile

Il mio primo post; P


0
//Create below methods into the Activity which contains RecyclerView.


private void createRecyclerView() {

final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    MyAdapter myAdapter=new MyAdapter(dataAray,MianActivity.this);
    recyclerView.setAdapter(myAdapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    setRecyclerViewClickListner(recyclerView);
}

private void setRecyclerViewClickListner(RecyclerView recyclerView){

    final GestureDetector gestureDetector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });


    recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
            View child =recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child!=null && gestureDetector.onTouchEvent(motionEvent)){
               int position=recyclerView.getChildLayoutPosition(child);
                String name=itemArray.get(position).name;

            return true;
        }

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

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean b) {

        }
    });
}

3
Si prega di aggiungere una spiegazione, scaricare il codice senza introduzione e una spiegazione su come questo risolve il problema non è molto utile.
Mark Rotteveel

0

Prova in questo modo

Classe adattatore:

 public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

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

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

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

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

    public ViewHolder(View itemView) {
        super(itemView);
        name = (TextView) itemView.findViewById(R.id.name);
        image = (ImageView) itemView.findViewById(R.id.image);
    }

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}

}

Nella tua attività o frammento:

ContentAdapter adapter = new ContentAdapter(itemList, this);

Nota: implementare OnItemClickListener in base al contesto fornito dall'utente nell'attività o nei metodi di frammentazione e overide.


Bella soluzione sopra. Puoi aggiungere a quanto sopra per mostrare come dovrebbe apparire il metodo onItemClick nell'attività? Sto cercando di passare la posizione dell'oggetto a un Intento (per aprire l'attività successiva) e continuo a ottenere la posizione sbagliata.
AJW

0

Ogni volta che uso un altro approccio. Le persone sembrano archiviare o ottenere una posizione su una vista, piuttosto che memorizzare un riferimento a un oggetto visualizzato da ViewHolder.

Uso invece questo approccio e lo memorizzo in ViewHolder quando viene chiamato onBindViewHolder () e imposto il riferimento a null in onViewRecycled ().

Ogni volta che ViewHolder diventa invisibile, viene riciclato. Quindi questo non influisce sul consumo di memoria di grandi dimensioni.

@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
    ...
    holder.displayedItem = adapterItemsList.get(i);
    ...
}

@Override
public void onViewRecycled(ItemViewHolder holder) {
    ...
    holder.displayedItem = null;
    ...
}

class ItemViewHolder extends RecyclerView.ViewHolder {
    ...
    MySuperItemObject displayedItem = null;
    ...
}

0

La breve estensione per il metodo Kotlin
restituisce la posizione assoluta di tutti gli elementi (non la posizione dei soli elementi visibili).

fun RecyclerView.getChildPositionAt(x: Float, y: Float): Int {
    return getChildAdapterPosition(findChildViewUnder(x, y))
}

E l'utilizzo

val position = recyclerView.getChildPositionAt(event.x, event.y)

0

Usa getLayoutPosition () nel metodo java dell'interfaccia personalizzata. Questo restituirà la posizione selezionata di un elemento, controlla tutti i dettagli

https://becody.com/get-clicked-item-and-its-position-in-recyclerview/


1
Benvenuto in Stack Overflow! Sebbene i collegamenti siano un ottimo modo per condividere la conoscenza, non risponderanno davvero alla domanda se si interrompono in futuro. Aggiungi alla tua risposta il contenuto essenziale del collegamento che risponde alla domanda. Nel caso in cui il contenuto sia troppo complesso o troppo grande per adattarsi qui, descrivere l'idea generale della soluzione proposta. Ricordarsi di conservare sempre un collegamento di riferimento al sito Web della soluzione originale. Vedi: come scrivo una buona risposta?
sɐunıɔ ןɐ qɐp

0

Dal progettista è possibile impostare la onClickproprietà di listItem su un metodo definito con un singolo parametro. Ho un metodo di esempio definito di seguito. Il metodo getAdapterPosition ti fornirà l'indice del listItem selezionato.

public void exampleOnClickMethod(View view){
    myRecyclerView.getChildViewHolder(view).getAdapterPosition());
}

Per informazioni sulla configurazione di RecyclerView, consultare la documentazione qui: https://developer.android.com/guide/topics/ui/layout/recyclerview


0
//simply check if the adapter position you get not less than zero

holder.btnDelItem.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(holder.getAdapterPosition()>=0){
            list.remove(holder.getAdapterPosition());
            notifyDataSetChanged();
        }
    }
});

3
Quando rispondi a una vecchia domanda, la tua risposta sarebbe molto più utile per gli altri utenti StackOverflow se includessi un contesto per spiegare come la tua risposta aiuta, in particolare per una domanda che ha già una risposta accettata. Vedi: come scrivo una buona risposta .
David Buck

0

Dopo molte prove ed errori ho scoperto che esiste un modo molto semplice per farlo che è creare un'interfaccia nella classe dell'adattatore e implementare che in frammento ora arriva il colpo di scena, ho istanziato il modello di visualizzazione all'interno della mia funzione di override presente nel framment ora puoi inviare i dati da quella funzione a viemodel e da lì ovunque.

Non so se questo metodo sia un buon metodo di codifica, ma per favore fatemelo sapere nei commenti e se qualcuno vuole vedere il fammi sapere nella sezione commenti, apro regolarmente il flusso di stackover.


0
recyclerView.addOnItemTouchListener(object : AdapterView.OnItemClickListener,
                RecyclerView.OnItemTouchListener {
                override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    TODO("Not yet implemented")
                }

                override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                    TODO("Not yet implemented")
                }

                override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                    TODO("Not yet implemented")
                }

                override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                    TODO("Not yet implemented")
                }

            })

Se usi Kotlin


Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolve il problema aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente si tradurrebbe in più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Богдан Опир

-4

In onViewHolder impostare onClickListiner su qualsiasi visualizzazione e nel clic laterale utilizzare questo codice:

Toast.makeText (Drawer_bar.this, "position" + position, Toast.LENGTH_SHORT) .show ();

Sostituisci Drawer_Barcon il nome della tua attività.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.