Selección única en RecyclerView

Sé que no hay métodos de selección por defecto en la clase recyclerview, pero he intentado de la siguiente manera,

public void onBindViewHolder(ViewHolder holder, final int position) { holder.mTextView.setText(fonts.get(position).getName()); holder.checkBox.setChecked(fonts.get(position).isSelected()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) { for (int i = 0; i < fonts.size(); i++) { fonts.get(i).setSelected(false); } fonts.get(position).setSelected(isChecked); } } }); } 

Mientras intentaba este código, conseguí la salida esperada, pero completamente no.

Te explicaré con imágenes.

De forma predeterminada, el primer elemento se selecciona desde mi adaptador

Introduzca aquí la descripción de la imagen

Entonces estoy tratando de seleccionar 2 º y luego 3 º, y luego 4 º y finalmente 5 º uno,

Introduzca aquí la descripción de la imagen

Aquí el 5to solamente debe ser seleccionado, pero los cinco están consiguiendo seleccionados.

Si desplazo la lista a la parte inferior y vuelvo otra vez a la parte superior,

Introduzca aquí la descripción de la imagen

Tengo lo que esperaba,

¿Cómo puedo superar este problema? Y algún tiempo Si hago que la lista avance muy rápidamente, se selecciona otro elemento. ¿Cómo superar este problema también?

Actualizar

Mientras intento usar notifyDataSetChanged() después de fonts.get(position).setSelected(isChecked); Tengo la siguiente excepción,

 java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462) at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982) at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493) at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338) at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111) 

Cualquier solución será muy apreciada.

Gracias.

La solución para el problema:

 public class yourRecyclerViewAdapter extends RecyclerView.Adapter<yourRecyclerViewAdapter.yourViewHolder> { private static CheckBox lastChecked = null; private static int lastCheckedPos = 0; ... ... public void onBindViewHolder(ViewHolder holder, final int position) { holder.mTextView.setText(fonts.get(position).getName()); holder.checkBox.setChecked(fonts.get(position).isSelected()); holder.checkBox.setTag(new Integer(position)); //for default check in first item if(position == 0 && fonts.get(0).isSelected() && holder.checkBox.isChecked()) { lastChecked = holder.checkBox; lastCheckedPos = 0; } holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CheckBox cb = (CheckBox)v; int clickedPos = ((Integer)cb.getTag()).intValue(); if(cb.isChecked) { if(lastChecked != null) { lastChecked.setChecked(false); fonts.get(lastCheckedPos).setSelected(false); } lastChecked = cb; lastCheckedPos = clickedPos; } else lastChecked = null; fonts.get(clickedPos).setSelected(cb.isChecked); } }); } ... ... } 

¡Espero que esto ayude!

Su demasiado tarde todavía publicarla puede ayudar a otra persona Utilice debajo del código como referencia para comprobar el solo artículo en RecyclerView

 /** * Created by subrahmanyam on 28-01-2016, 04:02 PM. */ public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> { private final String[] list; private int lastCheckedPosition = -1; public SampleAdapter(String[] list) { this.list = list; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = View.inflate(parent.getContext(), R.layout.sample_layout, null); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.choiceName.setText(list[position]); holder.radioButton.setChecked(position == lastCheckedPosition); } @Override public int getItemCount() { return list.length; } public class ViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.choice_name) TextView choiceName; @Bind(R.id.choice_select) RadioButton radioButton; public ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); radioButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { lastCheckedPosition = getAdapterPosition(); //because of this blinking problem occurs so //i have a suggestion to add notifyDataSetChanged(); // notifyItemRangeChanged(0, list.length);//blink list problem notifyDataSetChanged(); } }); } } } 

Parece que hay dos cosas en juego aquí:

(1) Las vistas se reutilizan, por lo que el oyente de edad sigue estando presente.

(2) Usted está cambiando los datos sin notificar al adaptador del cambio.

Me ocuparé de cada uno por separado.

(1) Ver reutilización

Básicamente, en onBindViewHolder le da un ViewHolder ya inicializado, que ya contiene una vista. ¡Ese ViewHolder puede o no haber estado vinculado previamente a algunos datos!

Tenga en cuenta este bit de código aquí:

 holder.checkBox.setChecked(fonts.get(position).isSelected()); 

Si el titular ha sido previamente vinculado, entonces la casilla de verificación ya tiene un oyente para cuando cambia el estado marcado! Ese oyente está siendo activado en este momento, que es lo que causaba su IllegalStateException .

Una solución fácil sería quitar el oyente antes de llamar a setChecked . Una solución elegante requeriría más conocimiento de sus opiniones – le animo a buscar una manera más agradable de manejar esto.

(2) Notificar al adaptador cuando los datos cambian

El oyente en su código está cambiando el estado de los datos sin notificar al adaptador de cualquier cambio posterior. No sé cómo sus opiniones están trabajando así que esto puede o no puede ser un problema. Normalmente, cuando cambia el estado de los datos, debe informar al adaptador de ello.

RecyclerView.Adapter tiene muchas opciones para elegir, incluyendo notifyItemChanged , que le dice que un elemento en particular ha cambiado de estado. Esto puede ser bueno para su uso

 if(isChecked) { for (int i = 0; i < fonts.size(); i++) { if (i == position) continue; Font f = fonts.get(i); if (f.isSelected()) { f.setSelected(false); notifyItemChanged(i); // Tell the adapter this item is updated } } fonts.get(position).setSelected(isChecked); notifyItemChanged(position); } 

Solo usa el estado de guardar mCheckedPosition

 @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.checkBox.setChecked(position == mCheckedPostion); holder.checkBox.setOnClickListener(v -> { if (position == mCheckedPostion) { holder.checkBox.setChecked(false); mCheckedPostion = -1; } else { mCheckedPostion = position; notifyDataSetChanged(); } }); } 

Es necesario borrar el OnCheckedChangeListener antes de establecer setChecked() :

 @Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.mRadioButton.setOnCheckedChangeListener(null); holder.mRadioButton.setChecked(position == mCheckedPosition); holder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCheckedPosition = position; notifyDataSetChanged(); } }); } 

De esta manera no activará java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling error de java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling .

Esto podría ayudar para aquellos que quieren una sola radio Botón Para trabajar -> Radio Button RecycleView – Gist

Si no se admite la expresión lamda Utilice esta opción

  View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { mSelectedItem = getAdapterPosition(); notifyItemRangeChanged(0, mItems.size()); } }; 

Aclamaciones

Esto sucede porque RecyclerView, como su nombre indica, hace un buen trabajo al reciclar sus ViewHolders. Esto significa que cada ViewHolder, cuando se pierde de vista (en realidad, se necesita un poco más que salir de la vista, pero tiene sentido simplificarlo de esa manera), se recicla; Esto implica que el RecyclerView toma este ViewHolder que ya está inflado y reemplaza sus elementos con los elementos de otro elemento en su conjunto de datos.

Ahora, lo que está pasando aquí es que una vez que se desplaza hacia abajo y su primer, seleccionado, ViewHolders salir de la vista, se están reciclando y se utiliza para otras posiciones de su conjunto de datos. Una vez que suba de nuevo, los ViewHolders que estaban vinculados a los primeros 5 elementos no son necesariamente los mismos, ahora .

Es por eso que debe mantener una variable interna en su adaptador que recuerda el estado de selección de cada elemento . De esta manera, en el método onBindViewHolder , puede saber si el elemento cuyo ViewHolder está enlazado actualmente o no, y modificar una vista en consecuencia, en este caso el estado de RadioButton (aunque yo sugeriría usar un CheckBox si planea Seleccionando varios elementos).

Si desea obtener más información sobre RecyclerView y su funcionamiento interno, le invito a revisar FancyAdapters , un proyecto que inicié en GitHub. Es una colección de adaptadores que implementan selección , arrastrar y soltar de elementos y deslizar para descartar capacidades. Tal vez al revisar el código puede obtener una buena comprensión de cómo funciona RecyclerView.

  public class GetStudentAdapter extends RecyclerView.Adapter<GetStudentAdapter.MyViewHolder> { private List<GetStudentModel> getStudentList; Context context; RecyclerView recyclerView; public class MyViewHolder extends RecyclerView.ViewHolder { TextView textStudentName; RadioButton rbSelect; public MyViewHolder(View view) { super(view); textStudentName = (TextView) view.findViewById(R.id.textStudentName); rbSelect = (RadioButton) view.findViewById(R.id.rbSelect); } } public GetStudentAdapter(Context context, RecyclerView recyclerView, List<GetStudentModel> getStudentList) { this.getStudentList = getStudentList; this.recyclerView = recyclerView; this.context = context; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.select_student_list_item, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.textStudentName.setText(getStudentList.get(position).getName()); holder.rbSelect.setChecked(getStudentList.get(position).isSelected()); holder.rbSelect.setTag(position); // This line is important. holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position)); } @Override public int getItemCount() { return getStudentList.size(); } private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { if (checkBox.isChecked()) { for (int i = 0; i < getStudentList.size(); i++) { getStudentList.get(i).setSelected(false); } getStudentList.get(position).setSelected(checkBox.isChecked()); notifyDataSetChanged(); } else { } } }; } } 

Este sencillo funcionó para mí

 private RadioButton lastCheckedRB = null; ... @Override public void onBindViewHolder(final CoachListViewHolder holder, final int position) { holder.priceRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { RadioButton checked_rb = (RadioButton) group.findViewById(checkedId); if (lastCheckedRB != null) { lastCheckedRB.setChecked(false); } //store the clicked radiobutton lastCheckedRB = checked_rb; } }); 

Por favor, prueba esto … Esto funciona para mí ..

En el adaptador, tome una matriz booleana escasa.

 SparseBooleanArray sparseBooleanArray; 

En constructor inicializar esto,

  sparseBooleanArray=new SparseBooleanArray(); 

En el soporte de fijación,

 @Override public void onBindViewHolder(DispositionViewHolder holder, final int position) { holder.tv_disposition.setText(dispList.get(position).getName()); if(sparseBooleanArray.get(position,false)) { holder.rd_disp.setChecked(true); } else { holder.rd_disp.setChecked(false); } setClickListner(holder,position); } private void setClickListner(final DispositionViewHolder holder, final int position) { holder.rd_disp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sparseBooleanArray.clear(); sparseBooleanArray.put(position, true); notifyDataSetChanged(); } }); } 

Rd_disp es el botón de radio en el archivo xml.

Por lo tanto, cuando la vista del reciclador cargue los elementos, en bindView Holder verificará si el sparseBooleanArray contiene el valor "true" correspondiendo a su posición.

Si el valor devuelto es true entonces ponemos la selección del botón de radio true.Else ponemos la selección falsa. En onclickHolder he limpiado el sparseArray y establecer el valor verdadero correspondiente a esa posición. Cuando llamo notify datasetChange, vuelve a llamar al onBindViewHolder y la condición se comprueba de nuevo. Esto hace que nuestra selección sólo seleccione la radio en particular.

Lo siguiente podría ser útil para RecyclerView con Single Choice .

Tres pasos para hacer eso, 1) Declarar una variable entera global,

 private int mSelectedItem = -1; 

2) en onBindViewHolder

  mRadio.setChecked(position == mSelectedItem); 

3) en onClickListener

 mSelectedItem = getAdapterPosition(); notifyItemRangeChanged(0, mSingleCheckList.size()); mAdapter.onItemHolderClick(SingleCheckViewHolder.this); 
  • Cómo cambiar el diseño del elemento recyclerview desde fuera?
  • RecyclerView cortar el último artículo
  • Elemento de centrado de Android en RecyclerView
  • Centrar verticalmente el elemento seleccionado en RecyclerView
  • Reemplazar ListView con RecyclerView
  • RecyclerView desplazado UP / DOWN oyente
  • Cómo redimensionar el visor en un RecyclerView
  • Agregue una vista dinámicamente al elemento del RecyclerView
  • Compruebe si RecyclerView es desplazable
  • ¿Cómo puedo hacer que RecyclerView actualice su diseño?
  • ¿Cómo implementar multi-select en RecyclerView?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.