How to Highlight the Selected Item of Recycler View

How to properly highlight selected item on RecyclerView?

I wrote a base adapter class to automatically handle item selection with a RecyclerView. Just derive your adapter from it and use drawable state lists with state_selected, like you would do with a list view.

I have a Blog Post Here about it, but here is the code:

public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
// Start with first item selected
private int focusedItem = 0;

@Override
public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);

// Handle key up and key down and attempt to move selection
recyclerView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

// Return false if scrolled to the bounds and allow focus to move off the list
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
return tryMoveSelection(lm, 1);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
return tryMoveSelection(lm, -1);
}
}

return false;
}
});
}

private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
int tryFocusItem = focusedItem + direction;

// If still within valid bounds, move the selection, notify to redraw, and scroll
if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
notifyItemChanged(focusedItem);
focusedItem = tryFocusItem;
notifyItemChanged(focusedItem);
lm.scrollToPosition(focusedItem);
return true;
}

return false;
}

@Override
public void onBindViewHolder(VH viewHolder, int i) {
// Set selected state; use a state list drawable to style the view
viewHolder.itemView.setSelected(focusedItem == i);
}

public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);

// Handle item click and set the selection
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Redraw the old selection and the new
notifyItemChanged(focusedItem);
focusedItem = getLayoutPosition();
notifyItemChanged(focusedItem);
}
});
}
}
}

How to highlight selected item in RecyclerView

Just add this below line in bindView

holder.itemView.setBackgroundColor(Color.parseColor("#000000"));

will work for you.

if you want to highlight a selected item just do like below

set it global

int selectedPosition=-1;

inside onBindViewHolder-

public void onBindViewHolder(FiltersAdapter.ViewHolder holder, int position) {
if(selectedPosition==position)
holder.itemView.setBackgroundColor(Color.parseColor("#000000"));
else
holder.itemView.setBackgroundColor(Color.parseColor("#ffffff"));

holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedPosition=position;
notifyDataSetChanged();

}
});
}

===========================================================

Above code works fine as per old school methodology but here is the updated version which might help you:

Kotlin-

If you want to highlight a selected item just follow:

var selectedPosition = -1; //make it global

inside onBindViewHolder-

override fun onBindViewHolder(holder: FiltersAdapter.ViewHolder, position: Int) {
if (selectedPosition == position)
holder.itemView.setBackgroundColor(Color.parseColor("#000000"))
else
holder.itemView.setBackgroundColor(Color.parseColor("#ffffff"))
}

And create inner class ViewHolder which implements View.OnClickListener and override the onClick function.

override fun onClick(v: View) {
when (v.id) {
R.id.parent-> {
selectedPosition = adapterPosition;
notifyDataSetChanged();
}
}
}

Cheers.

Highlight selected item in RecyclerView WHILE clicking

The code you provided, I don't think it is right approach to just highlight the selected item. Your code will notify items unnecessary many times.

This can be done with a selector drawable. As there are many answers which guide, how to use a selector background.

But here is the problem

selectableItemBackground just works for the touch, not selection. So here is a hack.

We will set foreground as ?selectableItemBackground and a custom background selector on same view.

I created a sample which fulfill your requirement.

  • First make a background selector which sets transparent color if deactivated, and a color when activated.

activated_color_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true">
<color android:color="@color/colorPrimary"/>
</item>
<item>
<color android:color="@android:color/transparent"/>
</item>
</selector>
  • Use this selector as your item's background. Also set foreground as ?selectableItemBackground.

row.xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@drawable/activated_color_selector"
android:foreground="?selectableItemBackground">

// other views inside

</LinearLayout>
  • Your model class will hold selected boolean.

Model.java

public class BaseModel extends BaseObservable {
private boolean selected;
// setter getter
}
  • Attach model selected field to the View. Also invert selected field when clicked.

Inside RecyclerView adapter

@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
final BaseModel model = list.get(position);
holder.linearLayout.setActivated(model.isSelected());
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
model.setSelected(!model.isSelected());
notifyItemChanged(position);
}
});
}

All done!

Highlight selected item inside a RecyclerView

RecyclerView does not handle item selection or states like a ListView does. Instead you have to handle this manually in your view holder.

The first thing you can do is declare your item view as clickable, in your `ViewHolder constructor :

    public ViewHolder(View itemView) {
super(itemView);

// Make this view clickable
itemView.setClickable(true);

// ...
}

Then if you want to highlight the selection, you must keep track of the selected rows in your adapter (for example using a List of indices), and in your onBindViewHolder method :

@Override 
public void onBindViewHolder(ViewHolder viewHolder, int i) {
// mark the view as selected:
viewHolder.parentview.setSelected(mSelectedRows.contains(i));
}

As a side note you should set the onClickListener on your parent view in the onCreateViewHolder instead of the onBindViewHolder. The onBindViewHolder method will be called many times for the same view holder, and you'll perform more operations than necessary

Properly highlighting the selected item in a RecyclerView

From the code you've posted, your solution works perfectly. I've recreated it myself with a tiny app, and everything does exactly what it should.

public class MainActivity extends AppCompatActivity {

private int selectedPosition = 0;
private MyAdapter adapter = new MyAdapter();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

RecyclerView rv = findViewById(R.id.recycler);
rv.setAdapter(adapter);
}

private class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.itemview, parent, false);
return new MyViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.itemView.setBackgroundColor((position == selectedPosition) ? Color.GREEN : Color.WHITE);
}

@Override
public int getItemCount() {
return 1000;
}
}

private class MyViewHolder extends RecyclerView.ViewHolder {

public MyViewHolder(View itemView) {
super(itemView);

itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != -1) {
adapter.notifyItemChanged(selectedPosition);
selectedPosition = position;
adapter.notifyItemChanged(selectedPosition);
}
});
}
}
}

That means that something else in your app is causing your code to not work. My best guess is that your adapter's data is changing over time in a way that's causing selectedPosition to become out of sync. For example:

  • selectedPosition is 10
  • a new item is inserted into the list at position 0
  • the adapter is notified notifyItemInserted(0)

At this point, selectedPosition is still 10, but the logically "selected" item is at position 11. When your onClick() method runs, you'll notify the item at position 10 that it has been changed, but that won't change the background of the item at position 11, so now you'll have two green rows.

Highlight item in RecyclerView while Pressed

In the root layout of your recycler view item.xml, you can write android:foreground="?attr/selectableItemBackground". This will highlight recycler view item, when a click occurs.

For example, let`s say it is your recycler view item layout:

 <androidx.constraintlayout.widget.ConstraintLayout

android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground">

...

</androidx.constraintlayout.widget.ConstraintLayout>`

how to highlight the selected Item of Recycler View?

You can use a StateListDrawable to achieve the desired effect.

Example

Create a new Drawable resource file in your drawable directory with the following content:

selector_row.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Color when the row is selected -->
<item android:drawable="@android:color/darker_gray" android:state_pressed="false" android:state_selected="true" />
<!-- Standard background color -->
<item android:drawable="@android:color/white" android:state_selected="false" />
</selector>

Now simply use this StateListDrawable as the background in the row-layout of your RecyclerView

row_recyclerview.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_row">

<!-- row content -->

</RelativeLayout>

Now as soon as the onClick() method in your adapter is called you simply need to do the following:

// myBackground is the RelativeLayout root of your row
myBackground.setSelected(true);

The rows' background will have the color (in this case darker_gray) as long as you call myBackground.setSelected(false). Of course you should create a SparseBooleanArray for example in order to know which row is selected and which isn't since the rows will be reused when scrolling.

Edit: Remember selected items
The idea behind the SparseBooleanArray is to remember the items which are selected. Following a sample on how to use it:

public class MyImageAdapter extends RecyclerView.Adapter<MyImageAdapter.MyViewHolder> {

private SparseBooleanArray selectedItems;

// Other stuff [...]

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// Set the selected state of the row depending on the position
holder.myBackground.setSelected(selectedItems.get(position, false));
}

public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

@Override
public void onClick(View v) {
// Save the selected positions to the SparseBooleanArray
if (selectedItems.get(getAdapterPosition(), false)) {
selectedItems.delete(getAdapterPosition());
myBackground.setSelected(false);
}
else {
selectedItems.put(getAdapterPosition(), true);
myBackground.setSelected(true);
}
}
}
}

highlight the selected item of recyclerview

First of all, give an initial value to selectedItems variable. There is also no need to make it optional, thus eliminating the need to unwrap optional value with question mark (this selectedItems? becomes this selectedItems):

private val selectedItems = SparseBooleanArray()

If you want to set selected positions before the adapter is loaded make this variable one of the constructor arguments. With this primary constructor you can remove init block:

class  Custom_detail_Adapter(private val context: Context, private val dataList: List<Product_images_response>, private val selectedItems: SparseBooleanArray) :
RecyclerView.Adapter<Custom_detail_Adapter.CustomViewHolder>() {

inner class CustomViewHolder ...

This SparseBooleanArray constructor argument can be prepopulated with values that indicate which items are selected.



Related Topics



Leave a reply



Submit