Eén selectie in RecyclerView

Ik weet dat er geen standaard selectiemethoden zijn in de klasse RecyclerView, maar ik heb het op de volgende manier geprobeerd:

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

Terwijl ik deze code probeerde, kreeg ik de verwachte output, maar niet helemaal.

Ik zal dit uitleggen met afbeeldingen.

Standaard wordt het eerste item van mijn adapter geselecteerd.

Vervolgens selecteer ik de 2e, dan de 3e, dan de 4e en tenslotte de 5e.

Hier moet alleen de 5e worden geselecteerd, maar ze worden alle vijf geselecteerd.

Als ik door de lijst naar beneden scroll en weer naar boven ga, krijg ik wat ik verwacht.

Hoe kan ik dit probleem oplossen? En soms, als ik heel snel door de lijst blader, wordt een ander item geselecteerd. Hoe kan ik dit probleem ook oplossen?

Terwijl ik probeerde notifyDataSetChanged()te gebruiken na fonts.get(position).setSelected(isChecked);, kreeg ik de volgende uitzondering:

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)

Antwoord 1, autoriteit 100%

De oplossing voor het probleem:

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

Antwoord 2, autoriteit 83%

Het is vrij laat, maar ik post het nog omdat het iemand anders kan helpen.

Gebruik de onderstaande code als referentie om een ​​enkel item in RecyclerView te controleren:

/**
 * 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) {
                    int copyOfLastCheckedPosition = lastCheckedPosition;
            lastCheckedPosition = getAdapterPosition();
            notifyItemChanged(copyOfLastCheckedPosition);
            notifyItemChanged(lastCheckedPosition);
                }
            });
        }
    }
}

Antwoord 3, autoriteit 12%

Zo ziet het eruit:

Enkelvoudige selectie recyclerbekijk de beste manier

In uw adapter:

private int selectedPosition = -1;

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

dat is het!

Zoals je kunt zien, meld ik (bijwerken)alleen het vorige geselecteerde item en het nieuw geselecteerde item.

Mijn Drawable stelt het in als achtergrond voor recyclerview onderliggende weergaven:

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

Antwoord 4, autoriteit 11%

Standaardwaarde:

private int mCheckedPostion = -1;

Gebruik gewoon mCheckedPositionom de status op te slaan:

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

Antwoord 5, autoriteit 11%

Het lijkt erop dat hier twee dingen spelen:

(1) De views worden hergebruikt, dus de oude luisteraar is nog steeds aanwezig.

(2) U wijzigt de gegevens zonder de adapter van de wijziging op de hoogte te stellen.

Ik zal elk afzonderlijk behandelen.

(1) Hergebruik bekijken

Kortom, in onBindViewHolderkrijgt u een reeds geïnitialiseerde ViewHolder, die al een weergave bevat. Die ViewHolderis al dan niet eerder aan bepaalde gegevens gebonden!

Noteer dit stukje code hier:

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

Als de houder eerder is gebonden, heeft het selectievakje al een luisteraar voor wanneer de aangevinkte status verandert! Die luisteraar wordt op dit punt getriggerd, wat uw IllegalStateExceptionveroorzaakte.

Een gemakkelijke oplossing zou zijn om de luisteraar te verwijderen voordat u setCheckedaanroept. Een elegante oplossing vereist meer kennis van uw standpunten – ik moedig u aan om te zoeken naar een leukere manier om hiermee om te gaan.

(2) Waarschuw de adapter wanneer gegevens veranderen

De luisteraar in uw code verandert de status van de gegevens zonder de adapter op de hoogte te stellen van eventuele latere wijzigingen. Ik weet niet hoe uw standpunten werken, dus dit kan wel of niet een probleem zijn. Wanneer de status van uw gegevens verandert, moet u dit doorgaans aan de adapter laten weten.

RecyclerView.Adapterheeft veel opties om uit te kiezen, waaronder notifyItemChanged, wat aangeeft dat een bepaald item van status is veranderd. Dit is misschien goed voor uw gebruik:

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

Antwoord 6, autoriteit 9%

U moet de OnCheckedChangeListenerwissen voordat u setChecked()instelt:

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

Op deze manier wordt de java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling-fout berekent.


Antwoord 7, autoriteit 7%

Dit kan helpen voor degenen die willen dat een enkele radioknop werkt –>
Radioknop RecycleView – Gist

Als lambda-expressies niet worden ondersteund, gebruik dan dit:

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

Antwoord 8

Dit gebeurt omdat RecyclerView, zoals de naam al doet vermoeden, goed werk levert bij het recyclenvan zijn ViewHolders. Dit betekent dat elke ViewHolder, wanneer hij uit het zicht verdwijnt (eigenlijk is er iets meer nodig dan uit het zicht verdwijnen, maar het is logisch om het op die manier te vereenvoudigen), wordt hij gerecycled; dit houdt in dat de RecyclerView deze ViewHolder neemt die al is opgeblazen en zijn elementen vervangt door de elementen van een ander item in uw dataset.

Wat hier aan de hand is, is dat als je eenmaal naar beneden scrolt en je eerste, geselecteerde, ViewHolders uit het zicht verdwijnen, ze worden gerecycled en gebruikt voor andere posities van je dataset. Als je weer omhoog gaat, zijn de ViewHolders die aan de eerste 5 items waren gebonden nu niet noodzakelijk hetzelfde.

Daarom moet u een interne variabele in uw adapter bewaren die de selectiestatus van elk item onthoudt. Op deze manier kunt u in de onBindViewHolder-methode weten of het item waarvan de ViewHolder momenteel wordt gebonden, is geselecteerd of niet, en een weergave dienovereenkomstig wijzigen, in dit geval de status van uw RadioButton (hoewel ik zou willen voorstellen om gebruik een CheckBox als u van plan bent meerdere items te selecteren).

Als je meer wilt weten over RecyclerView en zijn innerlijke werking, nodig ik je uit om FancyAdapterste bekijken, een project dat ik op GitHub ben begonnen. Het is een verzameling adapters die selectie, slepen en neerzettenvan elementen en vegenimplementeren om mogelijkheden te sluiten. Misschien kun je door de code te controleren een goed beeld krijgen van hoe RecyclerView werkt.


Antwoord 9

Deze eenvoudige werkte voor mij

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 != checked_rb) {
            lastCheckedRB.setChecked(false);
        }
        //store the clicked radiobutton
        lastCheckedRB = checked_rb;
    }
});

Antwoord 10

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

Antwoord 11

Zo ziet de klasse Adapter eruit:

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

Binnen MainActivity.java noemen we de ctor van de Adapter-klasse op deze manier. De doorgegeven context is van MainActivity aan de Adapter ctor:

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

Antwoord 12

public class LastTransactionAdapter extends RecyclerView.Adapter<LastTransactionAdapter.MyViewHolder> {
private Context context;
private List<PPCTransaction> ppcTransactionList;
private static int checkedPosition = -1;
public LastTransactionAdapter(Context context, List<PPCTransaction> ppcTransactionList) {
    this.context = context;
    this.ppcTransactionList = ppcTransactionList;
}
@NonNull
@Override
public LastTransactionAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.last_transaction_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull LastTransactionAdapter.MyViewHolder holder, int position) {
    holder.setLastTransaction(ppcTransactionList.get(position), position);
}
@Override
public int getItemCount() {
    return ppcTransactionList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
    private LinearLayout linTransactionItem;
    private RadioButton radioButton;
    private TextView tvDate, tvMerchantName, tvAmount, tvStatus;
    public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        linTransactionItem = itemView.findViewById(R.id.linTransactionItem);
        tvDate = itemView.findViewById(R.id.tvDate);
        tvMerchantName = itemView.findViewById(R.id.tvMerchantName);
        tvAmount = itemView.findViewById(R.id.tvAmount);
        tvStatus = itemView.findViewById(R.id.tvStatus);
        radioButton = itemView.findViewById(R.id.radioButton);
    }
    public void setLastTransaction(PPCTransaction ppcTransaction, int position) {
        tvDate.setText(ppcTransaction.getmDate());
        tvMerchantName.setText(ppcTransaction.getmMerchantName());
        tvAmount.setText(ppcTransaction.getmAmount());
        tvStatus.setText(ppcTransaction.getmStatus());
        if (checkedPosition == -1) {
            radioButton.setChecked(false);
        } else {
            if (checkedPosition == getAdapterPosition()) {
                radioButton.setChecked(true);
            } else {
                radioButton.setChecked(false);
            }
        }
        linTransactionItem.setOnClickListener(v -> {
            radioButton.setChecked(true);
            if (checkedPosition != getAdapterPosition()) {
                notifyItemChanged(checkedPosition);
                checkedPosition = getAdapterPosition();
            }
        });
    }
}

Antwoord 13

Ik heb een oplossing die je selectie opslaat wanneer je een recyclerlijst opent.

var mSelectedItem = -1
// store saved selection
fun setSelection(position: Int) {
    mSelectedItem = position
}
override fun onBindViewHolder(holder: GroupHolder, position: Int) {
    holder.bind(dataList[position])
    holder.radioButton.isChecked = position == mSelectedItem
}
inner class GroupHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val radioButton: RadioButton = itemView.rbValue
    fun bind(data: Data) = with(itemView) {
        radioButton.text = data.name
        val clickListener = View.OnClickListener {
            mSelectedItem = bindingAdapterPosition
            notifyDataSetChanged()
            setSelection(mSelectedItem)
        }
        radioButton.setOnClickListener(clickListener)
    }
}

Antwoord 14

Maak het niet te ingewikkeld:

SharedPreferences sharedPreferences = getSharedPreferences("appdetails", MODE_PRIVATE);
String selection = sharedPreferences.getString("selection", null);
public void onBindViewHolder(ViewHolder holder, final int position) {
    String name = fonts.get(position).getName();
    holder.mTextView.setText(name);
    if(selection.equals(name)) {
        holder.checkBox.setChecked(false);  //
        holder.checkBox.setChecked(true);
    }
    holder.checkbox.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            SharedPreferences sharedPreferences = getSharedPreferences("appdetails", MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString("selection", name);
            r.setChecked(false);
            editor.apply();
            holder.checkBox.setChecked(true);
        }
    });
}

Other episodes