studio recyclerview make how dependency create android android-recyclerview

android - make - Selección única en RecyclerView



recyclerview dependency (15)

Aún es demasiado tarde, publicarlo puede ayudar a otra persona. Use el código a continuación como referencia para verificar un solo elemento 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(); } }); } } }

Sé que no hay métodos de selección predeterminados en la clase recyclerview, pero lo 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); } } }); }

Al intentar este código, obtuve el resultado esperado, pero no del todo.

Explicaré esto con imágenes.

Por defecto, el primer elemento se selecciona de mi adaptador

Luego estoy tratando de seleccionar el segundo y luego el tercero, y luego el cuarto y finalmente el quinto,

Aquí el quinto solo debe seleccionarse, pero los cinco se seleccionan.

Si desplazo la lista al final y vuelvo al principio,

Obtuve lo que esperaba

¿Cómo puedo superar este problema? Y en algún momento, si desplazo la lista muy rápido, se selecciona otro elemento. ¿Cómo superar este problema también?

Actualizar

Mientras estoy tratando de 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)


Así es como se ve

Dentro de su adaptador

private int selectedPosition = -1;

Y enBindViewHolder

@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { if (selectedPosition == position) { holder.itemView.setSelected(true); //using selector drawable holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.white)); } else { holder.itemView.setSelected(false); holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.black)); } holder.itemView.setOnClickListener(v -> { if (selectedPosition >= 0) notifyItemChanged(selectedPosition); selectedPosition = holder.getAdapterPosition(); notifyItemChanged(selectedPosition); }); }

¡Eso es! Como puede ver, solo estoy notificando (actualizando) el elemento seleccionado anteriormente y el elemento recientemente seleccionado

My Drawable lo configuró como fondo para las vistas secundarias de vista de reciclaje

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="false" android:state_selected="true"> <shape android:shape="rectangle"> <solid android:color="@color/blue" /> </shape> </item>


Así es como se ve la clase Adapter:

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewHolder>{ Context context; ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList_; public int lastSelectedPosition=-1; public MyRecyclerViewAdapter(Context context, ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList) { this.context = context; this.routeDetailsFromFirestoreArrayList_ = routeDetailsFromFirestoreArrayList; } @NonNull @Override public MyRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { // LayoutInflater layoutInflater = LayoutInflater.from(mainActivity_.getBaseContext()); LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext()); View view = layoutInflater.inflate(R.layout.route_details, viewGroup, false); return new MyRecyclerViewHolder(view); } @Override public void onBindViewHolder(@NonNull final MyRecyclerViewHolder myRecyclerViewHolder, final int i) { /* This is the part where the appropriate checking and unchecking of radio button happens appropriately */ myRecyclerViewHolder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if(b) { if (lastSelectedPosition != -1) { /* Getting the reference to the previously checked radio button and then unchecking it.lastSelectedPosition has the index of the previously selected radioButton */ //RadioButton rb = (RadioButton) ((MainActivity) context).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton); RadioButton rb = (RadioButton) ((MainActivity) myRecyclerViewHolder.mRadioButton.getContext()).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton); rb.setChecked(false); } lastSelectedPosition = i; /* Checking the currently selected radio button */ myRecyclerViewHolder.mRadioButton.setChecked(true); } } }); } @Override public int getItemCount() { return routeDetailsFromFirestoreArrayList_.size(); } } // End of Adapter Class

Dentro de MainActivity.java llamamos al ctor de la clase Adapter de esta manera. El contexto pasado es de MainActivity para el adaptador ctor:

myRecyclerViewAdapter = new MyRecyclerViewAdapter(MainActivity.this, routeDetailsList);


Entonces, después de pasar tantos días sobre esto, esto es lo que se me ocurrió y funcionó para mí, y también es una buena práctica ,

  1. Cree una interfaz, asígnele el nombre algún oyente: SomeSelectedListener.
  2. Agregue un método que tome un entero: void onSelect (int position);
  3. Inicialice el oyente en el constructor del adaptador de reciclador como: a) primero declare globalmente como: oyente privado SomeSelectedListener b) luego en el constructor inicialice como: this.listener = oyente;
  4. Dentro de onClick () de la casilla de verificación dentro de onBindViewHolder (): actualice el método de la interfaz / escucha pasando la posición como: listener.onSelect (posición)
  5. En el modelo, agregue una variable para deseleccionar, digamos, mSelectedConstant e inicialícela allí a 0. Esto representa el estado predeterminado cuando no se selecciona nada.
  6. Agregue getter y setter para mSelectedConstant en el mismo modelo.
  7. Ahora, vaya a su fragmento / actividad e implemente la interfaz de escucha. Luego anule su método: onSelect (int position). Dentro de este método, recorra su lista que está pasando a su adaptador usando un bucle for y establezcaConstantConstant en 0 para todos:

código

@Override public void onTicketSelect(int position) { for (ListType listName : list) { listName.setmSelectedConstant(0); }

8. Fuera de esto, haga que la posición seleccionada sea constante 1:

código

list.get(position).setmSelectedConstant(1);

  1. Notifique este cambio llamando a: adapter.notifyDataSetChanged(); Inmediatamente después de esto.
  2. Último paso: regrese a su adaptador y actualice dentro de onBindViewHolder () después de que onClick () agregue el código para actualizar el estado de la casilla de verificación,

código

if (listVarInAdapter.get(position).getmSelectedConstant() == 1) { holder.checkIcon.setChecked(true); selectedTicketType = dataSetList.get(position);} else { commonHolder.checkCircularIcon.setChecked(false); }


Este simple me funcionó

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


Esto podría ayudar a aquellos que desean que funcione un solo botón de radio -> Radio Button RecycleView - Gist

Si las expresiones lambda no son compatibles, use esto en su lugar:

View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { notifyItemChanged(mSelectedItem); // to update last selected item. mSelectedItem = getAdapterPosition(); } };

Salud


Esto sucede porque RecyclerView, como su nombre lo indica, hace un buen trabajo reciclando sus ViewHolders. Esto significa que cada ViewHolder, cuando se pierde de vista (en realidad, toma un poco más que desaparecer, pero tiene sentido simplificarlo de esa manera), se recicla; esto implica que 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á sucediendo aquí es que una vez que se desplaza hacia abajo y su primer ViewHolders, seleccionado, desaparece, se recicla y se utiliza para otras posiciones de su conjunto de datos. Una vez que sube 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 recuerde el estado de selección de cada elemento . De esta forma, en el método onBindViewHolder , puede saber si el elemento cuyo ViewHolder se está vinculando actualmente se seleccionó o no, y modificar una Vista en consecuencia, en este caso el estado de su RadioButton (aunque sugeriría usar un CheckBox si planea seleccionando múltiples elementos).

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


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!


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


Parece que hay dos cosas en juego aquí:

(1) Las vistas se reutilizan, por lo que el antiguo oyente todavía está presente.

(2) Está cambiando los datos sin notificar el cambio al adaptador.

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 haberse vinculado previamente a algunos datos!

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

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

Si el titular se ha vinculado previamente, entonces la casilla de verificación ya tiene un escucha para cuando cambia el estado marcado. Ese oyente se está activando en este punto, que es lo que estaba causando su IllegalStateException .

Una solución fácil sería eliminar el oyente antes de llamar a setChecked . Una solución elegante requeriría un mayor conocimiento de sus puntos de vista. Le animo a que busque una mejor manera de manejar esto.

(2) Notificar al adaptador cuando los datos cambien

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 funcionan sus puntos de vista, por lo que esto puede o no ser un problema. Por lo general, cuando cambia el estado de sus datos, debe informar al adaptador al respecto.

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


Por favor intente esto ... Esto funciona para mí ...

En el adaptador, tome una matriz booleana dispersa.

SparseBooleanArray sparseBooleanArray;

En el constructor inicialice esto,

sparseBooleanArray=new SparseBooleanArray();

En titular de enlace agregar,

@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 un botón de opción en el archivo xml.

Entonces, cuando la vista del reciclador carga los elementos, en bindView Holder verifica si sparseBooleanArray contiene el valor "verdadero" correspondiente a su posición.

Si el valor devuelto es verdadero, configuramos la selección del botón de radio verdadero. De lo contrario, configuramos la selección como falsa. En onclickHolder, borré sparseArray y establecí el valor verdadero correspondiente a esa posición. Cuando llamo al conjunto de datos de notificación Vuelva a cambiarlo, llame al onBindViewHolder y la condición se verifica nuevamente. Esto hace que nuestra selección solo seleccione una radio en particular.


Quiero compartir lo similar que he logrado, puede ser que ayude a alguien. el siguiente código es de la aplicación para seleccionar una dirección de una lista de direcciones que se muestran en cardview(cvAddress) , de modo que al hacer clic en un item(cardview) particular item(cardview) la imageView dentro del elemento debe establecerse en un recurso diferente (seleccionar / deseleccionar)

@Override public void onBindViewHolder(final AddressHolder holder, final int position) { holderList.add(holder); holder.tvAddress.setText(addresses.get(position).getAddress()); holder.cvAddress.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { selectCurrItem(position); } }); } private void selectCurrItem(int position) { int size = holderList.size(); for(int i = 0; i<size; i++) { if(i==position) holderList.get(i).ivSelect.setImageResource(R.drawable.select); else holderList.get(i).ivSelect.setImageResource(R.drawable.unselect); } }

No sé si esta es la mejor solución o no, pero funcionó para mí.


solo usa mCheckedPosition guardar estado

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


OnCheckedChangeListener borrar OnCheckedChangeListener antes de configurar 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 .


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 { } } }; } }