Je sais qu'il n'y a pas de méthodes de sélection par défaut dans la classe recyclerview, mais j'ai essayé de suivre,
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);
}
}
});
}
En essayant ce code, j'ai eu la sortie attendue, mais pas du tout.
Je vais expliquer cela avec des images.
Par défaut, le premier élément est sélectionné dans mon adaptateur.
Ensuite, j'essaie de sélectionner le 2e, puis le 3e, puis le 4e et enfin le 5e
Ici, 5ème seulement devrait être sélectionné, mais tous les cinq sont sélectionnés.
Si je fais défiler la liste vers le bas et reviens en haut,
J'ai eu ce que j'attendais,
Comment puis-je surmonter ce problème? Et un peu de temps Si je fais défiler la liste très rapidement, un autre élément est sélectionné. Comment surmonter ce problème aussi?
Mettre à jour
Pendant que j'essaie d'utiliser notifyDataSetChanged()
après fonts.get(position).setSelected(isChecked);
, je suis l'exception suivante,
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)
La solution au problème:
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);
}
});
}
... ...
}
J'espère que cette aide!
Il est trop tard pour le poster, cela peut aider quelqu'un d'autre Utilisez le code ci-dessous comme référence pour vérifier un élément dans 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();
}
});
}
}
}
On dirait qu'il y a deux choses en jeu ici:
Je vais aborder chacun séparément.
En gros, dans onBindViewHolder
, vous recevez une ViewHolder
déjà initialisée, qui contient déjà une vue. Cette ViewHolder
peut ou peut ne pas avoir déjà été liée à certaines données!
Notez ce morceau de code ici:
holder.checkBox.setChecked(fonts.get(position).isSelected());
Si le titulaire a déjà été lié, la case à cocher contient déjà un écouteur pour le moment où l'état coché change! Cet auditeur est en train de se déclencher à ce stade, ce qui était à l'origine de votre IllegalStateException
.
Une solution simple serait de supprimer l’auditeur avant d’appeler setChecked
. Une solution élégante nécessiterait une meilleure connaissance de vos points de vue - je vous encourage à rechercher un moyen plus agréable de gérer cela.
L'écouteur de votre code modifie l'état des données sans notifier à l'adaptateur les modifications ultérieures. Je ne sais pas comment vos points de vue fonctionnent, donc cela peut ne pas être un problème. Généralement, lorsque l'état de vos données change, vous devez en informer l'adaptateur.
RecyclerView.Adapter
propose de nombreuses options, dont notifyItemChanged
, qui indique qu'un élément particulier a changé d'état. Cela pourrait être bon pour votre usage
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);
}
utilisez simplement mCheckedPosition
save status
@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();
}
});
}
Vous devez effacer la OnCheckedChangeListener
avant de définir 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 cette façon, l'erreur Java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
ne sera pas déclenchée.
Cela pourrait aider ceux qui souhaitent utiliser un seul bouton radio -> Radio Button RecycleView - Gist
Si les expressions lambda ne sont pas prises en charge, utilisez plutôt ceci:
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
notifyItemChanged(mSelectedItem); // to update last selected item.
mSelectedItem = getAdapterPosition();
}
};
À votre santé
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 {
}
}
};
}
}
Cela se produit parce que RecyclerView, comme son nom l'indique, fait du bon travail sur recycling ses ViewHolders. Cela signifie que chaque ViewHolder, lorsqu'il disparaît (en réalité, il en faut un peu plus que disparaître, mais il est logique de le simplifier de cette façon), il est recyclé; cela implique que RecyclerView utilise ce ViewHolder déjà gonflé et remplace ses éléments par les éléments d'un autre élément de votre ensemble de données.
Maintenant, ce qui se passe ici, c'est qu'une fois que vous avez fait défiler l'écran vers le bas et que votre premier ViewHolders sélectionné est perdu de vue, il est recyclé et utilisé pour d'autres positions de votre ensemble de données. Une fois que vous remontez, les ViewHolders qui étaient liés aux 5 premiers éléments ne sont pas nécessairement les mêmes, maintenant.
C'est pourquoi vous devez conserver une variable interne dans votre adaptateur qui mémorise l'état de sélection de chaque élément. De cette façon, dans la méthode onBindViewHolder
, vous pouvez savoir si l'élément dont ViewHolder est actuellement lié est sélectionné ou non, et modifier une vue en conséquence, dans ce cas l'état de votre radioButton sélection de plusieurs éléments).
Si vous souhaitez en savoir plus sur RecyclerView et son fonctionnement interne, je vous invite à consulter FancyAdapters , un projet que j'ai commencé sur GitHub. C'est un ensemble d'adaptateurs implémentant selection, drag & drop d'éléments et swipe pour ignorer les fonctionnalités. En vérifiant le code, vous pourrez peut-être bien comprendre le fonctionnement de RecyclerView.
Voici à quoi ça ressemble
À l'intérieur de votre adaptateur
private int selectedPosition = -1;
Et onBindViewHolder
@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);
});
}
C'est tout! Comme vous pouvez le voir, je ne fais que Notification (mise à jour) élément sélectionné précédemment et élément récemment sélectionné
My Drawable le définit comme arrière-plan pour les vues enfant recyclerview
<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>
Ce simple a fonctionné pour moi
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;
}
});
Ce qui suit pourrait être utile pour RecyclerView avec Single Choice.
Trois étapes pour le faire, 1) Déclarer une variable entière globale,
private int mSelectedItem = -1;
2) en onBindViewHolder
mRadio.setChecked(position == mSelectedItem);
3) en onClickListener
mSelectedItem = getAdapterPosition();
notifyItemRangeChanged(0, mSingleCheckList.size());
mAdapter.onItemHolderClick(SingleCheckViewHolder.this);
Voici à quoi ressemble la classe 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
Dans MainActivity.Java, nous appelons le ctor de la classe Adapter comme ceci. Le contexte passé est de MainActivity à l'adaptateur ctor:
myRecyclerViewAdapter = new MyRecyclerViewAdapter(MainActivity.this, routeDetailsList);
Je souhaite partager la même chose que j’ai réalisée: cela aiderait peut-être .. .. ci-dessous le code provient de l’application pour sélectionner une adresse dans une liste d’adresses affichées dans cardview(cvAddress)
, de sorte qu’au clic de item(cardview)
la imageView
à l'intérieur de l'élément doit être défini sur une ressource différente (sélectionner/désélectionner)
@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);
}
}
Je ne sais pas si c'est la meilleure solution ou pas mais cela a fonctionné pour moi.
Donc, après y avoir passé tant de jours, voici ce que j’ai trouvé qui a fonctionné pour moi et qui est une bonne pratique aussi,
code
@Override
public void onTicketSelect(int position) {
for (ListType listName : list) {
listName.setmSelectedConstant(0);
}
8. En dehors de cela, réglez la position sélectionnée sur 1:
code
list.get(position).setmSelectedConstant(1);
adapter.notifyDataSetChanged();
immédiatement après.code
if (listVarInAdapter.get(position).getmSelectedConstant() == 1) {
holder.checkIcon.setChecked(true);
selectedTicketType = dataSetList.get(position);}
else {
commonHolder.checkCircularIcon.setChecked(false);
}
S'il te plaît, essaie ça… .. Ça marche pour moi….
Dans l'adaptateur, prenez un tableau booléen clairsemé.
SparseBooleanArray sparseBooleanArray;
En constructeur initialiser cela,
sparseBooleanArray=new SparseBooleanArray();
Dans le support de reliure ajouter,
@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 est un bouton radio dans un fichier XML.
Ainsi, lorsque la vue Recycler charge les éléments, dans bindView Holder, elle vérifie si sparseBooleanArray contient la valeur "true" correspondant à sa position.
Si la valeur renvoyée est true, nous définissons la sélection du bouton radio sur true.Else Nous définissons la sélection sur false . call notify datasetChangez-le à nouveau, appelez onBindViewHolder et la condition sera vérifiée à nouveau. Cela rend notre sélection pour ne sélectionner que la radio particulière.