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.

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

¿Hay alguna manera de establecer un onClickListener en un RecyclerView ?

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> 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.