Changing background color of selected item in recyclerview

How to change the background color of only selected view in my recycle view example?only the background color of clicked itemview needs to be changed. Only one selected item must be displayed with background color change at a time and the rest needs to be as before selecting. here is my code :

MainActivity

public class MainActivity extends AppCompatActivity {
RecyclerView rv1;
    private  final String android_versions[]={
                "Donut",
                "Eclair",
                "Froyo",
                "Gingerbread",
                "Honeycomb",
                "Ice Cream Sandwich",
                "Jelly Bean",
                "KitKat",
                "Lollipop",
                "Marshmallow"
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private  void initViews(){
        rv1=(RecyclerView)findViewById(R.id.recyclerView1);
        rv1.setHasFixedSize(true);
        RecyclerView.LayoutManager layoutManager=new LinearLayoutManager(getApplicationContext());
        rv1.setLayoutManager(layoutManager);

        RecyclerDataAdapter rda=new RecyclerDataAdapter(rv1,getApplicationContext(),android_versions);
        rv1.setAdapter(rda);
    }
}

RecyclerDataadapter

public class RecyclerDataAdapter extends RecyclerView.Adapter<RecyclerDataAdapter.ViewHolder> {

private String android_versionnames[];
    private Context context1;

    private RecyclerView mRecyclerView;


    public RecyclerDataAdapter(RecyclerView recylcerView,Context context,String android_versionnames[]){
        this.android_versionnames=android_versionnames;
        this.context1=context;
mRecyclerView=recylcerView;
        setHasStableIds(true);
        System.out.println("Inside dataadapter,Android names : \n ");
        for(int i=0;i<android_versionnames.length;i++){
            System.out.println("\n"+android_versionnames[i]);
        }
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.tv1.setText(android_versionnames[position]);
    }


    @Override
    public int getItemCount() {
        return android_versionnames.length;
    }


    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView tv1;
        LinearLayout row_linearlayout;
        RecyclerView rv2;

        public ViewHolder(final View itemView) {
            super(itemView);
            tv1=(TextView)itemView.findViewById(R.id.txtView1);
            row_linearlayout=(LinearLayout)itemView.findViewById(R.id.row_linrLayout);
            rv2=(RecyclerView)itemView.findViewById(R.id.recyclerView1);
            /*itemView.setBackgroundColor(0x00000000);//to transparent*/

        }
    }
}

Solution 1:

Finally, I got the answer.

public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.tv1.setText(android_versionnames[position]);

        holder.row_linearlayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                row_index=position;
                notifyDataSetChanged();
            }
        });
        if(row_index==position){
            holder.row_linearlayout.setBackgroundColor(Color.parseColor("#567845"));
            holder.tv1.setTextColor(Color.parseColor("#ffffff"));
        }
        else
        {
            holder.row_linearlayout.setBackgroundColor(Color.parseColor("#ffffff"));
            holder.tv1.setTextColor(Color.parseColor("#000000"));
        }

    }

here 'row_index' is set as '-1' initially

public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView tv1;
        LinearLayout row_linearlayout;
        RecyclerView rv2;

        public ViewHolder(final View itemView) {
            super(itemView);
            tv1=(TextView)itemView.findViewById(R.id.txtView1);
            row_linearlayout=(LinearLayout)itemView.findViewById(R.id.row_linrLayout);
            rv2=(RecyclerView)itemView.findViewById(R.id.recyclerView1);
        }
    }

Solution 2:

A really simple way to achieve this would be:

//instance variable
List<View>itemViewList = new ArrayList<>();

//OnCreateViewHolderMethod
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    
    final View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row, parent, false);
    final MyViewHolder myViewHolder = new MyViewHolder(itemView);

    itemViewList.add(itemView); //to add all the 'list row item' views

    //Set on click listener for each item view
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            for(View tempItemView : itemViewList) {
                /** navigate through all the itemViews and change color
                of selected view to colorSelected and rest of the views to colorDefault **/
                if(itemViewList.get(myViewHolder.getAdapterPosition()) == tempItemView) {
                    tempItemView.setBackgroundResource(R.color.colorSelected);
                }
                else{
                    tempItemView.setBackgroundResource(R.color.colorDefault);
                }
            }
        }
    });
    return myViewHolder;
}

UPDATE

The method above may ruin some default attributes of the itemView, in my case, i was using CardView, and the corner radius of the card was getting removed on click.

Better solution:

//instance variable
List<CardView>cardViewList = new ArrayList<>();

public class MyViewHolder extends RecyclerView.ViewHolder {
        CardView cardView; //THIS IS MY ROOT VIEW
        ...

        public MyViewHolder(View view) {
            super(view);
            cardView = view.findViewById(R.id.row_item_card);
            ...
        }
}

@Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        final OurLocationObject locationObject = locationsList.get(position);
        ...

        cardViewList.add(holder.cardView); //add all the cards to this list

        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //All card color is set to colorDefault
                for(CardView cardView : cardViewList){
                cardView.setCardBackgroundColor(context.getResources().getColor(R.color.colorDefault));
                }
                //The selected card is set to colorSelected
                holder.cardView.setCardBackgroundColor(context.getResources().getColor(R.color.colorSelected));
            }
        });
}

UPDATE 2 - IMPORTANT

onBindViewHolder method is called multiple times, and also every time the user scrolls the view out of sight and back in sight! This will cause the same view to be added to the list multiple times which may cause problems and minor delay in code executions!

To fix this, change

cardViewList.add(holder.cardView);

to

if (!cardViewList.contains(holder.cardView)) {
    cardViewList.add(holder.cardView);
}

Solution 3:

I can suggest this solution, which I used in my app. I've placed this code of onTouchListener in my ViewHolder class's constructor. itemView is constructor's argument. Be sure to use return false on this method because this need for working OnClickListener

itemView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN)
        {
            v.setBackgroundColor(Color.parseColor("#f0f0f0"));
        }
        if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL)
        {
            v.setBackgroundColor(Color.TRANSPARENT);
        }
        return false;
    }
});

Solution 4:

Create Drawable file in Drawable foloder

<item android:drawable="@color/SelectedColor" android:state_pressed="true"></item>
<item android:drawable="@color/SelectedColor" android:state_selected="true"></item>
<item android:drawable="@color/DefultColor"></item>

And in xml file

android:background="@drawable/Drawable file"

In RecyclerView onBindViewHolder

holder.button.setSelected(holder.button.isSelected()?true:false);

Like toggle button

Solution 5:

I was able to change the selected view color like this. I think this is the SIMPLE WAY (because you don't have to create instance of layouts and variables.

MAKE SURE YOU DONT GIVE ANY BACKGROUND COLOR INSIDE YOUR RECYCLER VIEW's TAG.

holder.itemView.setBackgroundColor(Color.parseColor("#8DFFFFFF"));

onBindViewHolder() method is given below

@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {

        holder.item_1.setText(list_items.get(position).item_1);
        holder.item_2.setText(list_items.get(position).item_2);
        holder.select_cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                if (isChecked){

                    holder.itemView.setBackgroundColor(Color.parseColor("#8DFFFFFF"));
                }else {

                    holder.itemView.setBackgroundColor(Color.parseColor("#FFFFFF"));
                }
            }
        });
}