Single selection in RecyclerView

后端 未结 15 979
深忆病人
深忆病人 2020-11-22 15:15

I know there are no default selection methods in recyclerview class, But I have tried in following way,

public void onBindViewHolder(ViewHolder holder, final         


        
相关标签:
15条回答
  • 2020-11-22 16:01

    It's quite late, but I'm still posting it as it may help someone else.

    Use the code below as a reference to check a single item in 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) {
                        int copyOfLastCheckedPosition = lastCheckedPosition;
                lastCheckedPosition = getAdapterPosition();
                notifyItemChanged(copyOfLastCheckedPosition);
                notifyItemChanged(lastCheckedPosition);
    
                    }
                });
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 16:01

    Looks like there are two things at play here:

    (1) The views are reused, so the old listener is still present.

    (2) You are changing the data without notifying the adapter of the change.

    I will address each separately.

    (1) View reuse

    Basically, in onBindViewHolder you are given an already initialized ViewHolder, which already contains a view. That ViewHolder may or may not have been previously bound to some data!

    Note this bit of code right here:

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

    If the holder has been previously bound, then the checkbox already has a listener for when the checked state changes! That listener is being triggered at this point, which is what was causing your IllegalStateException.

    An easy solution would be to remove the listener before calling setChecked. An elegant solution would require more knowledge of your views - I encourage you to look for a nicer way of handling this.

    (2) Notify the adapter when data changes

    The listener in your code is changing the state of the data without notifying the adapter of any subsequent changes. I don't know how your views are working so this may or may not be an issue. Typically when the state of your data changes, you need to let the adapter know about it.

    RecyclerView.Adapter has many options to choose from, including notifyItemChanged, which tells it that a particular item has changed state. This might be good for your use

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 16:01

    I want to share the similar thing I have achieved, may be it will help someone. below code is from the application to select an address from a list of addresses that are displayed in cardview(cvAddress), so that on click of particular item(cardview) the imageView inside the item should set to different resource(select/unselect)

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

    I don't know this is best solution or not but this worked for me.

    0 讨论(0)
  • 2020-11-22 16:07

    This simple one worked for me

    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;
        }
    });
    
    0 讨论(0)
  • 2020-11-22 16:07

    This happens because RecyclerView, as the name suggests, does a good job at recycling its ViewHolders. This means that every ViewHolder, when it goes out of sight (actually, it takes a little more than going out of sight, but it makes sense to simplify it that way), it is recycled; this implies that the RecyclerView takes this ViewHolder that is already inflated and replaces its elements with the elements of another item in your data set.

    Now, what is going on here is that once you scroll down and your first, selected, ViewHolders go out of sight, they are being recycled and used for other positions of your data set. Once you go up again, the ViewHolders that were bound to the first 5 items are not necessarely the same, now.

    This is why you should keep an internal variable in your adapter that remembers the selection state of each item. This way, in the onBindViewHolder method, you can know if the item whose ViewHolder is currently being bound was selected or not, and modify a View accordingly, in this case your RadioButton's state (though I would suggest to use a CheckBox if you plan on selecting multiple items).

    If you want to learn more about RecyclerView and its inner workings, I invite you to check FancyAdapters, a project I started on GitHub. It is a collection of adapters that implement selection, drag&drop of elements and swipe to dismiss capabilities. Maybe by checking the code you can obtain a good understanding on how RecyclerView works.

    0 讨论(0)
  • 2020-11-22 16:07

    The following might be helpful for RecyclerView with Single Choice.

    Three steps to do that, 1) Declare a global integer variable,

    private int mSelectedItem = -1;
    

    2) in onBindViewHolder

     mRadio.setChecked(position == mSelectedItem);
    

    3) in onClickListener

    mSelectedItem = getAdapterPosition();
    notifyItemRangeChanged(0, mSingleCheckList.size());
    mAdapter.onItemHolderClick(SingleCheckViewHolder.this);
    
    0 讨论(0)
提交回复
热议问题