Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select items in RecyclerView

Similar question have been asked, but i can't get any of them work.

What i want is to select item in RecyclerView, change the background of that item view, and store the position of item selected.

The main problem is that you have onCreateViewHolder (in adapter), onBindViewHolder (in adapter) and ViewHolder constructor and everybody is working with different methods. Now, i don't even know where to put onClickListener (in previous projects i've put it in ViewHolder), because people are suggesting the other two methods too.

My idea was to store each ViewHolder (or View) in list, so i can have reference to each row, and change the background from there. But that didn't work for me, because when i try to add to list of View(or ViewHolders), from any of three places (onCreateVH, onBindVH, VH class), my app crashes for some reason (null pointer ex).

Any suggestions? Where and how to implement it?

like image 643
JoKr Avatar asked Feb 17 '15 20:02

JoKr


People also ask

What is RecyclerView selection?

Pretty much every single app has a list of something they want to display. RecyclerView Selection is a library that will allow you to handle item selection a lot easier. It will help you handle motion events and touch events, and convert them into selection in the RecyclerView.

What is setHasStableIds?

setHasStableIds is an optimization hint that you can give to the recycler. You're telling it "when I provide a ViewHolder , its id is unique and will not change." It's very easy to write an Adapter that does otherwise - for example, linking the id to item position.


2 Answers

Make global variable to store position and handle click listener in ViewHolder. Onclick of item, change the global position value like

textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        globalPosition=getAdapterPosition();
        notifyDataSetChanged();
    }
});

then in onBindViewHolder

if(postion==globalPosition)
{
    //change color like
    textview.setTextColor(Color.RED);
}
else
{
    //revert back to regular color 
    textview.setTextColor(Color.WHITE);
}

with this code, the item you clicked get red colored and all other wiil be in white.

like image 108
Sadashiv Avatar answered Sep 19 '22 06:09

Sadashiv


First of all, you asked where to put the onClickListener - you should put it in the onBindViewHolder. You can also attach a onClickListener in the ViewHolder class of your item like so:

public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                }
            });
        }
    }

As to implementing a single selection behaviour, the answers given here work but calling onNotifyDatasetChanged is bad for performance if you have a lot of items as it rebinds all views from scratch each time.

The link given by denvercoder9 is a good tutorial to understand a lot of things about setting up RecyclerView, but I think it makes single selection behaviour complicated. I am going to answer comprehensively giving everything one might need to make it work. Here's how I implemented the single selection behaviour:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    final YourItemViewHolder itemViewHolder = (YourItemViewHolder) holder;
    //This will remember which one was selected
    itemViewHolder.getItemSelectionIndicator()
            .setSelected(position == mSelectedPosition);
        itemViewHolder.getItemWrapperView()
            .setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(mLogTag, "Tapped on " + position);
                    //This is the previous selection
                    notifyItemChanged(mSelectedPosition);
                    itemViewHolder.getItemSelectionIndicator().setSelected(true);
                    mSelectedPosition = position;
                    //This is the new selection
                    notifyItemChanged(position);
                }
            });

getItemSelectionIndicator() and getItemWrapperView() are methods inside the item's ViewHolder that return specific views from the item layout. A ItemWrapperView could be the top most Linear/RelativeLayout that wraps the entire item. Setting a click listener on it will ensure clicks work if user taps anywhere in the item's view.

ItemSelectionIndicator can be a Linear or RelativeLayout that has been set a state list drawable background. This means when it is set as selected, it shows a shape drawable automatically to indicate selection. When it is set as unselected, the drawable will be removed automatically. This is what the indicator view looks like:

<RelativeLayout
        android:id="@+id/selection_indicator"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/selection_indicator_state_list"/>

This is selection_indicator_state_list.xml in res/drawable:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Selected background -->
    <item android:state_selected="true"
          android:drawable="@drawable/item_selection_shape"/>
    <!-- Unselected background -->
    <item android:state_selected="false"
        android:drawable="@color/transparent"/>
</selector>

And this is item_selection_shape.xml in res/drawable:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    //22 is value of transparency, BDBDBD is the color
    <solid android:color="#22BDBDBD"/>
    <stroke android:width="2dp" android:color="@color/md_blue_600"/>
    <corners
        android:radius="3dp"/>
</shape>
like image 29
Ali Kazi Avatar answered Sep 20 '22 06:09

Ali Kazi