Botón de manejar Haga clic dentro de una fila en RecyclerView

Estoy usando el siguiente código para manejar clics de fila. ( fuente )

static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener { private GestureDetector gestureDetector; private ClickListener clickListener; public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) { this.clickListener = clickListener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null) { clickListener.onLongClick(child, recyclerView.getChildPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View child = rv.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onClick(child, rv.getChildPosition(child)); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } } 

Sin embargo, esto funciona si quiero tener un botón de borrar en cada fila. No estoy seguro de cómo implementar eso con esto.

Aà ± adà © al oyente OnClick para eliminar el botón que funciona (elimina la fila) pero también activa el onclick en la fila completa.

¿Alguien puede ayudarme a evitar el clic completo en la fila si se hace clic en un solo botón?

Gracias.

así es como manejo múltiples eventos onClick dentro de un recyclerView:

Editar: actualizado para incluir devoluciones de llamada (como se menciona en otros comentarios). He usado una WeakReference en el ViewHolder para eliminar una posible pérdida de memoria.

Definir interfaz:

 public interface ClickListener { void onPositionClicked(int position); void onLongClicked(int position); } 

Entonces el adaptador:

 public class MyAdapter extends RecyclerView.Adapter { private final ClickListener listener; private final List itemsList; public MyAdapter(List itemsList, ClickListener listener) { this.listener = listener; this.itemsList = itemsList; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row_layout), parent, false), listener); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { // bind layout and data etc.. } @Override public int getItemCount() { return itemsList.size(); } public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { private ImageView iconImageView; private TextView iconTextView; private WeakReference listenerRef; public MyViewHolder(final View itemView, ClickListener listener) { super(itemView); listenerRef = new WeakReference<>(listener); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); itemView.setOnClickListener(this); iconTextView.setOnClickListener(this); iconImageView.setOnLongClickListener(this); } // onClick Listener for view @Override public void onClick(View v) { if (v.getId() == iconTextView.getId()) { Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } listenerRef.get().onPositionClicked(getAdapterPosition()); } //onLongClickListener for view @Override public boolean onLongClick(View v) { final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setTitle("Hello Dialog") .setMessage("LONG CLICK DIALOG WINDOW FOR ICON " + String.valueOf(getAdapterPosition())) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.create().show(); listenerRef.get().onLongClicked(getAdapterPosition()); return true; } } } 

Luego, en su actividad / fragmento, lo que sea que pueda implementar: Clicklistener , o clase anónima, si así lo desea:

 MyAdapter adapter = new MyAdapter(myItems, new ClickListener() { @Override public void onPositionClicked(int position) { // callback performed on click } @Override public void onLongClicked(int position) { // callback performed on click } }); 

Para obtener el elemento en el que se hizo clic, coincide con la vista id ievgetId () == whateverItem.getId ()

Espero que este enfoque ayude!

Encuentro eso típicamente:

  • Necesito usar oyentes múltiples porque tengo varios botones.
  • Quiero que mi lógica esté en la actividad y no en el adaptador o el visitante.

Entonces, la respuesta de @mark-keen funciona bien pero tener una interfaz proporciona más flexibilidad:

 public static class MyViewHolder extends RecyclerView.ViewHolder { public ImageView iconImageView; public TextView iconTextView; public MyViewHolder(final View itemView) { super(itemView); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); iconTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconTextViewOnClick(v, getAdapterPosition()); } }); iconImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconImageViewOnClick(v, getAdapterPosition()); } }); } } 

Donde onClickListener está definido en su adaptador:

 public MyAdapterListener onClickListener; public interface MyAdapterListener { void iconTextViewOnClick(View v, int position); void iconImageViewOnClick(View v, int position); } 

Y probablemente establezca a través de su constructor:

 public MyAdapter(ArrayList newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; } 

Luego puede manejar los eventos en su Actividad o donde sea que esté usando su RecyclerView:

 mAdapter = new MyAdapter(mRows, new MyAdapter.MyAdapterListener() { @Override public void iconTextViewOnClick(View v, int position) { Log.d(TAG, "iconTextViewOnClick at position "+position); } @Override public void iconImageViewOnClick(View v, int position) { Log.d(TAG, "iconImageViewOnClick at position "+position); } }); mRecycler.setAdapter(mAdapter); 

Quería una solución que no creara ningún objeto extra (es decir, oyentes) que tuviera que ser recolectada después, y no requiriera anidar un titular de vista dentro de una clase de adaptador.

En la clase ViewHolder

 private static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private final TextView ....// declare the fields in your view private ClickHandler ClickHandler; public MyHolder(final View itemView) { super(itemView); nameField = (TextView) itemView.findViewById(R.id.name); //find other fields here... Button myButton = (Button) itemView.findViewById(R.id.my_button); myButton.setOnClickListener(this); } ... @Override public void onClick(final View view) { if (clickHandler != null) { clickHandler.onMyButtonClicked(getAdapterPosition()); } } 

Puntos a tener en cuenta: la interfaz ClickHandler está definida, pero no se inicializa aquí, por lo que no se supone en el método onClick que alguna vez se haya inicializado.

La interfaz ClickHandler ve así:

 private interface ClickHandler { void onMyButtonClicked(final int position); } 

En el adaptador, establezca una instancia de ‘ClickHandler’ en el constructor y anule onBindViewHolder para inicializar `clickHandler ‘en el titular de la vista:

 private class MyAdapter extends ...{ private final ClickHandler clickHandler; public MyAdapter(final ClickHandler clickHandler) { super(...); this.clickHandler = clickHandler; } @Override public void onBindViewHolder(final MyViewHolder viewHolder, final int position) { super.onBindViewHolder(viewHolder, position); viewHolder.clickHandler = this.clickHandler; } 

Nota: Sé que viewHolder.clickHandler se puede configurar varias veces con el mismo valor exacto, pero es más económico que buscar nulos y bifurcaciones, y no hay costo de memoria, solo una instrucción adicional.

Finalmente, cuando crea el adaptador, se ve obligado a pasar una instancia de ClickHandler al constructor, así:

 adapter = new MyAdapter(new ClickHandler() { @Override public void onMyButtonClicked(final int position) { final MyModel model = adapter.getItem(position); //do something with the model where the button was clicked } }); 

Tenga en cuenta que el adapter es una variable miembro aquí, no una variable local

Solo quería agregar otra solución si ya tiene un oyente táctil reciclador y desea manejar todos los eventos táctiles en él en lugar de tratar el evento táctil por separado en el titular de la vista. La clave de esta versión adaptada de la clase es devolver la vista de botón en la callback onItemClick () cuando se toca, a diferencia del contenedor de elementos. Luego puede probar que la vista sea un botón y llevar a cabo una acción diferente. Tenga en cuenta que al tocar prolongadamente el botón se interpreta como un toque largo en toda la fila.

 public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { public static interface OnItemClickListener { public void onItemClick(View view, int position); public void onItemLongClick(View view, int position); } private OnItemClickListener mListener; private GestureDetector mGestureDetector; public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // Important: x and y are translated coordinates here final ViewGroup childViewGroup = (ViewGroup) recyclerView.findChildViewUnder(e.getX(), e.getY()); if (childViewGroup != null && mListener != null) { final List viewHierarchy = new ArrayList(); // Important: x and y are raw screen coordinates here getViewHierarchyUnderChild(childViewGroup, e.getRawX(), e.getRawY(), viewHierarchy); View touchedView = childViewGroup; if (viewHierarchy.size() > 0) { touchedView = viewHierarchy.get(0); } mListener.onItemClick(touchedView, recyclerView.getChildPosition(childViewGroup)); return true; } return false; } @Override public void onLongPress(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(), e.getY()); if(childView != null && mListener != null) { mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView)); } } }); } public void getViewHierarchyUnderChild(ViewGroup root, float x, float y, List viewHierarchy) { int[] location = new int[2]; final int childCount = root.getChildCount(); for (int i = 0; i < childCount; ++i) { final View child = root.getChildAt(i); child.getLocationOnScreen(location); final int childLeft = location[0], childRight = childLeft + child.getWidth(); final int childTop = location[1], childBottom = childTop + child.getHeight(); if (child.isShown() && x >= childLeft && x <= childRight && y >= childTop && y <= childBottom) { viewHierarchy.add(0, child); } if (child instanceof ViewGroup) { getViewHierarchyUnderChild((ViewGroup) child, x, y, viewHierarchy); } } } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { mGestureDetector.onTouchEvent(e); return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){} @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } } 

Luego usándolo desde actividad / fragmento:

 recyclerView.addOnItemTouchListener(createItemClickListener(recyclerView)); public RecyclerItemClickListener createItemClickListener(final RecyclerView recyclerView) { return new RecyclerItemClickListener (context, recyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { if (view instanceof AppCompatButton) { // ... tapped on the button, so go do something } else { // ... tapped on the item container (row), so do something different } } @Override public void onItemLongClick(View view, int position) { } }); } 

Debes devolver verdadero dentro de onInterceptTouchEvent() cuando manejas un evento de clic.

Primero puede verificar si tiene entradas similares, si obtiene una colección con tamaño 0, comience una nueva consulta para guardar.

O

más profesional y más rápido. crear un disparador de nube (antes de guardar)

mira esta respuesta https://stackoverflow.com/a/35194514/1388852