Cómo obtener clics en RecyclerView (NO los niños)
¿Hay alguna manera de establecer un onClickListener
en un RecyclerView
?
Tengo un RecyclerView
con algunos niños en él, y el establecimiento de un OnClickListener
en el padre RecyclerView
. Sin embargo, el onClick
no se dispara cuando hago clic en esa vista. Vea el código de ejemplo abajo – queremos obtener clics en el padre, NO en los niños. En este escenario, no nos preocupan los clics en los elementos.
- RecyclerView se bloquea al actualizar en la parte superior
- RecyclerView no se desplaza hacia abajo
- Android FlowLayout como RecyclerView LayoutManager
- RecyclerView causa problemas al reciclar
- Cómo ampliar el espacio entre RecyclerView Item mientras se desplaza
He intentado hacer setFocusable(false)
, setClickable(false)
, y setOnClickListener(null)
en los niños en vano. En cualquier caso, no creo que los niños estén robando clics de los padres, porque cuando hago clic en el área donde no hay niños, los clics tampoco se registran.
package com.formagrid.hellotest; import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.Arrays; import java.util.List; public class HelloActivity extends Activity { private RecyclerView mRecyclerView; private RecyclerAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hello); mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mAdapter = new RecyclerAdapter(Arrays.asList("hi", "this", "is", "some", "text")); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.d("patricia", view.toString()); } }); } @Override protected void onDestroy() { super.onDestroy(); } @Override protected void onPause() { super.onPause(); } @Override protected void onResume() { super.onResume(); } public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.Holder> { public class Holder extends RecyclerView.ViewHolder { protected TextView textView; public Holder(TextView itemView) { super(itemView); this.textView = itemView; } } private List<String> contents; public RecyclerAdapter(List<String> contents) { this.contents = contents; } @Override public Holder onCreateViewHolder(ViewGroup parent, int viewType) { return new Holder(new TextView(parent.getContext())); } @Override public void onBindViewHolder(Holder holder, int position) { holder.textView.setText(contents.get(position)); } @Override public int getItemCount() { return contents.size(); } } }
- Cómo hacer un indicador de página para reciclaje horizontal
- Adjuntar TextView a RecyclerView
- La vista de Recycler no se desplaza correctamente después de implementar el desplazamiento para actualizar el diseño.
- ¿Cómo desplazar el RecyclerView de forma programática por unos píxeles específicos?
- RecyclerView expandible cardView
- ¿Cómo ajustar correctamente el valor de elevación a recyclerview?
- Los elementos de RecyclerView cambian de tamaño después de la actualización
- RecyclerView onCreateViewHolder Tipo de devolución Incompatibilidad con varias vistas personalizadas ViewHolders
¿Hay alguna manera de establecer un
onClickListener
en unRecyclerView
?
No. Es decir, puede establecer un OnClickListener
, pero RecyclerView
nunca lo llamará. RecyclerView
intercepta todos los eventos de toque, pero nunca llama a performClick()
, que es como View
invoca a su oyente.
Sin embargo, puede simular un OnClickListener
con un OnTouchListener
y un GestureDetector
. Para el GestureDetector
del GestureDetector
, podemos usar un SimpleOnGestureListener
, implementando solo el método onSingleTapUp()
.
class ClickListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { Toast.makeText(HelloActivity.this, "Clicked", 0).show(); return true; } };
Entonces sólo necesitamos alimentar el GestureDetector
el MotionEvent
s de un OnTouchListener
, y comprobar el retorno para decidir si consumir el evento, para no interferir con desplazamiento, arrastrar, etc.
final GestureDetector detector = new GestureDetector(HelloActivity.this, new ClickListener()); mRecyclerView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if(detector.onTouchEvent(event)) { return true; } return false; } } );
Tenga en cuenta que la solución anterior funciona con prácticamente sólo un "sencillo" RecyclerView
, como el descrito y dado en la pregunta. Si está utilizando un manejo de artículos más complicado, como arrastrar y soltar o deslizar, tendremos que manejar nuestra detección de gestos un poco más arriba en la cadena de eventos táctiles.
Para ello, podemos subclasificar RecyclerView
y realizar la detección en el método dispatchTouchEvent()
. Si se detecta un solo toque, simplemente llamamos performClick()
, que disparará OnClickListener
RecyclerView
.
public class ClickableRecyclerView extends RecyclerView { private final GestureDetectorCompat detector; public ClickableRecyclerView(Context context) { this(context, null); } public ClickableRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); detector = new GestureDetectorCompat(context, new ClickListener()); } private class ClickListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { performClick(); return true; } }; @Override public boolean dispatchTouchEvent(MotionEvent e) { detector.onTouchEvent(e); return super.dispatchTouchEvent(e); } }
Sólo use esta subclase en lugar de su RecyclerView
regular, y establecer un OnClickListener
en él como lo haría normalmente. Pueden ser necesarios constructores adicionales, dependiendo de cómo esté instanciando esto.
Intente algo como envolver su recyclerview dentro de un relativelayout o linearlayout o FrameLayout y entonces fijar el oyente del tecleo al relativelayout / linearlayout / framelayout.
<RelativeLayout android:id="@+id/myLayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView> </RelativeLayout>
Ahora :
RelativeLayout rl = (RelativeLayout) findViewById(R.id.myLayout); rl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.d("patricia", view.toString()); } });
Un evento de clic en RecycleView? Intente de esta manera:
//set android:descendantFocusability="blocksDescendants" on parent layout //then setOnClickListener <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" **android:descendantFocusability="blocksDescendants"** android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </LinearLayout>