Estoy usando el siguiente código para manejar los clics en las filas. ( 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.
Adjunté el 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 hacer clic en una fila completa si se hace clic en un solo botón?
Gracias.
fuente
this
está haciendo referencia a sí mismo, el ViewHolder (que es una clase estática separada en este caso): está configurando el oyente en el Viewholder que tiene sus datos vinculadosonBindViewHolder()
, no tiene nada que ver con el contexto en el Adaptador. No sé qué problemas tienes exactamente, pero esta solución funciona bien.android:background="?attr/selectableItemBackground"
en su xml para la vista?Encuentro que normalmente:
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 su constructor:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; }
Luego, puede manejar los eventos en su Actividad o donde sea que se use 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);
fuente
onClickListener
a la clase Viewholder anidada estática? A menos que me esté perdiendo algo, no puedo ver cómo se lo pasa a su ViewHolder. Además, si solo usa un método de interfaz, podría usar una expresión Lambda, que condensa todo.Quería una solución que no creara ningún extra objeto (es decir, oyentes) que tendrían que ser recolectados como basura más tarde, y que no requiriera anidar un titular de vista dentro de una clase de adaptador.
En la
ViewHolder
claseprivate 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
ClickHandler
interfaz está definida, pero no inicializada aquí, por lo que no hay suposición en elonClick
método que alguna vez se haya inicializado.La
ClickHandler
interfaz se 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 está configurando potencialmente varias veces con el mismo valor exacto, pero esto es más barato que verificar nulos y ramificaciones, y no hay costo de memoria, solo una instrucción adicional.
Finalmente, cuando crea el adaptador, se ve obligado a pasar una
ClickHandler
instancia 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 aquí
adapter
es una variable miembro, no una variable localfuente
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 del botón por separado en el soporte de la vista. Lo clave que hace esta versión adaptada de la clase es devolver la vista del botón en la devolución de llamada onItemClick () cuando se toca, a diferencia del contenedor de elementos. A continuación, puede probar si la vista es un botón y realizar una acción diferente. Tenga en cuenta que un toque prolongado en el botón se interpreta como un toque prolongado 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<View> viewHierarchy = new ArrayList<View>(); // 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<View> 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 de 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) { } }); }
fuente
Debe devolver verdadero en el interior
onInterceptTouchEvent()
cuando maneja el evento de clic.fuente
Primero puede verificar si tiene entradas similares, si obtiene una colección con tamaño 0, inicie una nueva consulta para guardar.
O
forma más profesional y rápida. crear un disparador de nube (antes de guardar)
mira esta respuesta https://stackoverflow.com/a/35194514/1388852
fuente
Simplemente coloque un método de anulación llamado getItemId Consígalo haciendo clic derecho> generar> métodos de anulación> getItemId Coloque este método en la clase Adaptador
fuente