Cómo resaltar el elemento seleccionado de Recycler View?

Tengo una vista de reciclaje con las imágenes cargadas desde el almacenamiento interno. Quiero destacar el elemento seleccionado al hacer clic en él. Intenté muchas cosas pero no funcionaba. En realidad lo que necesito es cuando hago clic en cualquier elemento de Recycler View que el elemento debe ir en Mi ArrayList y también debe ser resaltado y de nuevo cuando hago clic o decir unselect debe volver a ser normal. Aquí está mi código:

public class Images extends Fragment { private List<ImageHolder> imageList; Cursor imageCursor; RecyclerView recyclerView; MyImageAdapter adapter; ActionButton clickButton; List<String> listofImages; List<Integer> pos; int columnIndex; StringBuilder stringBuilder; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootlayout = inflater.inflate(R.layout.image, container, false); listofImages=new ArrayList<String>(); pos=new ArrayList<Integer>(); stringBuilder=new StringBuilder(); ContentResolver imageResolver = getActivity().getContentResolver(); Uri imageUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; String projection[]={MediaStore.Images.Thumbnails._ID,MediaStore.Images.Media.TITLE}; imageCursor = getActivity().managedQuery(imageUri, projection, null, null, null); clickButton= (ActionButton) rootlayout.findViewById(R.id.action_button); recyclerView = (RecyclerView) rootlayout.findViewById(R.id.recycler_view_image); adapter = new MyImageAdapter(getActivity(), getImageList()); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(),recyclerView,new RecyclerTouchListener.ClickListener() { @Override public void onClick(View view, int position) { TextView tv= (TextView) view.findViewById(R.id.list_text_all); int flag=0; String[] projection = {MediaStore.Images.Media.DATA}; imageCursor = getActivity().managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null); columnIndex = imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); imageCursor.moveToPosition(position); // Get image filename String imagePath = imageCursor.getString(columnIndex); if (listofImages.contains(imagePath)){ Log.d("Contains Test","Yes"); listofImages.remove(imagePath); pos.remove(position); } else { listofImages.add(imagePath); pos.add(position); Log.d("Contains Test","No"); } String s=listofImages.size()+" "+imagePath; Log.d("Inserted",s); } @Override public void onLongClick(View view, int position) {} })); clickButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (int i=0;i<listofImages.size();i++){ stringBuilder.append(listofImages.get(i)+"\n"); } Toast.makeText(getActivity(),stringBuilder,Toast.LENGTH_LONG).show(); } }); return rootlayout; } public List<ImageHolder> getImageList() { imageList=new ArrayList<ImageHolder>(); if(imageCursor!=null && imageCursor.moveToFirst()){ int titleColumn = imageCursor.getColumnIndex (android.provider.MediaStore.Images.Media.TITLE); int idColumn = imageCursor.getColumnIndex (android.provider.MediaStore.Images.Media._ID); do { ImageHolder img=new ImageHolder(); img.id=imageCursor.getLong(idColumn); img.title=imageCursor.getString(titleColumn); img.iconid= imageCursor.getInt(idColumn); imageList.add(img); } while (imageCursor.moveToNext()); } return imageList; } } 

Esta es mi clase de adaptador:

 public class MyImageAdapter extends RecyclerView.Adapter<MyImageAdapter.MyViewHolder> { Context context; private LayoutInflater inflater; List<ImageHolder> data= Collections.emptyList(); private ClickListener clickListener; int width,height; public MyImageAdapter(Context context, List<ImageHolder> data1) { inflater = LayoutInflater.from(context); this.data=data1; this.context=context; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.all_row, parent, false); MyViewHolder holder=new MyViewHolder(view); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { try{ ImageHolder current=data.get(position); holder.title.setText(current.title); Log.d("Imageid:"+current.iconid,""); Uri IMAGE_URI = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + current.iconid); Bitmap bitmap = Bitmap.createScaledBitmap(decodeUri(IMAGE_URI), 200, 200, true); holder.img.setImageBitmap(bitmap); } catch(Exception e){} } public void deleteRecyclerData(int position){ data.remove(position); notifyItemRemoved(position); } private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException { BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream( context.getContentResolver().openInputStream(selectedImage), null, o); final int REQUIRED_SIZE = 100; int width_tmp = o.outWidth, height_tmp = o.outHeight; int scale = 1; while (true) { if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) { break; } width_tmp /= 2; height_tmp /= 2; scale *= 2; } BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; return BitmapFactory.decodeStream( context.getContentResolver().openInputStream(selectedImage), null, o2); } @Override public int getItemCount() { return data.size(); } public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ TextView title; // TextView artist; ImageView img; CheckBox checkBox; public MyViewHolder(View itemView) { super(itemView); title= (TextView) itemView.findViewById(R.id.list_text_all); img= (ImageView) itemView.findViewById(R.id.list_image_all); img.setOnClickListener(this); } @Override public void onClick(View v) {} } public interface ClickListener{ public void itemClicked(View view, int position); } } 

Puede utilizar un StateListDrawable para lograr el efecto deseado.

Ejemplo

Cree un nuevo archivo de recursos Drawable en su directorio dibujable con el siguiente contenido:

Selector_row.xml

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Color when the row is selected --> <item android:drawable="@android:color/darker_gray" android:state_pressed="false" android:state_selected="true" /> <!-- Standard background color --> <item android:drawable="@android:color/white" android:state_selected="false" /> </selector> 

Ahora simplemente use este StateListDrawable como el fondo en la fila-disposición de su RecyclerView

Row_recyclerview.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/selector_row"> <!-- row content --> </RelativeLayout> 

Ahora, tan pronto como se llama al método onClick() en su adaptador, simplemente tiene que hacer lo siguiente:

 // myBackground is the RelativeLayout root of your row myBackground.setSelected(true); 

El fondo de las filas tendrá el color (en este caso, más oscuro ), siempre y cuando llame a myBackground.setSelected(false) . Por supuesto, debe crear un SparseBooleanArray por ejemplo para saber qué fila está seleccionada y cuál no es ya que las filas se reutilizarán al desplazarse.

Editar: Recuerde los elementos seleccionados
La idea detrás de la SparseBooleanArray es recordar los elementos que se seleccionan. Después de una muestra sobre cómo usarlo:

 public class MyImageAdapter extends RecyclerView.Adapter<MyImageAdapter.MyViewHolder> { private SparseBooleanArray selectedItems; // Other stuff [...] @Override public void onBindViewHolder(MyViewHolder holder, int position) { // Set the selected state of the row depending on the position holder.myBackground.setSelected(selectedItems.get(position, false)); } public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ @Override public void onClick(View v) { // Save the selected positions to the SparseBooleanArray if (selectedItems.get(getAdapterPosition(), false)) { selectedItems.delete(pos); myBackground.setSelected(false); } else { selectedItems.put(getAdapterPosition(), true); myBackground.setSelected(true); } } } } 

No hay selector en RecyclerView como ListView y GridView, pero intenta por debajo de lo que funcionó para mí

Crear un selector dibujable como a continuación

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <solid android:color="@color/blue" /> </shape> </item> <item android:state_pressed="false"> <shape> <solid android:color="@android:color/transparent" /> </shape> </item> </selector> 

A continuación, establezca esta disposición como fondo de su diseño de fila RecyclerView como

 android:background="@drawable/selector" 

Puede agregar esto a su row_item.xml

 android:clickable="true" android:background="?attr/selectableItemBackground" 

Por ejemplo:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:background="?attr/selectableItemBackground" <!-- row content --> 

Si la versión de Android es Lolipop o superior, el selector viene con ondulación. Y un punto culminante para la otra versión. Espero eso ayude

He intentado varias maneras por horas y aquí están las dos soluciones que salí con. Ambas soluciones suponen que tengo mi RecyclerView declarado como sigue:

Activity.xml

 <android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_height="match_parent" android:layout_width="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> 

Nada especial aquí, sólo una declaración regular de RecyclerView . Ahora vamos a ver los otros archivos, comenzando con la solución más fácil y viable.

Primera solución (sólo XML)

Layout / item.xml

Los dos atributos importantes aquí en el grupo de ViewGroup raíz del ViewGroup son de background y se clickable .

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@drawable/selector_item" android:clickable="true" android:gravity="center" android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="horizontal" android:padding="16dp"> ... </LinearLayout> 

Drawable / selector_item.xml

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/background_item_pressed" android:state_pressed="true" /> <item android:drawable="@drawable/background_item" /> </selector> 

Segunda solución (XML + Java)

Item.xml

Aquí no hay ningún atributo de background ni de clickable .

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:gravity="center" android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="horizontal" android:padding="16dp"> ... </LinearLayout> 

Adapter.java

 public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> { public class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); itemView.setOnTouchListener(itemTouchListener); } } ... private View.OnTouchListener itemTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: v.setBackgroundResource(R.drawable.background_item_event_pressed); break; case MotionEvent.ACTION_CANCEL: // CANCEL triggers when you press the view for too long // It prevents UP to trigger which makes the 'pressed' background permanent which isn't what we want case MotionEvent.ACTION_OUTSIDE: // OUTSIDE triggers when the user's finger moves out of the view case MotionEvent.ACTION_UP: v.setBackgroundResource(R.drawable.background_item_event); break; default: break; } return true; } }; ... } 

Recomiendo encarecidamente el uso de la primera solución, ya que es más fácil de mantener y más potente, ya que también le permite añadir efectos ripple (en el drawable/background_item... archivos XML), que creo que no es posible con la solución 2.

Si logras usar un patrón de patrón observable como Otto o AndroidRx, puedes seguir cómo resaltar el fondo como se explicó anteriormente y para cada itemView de ViewHolder puedes suscribirte a la observable y anular la suscripción cuando se separe de tu recyclerview como hice aquí :

https://github.com/juanmendez/jm_android_dev/blob/master/01.fragments/06.fragments_with_rx/app/src/main/java/info/juanmendez/android/recyclerview/ui/listing/recyclerview/CountryHolder.java# L49

Por cierto para una demostración rápida mi itemView está usando linearLayout, así que era fácil fijar el color de fondo como amarillo.

Introduzca aquí la descripción de la imagen

Esta solución es más de un aspecto interactivo como el tableView en IOS. Destacará y luego destapará las celdas.

 @Override public void onBindViewHolder(Cell holder, final int position) { if(requests != null) { holder.setView(requests.get(position), context); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { Logs.print("In OnClickListener", position + " selected"); } }); holder.itemView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Logs.print("In Touch Handler", "A press has started"); v.setSelected(true); break; case MotionEvent.ACTION_UP: Logs.print("In Touch Handler", "A press has been completed"); v.setSelected(false); break; case MotionEvent.ACTION_CANCEL: Logs.print("In Touch Handler", "gesture aborted"); v.setSelected(false); break; } return true; } }); } } 
  • Firebase UI RecyclerView onClick
  • Vista de reciclador - cambia el tamaño de la vista de elemento mientras se desplaza (para efecto de carrusel)
  • Recyclerviews en ViewPager
  • TableLayout Vs Recycler Ver?
  • Sdk mínimo para recycleview y pros / contras de reemplazar listas con ella
  • RecyclerView RecyclerViewDataObserver no estaba registrado
  • Rendimiento extremadamente Laggy RecyclerView
  • Las animaciones de elementos predictivos de RecyclerView no funcionan (APPEARING)
  • Deslizar un elemento a la vez
  • Crear un RecyclerView con desplazamiento horizontal y vertical
  • RecyclerView se desplaza hacia arriba en notifyDataSetChanged en la pantalla de chat
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.