Obtener el elemento en el que se hizo clic y su posición en RecyclerView


105

Estoy reemplazando mi lista ListViewcon RecyclerView, que se muestra bien, pero me gustaría saber cómo hacer clic en el elemento y su posición, similar al métodoOnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id) que usamos en ListView.

¡Gracias por las ideas!




La forma más fácil para mí: CustomSelectionCallback
ATES

Respuestas:


177

Basado en el enlace: ¿Por qué RecyclerView no tiene onItemClickListener ()? y ¿En qué se diferencia RecyclerView de Listview? , y también la idea general de @ Duncan, doy mi solución aquí:

  • Defina una interfaz RecyclerViewClickListenerpara pasar el mensaje del adaptador a Activity/ Fragment:

    public interface RecyclerViewClickListener {
        public void recyclerViewListClicked(View v, int position);
    }
  • En Activity/ Fragmentimplemente la interfaz, y también pase el oyente al adaptador:

    @Override
    public void recyclerViewListClicked(View v, int position){... ...}
    
    //set up adapter and pass clicked listener this
    myAdapter = new MyRecyclerViewAdapter(context, this);
  • En Adaptery 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());     
    
           }
       }
    }

Después de la prueba, funciona bien.


[ ACTUALIZAR ]

Dado que API 22, RecyclerView.ViewHoler.getPosition()está en desuso, entonces con getLayoutPosition().


22
use el método getLayoutPosition () ya que getPosition () está ahora depurado.
Ankur Chaudhary

2
gracias por este fragmento de código :) solo un consejo: haga referencia a la estática private static RecyclerViewClickListener itemListener;de forma estática en el constructor.
David Artmann

1
@khurramengr Terminé agregando un oyente en cada elemento del método onBindViewHolder. Además, no se podía hacer clic en todos los elementos de mi lista (solo en parte del elemento).
Ferran Negre

1
¿No debería utilizar una WeakReference aquí new MyRecyclerViewAdapter(context, this):? Esto podría causar pérdidas de memoria
RamwiseMatt

2
public void recyclerViewListClicked(View v, int position);El modificador publices redundante para los métodos de interfaz
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
Gracias. getAdapterPosition () es lo que necesitaba.
Ayaz Alifov

1
¡Gracias por el hombre del código! Salvó mi día. No estaba seguro de dónde poner getAdapterPosition () y siguiendo su código lo hice funcionar ... Agradable y fácil :)
Vinay Vissh

Muchas gracias amigo, ¡tu código era exactamente lo que necesito! Happy man
Naveen Nirban Yadav

13

A continuación se muestra un ejemplo para configurar un escucha de clics.

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

    }

¿Qué es getAdapterPostion (); ¿aquí?
TheDevMan

1
la posición de la vista en la que se hizo clic. no es un método personalizado.
Gilberto Ibarra

Esta parece la forma más limpia de hacer esto, especialmente porque casi siempre está interesado en obtener datos usando la posición y es posible que no tenga acceso al objeto RecyclerView.
FrostRocket

Muchas gracias porint position = getAdapterPosition();
Shylendra Madda

Tenga en cuenta que llamar a getAdapterPosition mientras se actualiza la lista, por ejemplo, debido a llamar a notififyXYZ, devolverá -1
David

11

Coloque este código donde defina la vista de reciclador en actividad.

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

Luego haga una clase separada y coloque este código:

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

crear un archivo java con el siguiente código

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

}

y simplemente use el oyente en su objeto RecyclerView.

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

RecyclerItemClickListenerno es una clase de Android predeterminada, debe proporcionar la biblioteca que está utilizando o la clase en sí.
Greg

@Greg gracias !! ¡Olvidé publicar!
MazRoid

5

Si desea hacer clic en el evento de reciclar-Ver desde la actividad / fragmento en lugar del adaptador, también puede usar el siguiente atajo.

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

También puede usar otras formas largas como usar la interfaz


,, hola, pero ¿qué pasa si también tenemos que hacer clic en la posición del elemento? ?
mfaisalhyder

Puede simplemente establecer la posición del elemento en TAG a través del adaptador y luego obtener la etiqueta como se mencionó anteriormente txtStatusChange.getTag (). ToString ()
Amandeep Rohila

@AmandeepRohila Para que el código funcione, primero tengo que desplazarme hasta el final de una vista de reciclaje horizontal. Solo entonces funciona el brindis. ¿No hay alguna manera de hacer que funcione sin tener que desplazarse hacia adelante y hacia atrás primero?
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) {
            }
        }
    )
);

¿Que brujería es esta? Funciona perfectamente.
Bolling

2

Utilice el siguiente código: -

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 no proporciona tal método.

Para administrar los eventos de clic en RecyclerView terminé implementando onClickListener en mi adaptador, al vincular el ViewHolder: En mi ViewHolder mantengo una referencia a la vista raíz (como puede hacer con sus ImageViews, TextViews, etc.) y al vincular el viewHolder le puse una etiqueta con la información que necesito para manejar el clic (como la posición) y un oyente de clics


3
sí, podemos configurar el oyente de clics en ViewHolder, pero necesito devolver la vista y la posición del elemento en el que se hizo clic a mi fragmento para continuar con el proceso
Xcihnegn

1

Esta es la forma más sencilla y fácil de encontrar la posición del elemento en el que se hizo clic:

También me he enfrentado al mismo problema.

Quería encontrar la posición del elemento seleccionado / seleccionado del RecyclerView () y realizar algunas operaciones específicas en ese elemento en particular.

El método getAdapterPosition () funciona de maravilla para este tipo de cosas. Encontré este método después de un día de investigación y después de probar muchos otros métodos.

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

No tiene que utilizar ningún método adicional. Simplemente cree una variable global llamada 'posición' e inicialícela con getAdapterPosition () en cualquiera de los métodos principales del adaptador (clase o similar).

Aquí hay una breve documentación de este enlace .

getAdapterPosition agregado en la versión 22.1.0 int getAdapterPosition () Devuelve la posición del adaptador del elemento representado por este ViewHolder. Tenga en cuenta que esto puede ser diferente de getLayoutPosition () si hay actualizaciones de adaptador pendientes pero aún no se ha realizado una nueva pasada de diseño. RecyclerView no maneja ninguna actualización del adaptador hasta el siguiente recorrido de diseño. Esto puede crear inconsistencias temporales entre lo que el usuario ve en la pantalla y el contenido del adaptador. Esta inconsistencia no es importante ya que será menos de 16 ms, pero podría ser un problema si desea utilizar la posición ViewHolder para acceder al adaptador. A veces, es posible que deba obtener la posición exacta del adaptador para realizar algunas acciones en respuesta a los eventos del usuario. En ese caso, debe usar este método que calculará la posición del adaptador de ViewHolder.

Feliz de ayudar. No dudes en plantear dudas.


¿Cuál es la forma más sencilla y fácil de obtener el valor del elemento de RecyclerView en el que se hizo clic?
अक्षय परूळेकर

1

Mi simple solucion

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

Simplemente coloque o coloque los datos que necesita obtener de la actividad.

Constructor de adaptadores:

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

En actividad:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

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

        selectedPosition = 0;

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

En Adapter onClickLictener en el elemento
en onBindViewHolder

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

Ahora, cada vez que cambia de posición en RecyclerView, se mantiene en el Holder (o tal vez debería llamarse Listener)

Espero que te sea de utilidad

Mi primera publicación; 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
Agregue una explicación, simplemente descargar el código sin introducción y una explicación de cómo esto soluciona el problema no es muy útil.
Mark Rotteveel

0

Prueba de esta manera

Clase de adaptador:

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

}

En tu actividad o fragmento:

ContentAdapter adapter = new ContentAdapter(itemList, this);

Nota: Implemente OnItemClickListener según el contexto proporcionado por usted en la actividad o los métodos de fragmentación y anulación.


Buena solución arriba. ¿Puede agregar a lo anterior para mostrar cómo debería verse el método onItemClick en la Actividad? Estoy tratando de pasar la posición del elemento a una intención (para abrir la siguiente actividad) y sigo obteniendo la posición incorrecta.
AJW

0

Cada vez que utilizo otro enfoque. Las personas parecen almacenar u obtener una posición en una vista, en lugar de almacenar una referencia a un objeto que ViewHolder muestra.

En su lugar, uso este enfoque, y simplemente lo almaceno en ViewHolder cuando se llama a onBindViewHolder (), y establezco una referencia a nula en onViewRecycled ().

Cada vez que ViewHolder se vuelve invisible, se recicla. Así que esto no afecta al gran consumo de memoria.

@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 extensión corta para el método Kotlin
devuelve la posición absoluta de todos los elementos (no la posición de solo los elementos visibles).

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

Y uso

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

0

Utilice getLayoutPosition () en su método java de interfaz personalizada. Esto devolverá la posición seleccionada de un artículo, verifique todos los detalles en

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


1
¡Bienvenido a Stack Overflow! Si bien los enlaces son una excelente manera de compartir conocimientos, realmente no responderán a la pregunta si se rompen en el futuro. Agregue a su respuesta el contenido esencial del enlace que responde a la pregunta. En caso de que el contenido sea demasiado complejo o demasiado grande para caber aquí, describa la idea general de la solución propuesta. Recuerde mantener siempre una referencia de enlace al sitio web de la solución original. Ver: ¿Cómo escribo una buena respuesta?
sɐunıɔ ןɐ qɐp

0

Desde el diseñador, puede establecer la onClickpropiedad de listItem en un método definido con un solo parámetro. Tengo un método de ejemplo definido a continuación. El método getAdapterPosition le dará el índice del listItem seleccionado.

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

Para obtener información sobre cómo configurar un RecyclerView, consulte la documentación aquí: 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
Al responder una pregunta anterior, su respuesta sería mucho más útil para otros usuarios de StackOverflow si incluyera algo de contexto para explicar cómo ayuda su respuesta, particularmente para una pregunta que ya tiene una respuesta aceptada. Ver: ¿Cómo escribo una buena respuesta ?
David Buck

0

Después de muchas pruebas y errores, descubrí que hay una manera muy fácil de hacer esto que es creando una interfaz en la clase de adaptador e implementando eso en un fragmento, ahora aquí viene el giro, instalé el modelo de vista dentro de mi función de anulación presente en el fragment ahora puede enviar los datos de esa función a viemodel y desde allí a cualquier lugar.

No sé si este método es un buen método de codificación, pero hágamelo saber en el comentario y, si alguien quiere ver el, hágamelo saber en la sección de comentarios. Abro regularmente el flujo de 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")
                }

            })

Si usa Kotlin


Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación y probablemente resultaría en más votos a favor. Recuerde que está respondiendo la pregunta a los lectores en el futuro, no solo a la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos.
Богдан Опир

-4

En onViewHolder configure onClickListiner en cualquier vista y en el clic lateral use este código:

Toast.makeText (Drawer_bar.this, "posición" + posición, Toast.LENGTH_SHORT) .show ();

Reemplácelo Drawer_Barcon el nombre de su actividad.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.