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*/
}
}
}
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);
}
}
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);
}
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;
}
});
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
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"));
}
}
});
}
What I did to achieve this was actually taking a static variable to store the last clicked position of the item in the RecyclerView and then notify the adapter to update the layout at the position on the last clicked position i.e. notifyItemChanged(lastClickedPosition) whenever a new position is clicked. Calling notifyDataSetChanged() on the whole layout is very costly and unfeasible so doing this for only one position is much better.
Here's the code for this:
public class RecyclerDataAdapter extends RecyclerView.Adapter<RecyclerDataAdapter.ViewHolder> {
private String android_versionnames[];
private Context mContext;
private static lastClickedPosition = -1; // Variable to store the last clicked item position
public RecyclerDataAdapter(Context context,String android_versionnames[]){
this.android_versionnames = android_versionnames;
this.mContext = context;
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).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]);
holder.itemView.setBackgroundColor(mContext.getResources().
getColor(R.color.cardview_light_background));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundColor(mContext.getResources().
getColor(R.color.dark_background));
if (lastClickedPosition != -1)
notifyItemChanged(lastClickedPosition);
lastClickedPosition = position;
}
});
}
#Override
public int getItemCount() {
return android_versionnames.length;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tv1;
public ViewHolder(final View itemView) {
super(itemView);
tv1=(TextView)itemView.findViewById(R.id.txtView1);
}
}
}
So we will be actually updating only the intended item and not re-running unnecessary updates to the items which have not even been changed.
If you use kotlin, it's really simple.
In your RecyclerAdapter class
userV.invalidateRecycler()
holder.card_User.setCardBackgroundColor(Color.parseColor("#3eb1ae").withAlpha(60))
In your fragment or Activity
override fun invalidateRecycler() {
if (v1.recyclerCompanies.childCount > 0) {
v1.recyclerCompanies.childrenRecursiveSequence().iterator().forEach { card ->
if (card is CardView) {
card.setCardBackgroundColor(Color.WHITE)
}
}
}
}
There is a very simple solution to this, you don't have to work in the adapter. To change the background of a clicked item in the RecyclerView you need to catch the click in the adapter using an iterface:
interface ItemClickListener {
fun onItemClickListener(item: Item, position: Int)
}
When we click we will get the item and the items position. In our bind function in the adapter we will set the on click listener:
container.setOnClickListener {
onClickListener.onItemClickListener(item, position)
}
In your activity you will then implement this interface:
class MainActivity : AppCompatActivity(), ItemAdapter.ItemClickListener {
Next we need to implement the background changing logic on item click. The logic is this: when the user clicks on an item, we check if the background on the clicked item is white (the item is not previously clicked) and if this condition is true, we change the background on all of the items in the RecyclerView to white (to invalidate previously clicked and marked items if there are any) and then change the background color of the clicked item to teal to mark it. And if the background of the clicked item is teal (which means the user clicks again on the same previously marked item), we change the background color on all of the items to white. First we will need to get our item background color as a ColorDrawable. We will use an iterator function to go through all of the items (children) of the RecyclerView and forEach() function to change the background on everyone of them. This method will look like this:
override fun onItemClickListener(item: Item, position: Int) {
val itemBackground: ColorDrawable =
binding.recycler[position].background as ColorDrawable
if (itemBackground.color == ContextCompat.getColor(this, R.color.white)) {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
binding.recycler[position].setBackgroundColor(
ContextCompat.getColor(this, R.color.teal_200)
)
} else {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
}
}
So now you change the background on item click, if you click the same item, you will change the background back to what it was before.
My solution:
public static class SimpleItemRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> {
private final MainActivity mParentActivity;
private final List<DummyContent.DummyItem> mValues;
private final boolean mTwoPane;
private static int lastClickedPosition=-1;
**private static View viewOld=null;**
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
DummyContent.DummyItem item = (DummyContent.DummyItem) view.getTag();
if (mTwoPane) {
Bundle arguments = new Bundle();
arguments.putString(ItemDetailFragment.ARG_ITEM_ID, item.id);
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
mParentActivity.getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment)
.commit();
} else {
Context context = view.getContext();
Intent intent = new Intent(context, ItemDetailActivity.class);
intent.putExtra(ItemDetailFragment.ARG_ITEM_ID, item.id);
context.startActivity(intent);
}
**view.setBackgroundColor(mParentActivity.getResources().getColor(R.color.SelectedColor));
if(viewOld!=null)
viewOld.setBackgroundColor(mParentActivity.getResources().getColor(R.color.DefaultColor));
viewOld=view;**
}
};
viewOld is null at the beginning, then points to the last selected view.
With onClick you change the background of the selected view and redefine the background of the penultimate view selected.
Simple and functional.
In your adapter class make Integer variable as index and assign it to "0" (if you want to select 1st item by default, if not assign "-1").Then on your onBindViewHolder method,
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.texttitle.setText(listTitle.get(position));
holder.itemView.setTag(listTitle.get(position));
holder.texttitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = position;
notifyDataSetChanged();
}
});
if (index == position)
holder.texttitle.setTextColor(mContext.getResources().getColor(R.color.selectedColor));
else
holder.texttitle.setTextColor(mContext.getResources().getColor(R.color.unSelectedColor));
}
Thats it and you are good to go.in If condition true section place your selected color or what ever you need, and else section place unselected color or what ever.
Calling Notifydatasetchanged May be expensive when you need to change one item We can overcome by saving the old position and call notifyItemChanged
var old_postion=-1
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) {
notifyItemChanged(old_position)
//After item change happens set the old_postion as current position
old_position=position
}
});
if(old_position==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"));
}
}
Create a selector into Drawable folder:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#color/blue" />
</shape>
</item>
<item android:state_pressed="false">
<shape>
<solid android:color="#android:color/transparent" />
</shape>
</item>
</selector>
Add the property into your xml (where you declare the RecyclerView):
android:background="#drawable/selector"
Add click listener for item view in .onBindViewHolder() of your RecyclerView's adapter. get currently selected position and change color by .setBackground() for previously selected and current item
Most Simpler Way From My Side is to Add a variable in adapterPage as last Clicked Position.
in onBindViewHolder paste this code which checks for last stored position matched with loading positions
Constants is the class where i declare my global variables
if(Constants.LAST_SELECTED_POSITION_SINGLE_PRODUCT == position) {
//change the view background here
holder.colorVariantThumb.setBackgroundResource(R.drawable.selected_background);
}
//on view click you store the position value and notifyItemRangeChanged will
// call the onBindViewHolder and will check the condition
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view){
Constants.LAST_SELECTED_POSITION_SINGLE_PRODUCT=position;
notifyItemRangeChanged(0, mColorVariants.size());
}
});
I managed to do this from my Activity where i'm setting my Rv and not from the adapter
If someone need to do something similar here's the code
In this case the color changes on a logClick
#Override
public void onLongClick(View view, int position) {
Toast.makeText(UltimasConsultasActivity.this, "Item agregado a la lista de mails",
Toast.LENGTH_SHORT).show();
sendMultipleMails.setVisibility(View.VISIBLE);
valueEmail.setVisibility(View.VISIBLE);
itemsSeleccionados.setVisibility(View.VISIBLE);
listaEmails.add(superListItems.get(position));
listaItems ="";
NameOfyourRecyclerInActivity.findViewHolderForAdapterPosition(position).NameOfYourViewInTheViewholder.setBackgroundColor((Color.parseColor("#336F0D")));
for(int itemsSelect = 0; itemsSelect <= listaEmails.size() -1; itemsSelect++){
listaItems += "*"+listaEmails.get(itemsSelect).getDescripcion() + "\n";
}
itemsSeleccionados.setText("Items Seleccionados : "+ "\n" + listaItems);
}
}));
My Solution
With my solution I'm not using notifyDataSetChanged(), because annoying whenever item is clicked, all the items from list got refreshed. To tackle this problem, I used notifyItemChanged(position); This will only change the selected item.
Below I have added the code of my omBindViewHolder.
private int previousPosition = -1;
private SingleViewItemBinding previousView;
#Override
public void onBindViewHolder(#NonNull final ItemViewHolder holder, final int position) {
holder.viewBinding.setItem(itemList.get(position));
holder.viewBinding.rlContainerMain.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
clickRecyclerView.clickRecyclerItem(position, 0);
previousPosition = position;
notifyItemChanged(position);
if(previousView != null){
previousView.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorGrayLight));
}
}
});
if(position == previousPosition){
previousView = holder.viewBinding;
holder.viewBinding.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorPrimary));
}
else {
holder.viewBinding.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorGrayLight));
}
}
in the Kotlin you can do this simply:
all you need is to create a static variable like this:
companion object {
var last_position = 0
}
then in your onBindViewHolder add this code:
holder.item.setOnClickListener{
holder.item.setBackgroundResource(R.drawable.selected_item)
notifyItemChanged(last_position)
last_position=position
}
which item is the child of recyclerView which you want to change its background after clicking on it.
I made this implementation in kotlin I thing is not very efficient but works
ivIsSelected is a ImageView that represent in my case a check mark
var selectedItems = mutableListOf<Int>(-1)
override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
// holder.setData(ContactViewModel, position) // I'm passing this to the ViewHolder
holder.itemView.setBackgroundColor(Color.WHITE)
holder.itemView.ivIsSelected.visibility = INVISIBLE
selectedItems.forEach {
if (it == position) {
holder.itemView.setBackgroundColor(Color.argb(45, 0, 255, 43))
holder.itemView.ivIsSelected.visibility = VISIBLE
}
}
holder.itemView.setOnClickListener { it ->
it.setBackgroundColor(Color.BLUE)
selectedItems.add(position)
selectedItems.forEach { selectedItem -> // this forEach is required to refresh all the list
notifyItemChanged(selectedItem)
}
}
}
A faster and simpler way is saving the previous View element selected, so you don't have to use notifyDataSetChanged() or notifyItemChanged(position).
First, add an instance variable inside your Adapter (RecyclerDataAdapter):
View prevElement;
Then, inside your function onClick() (or in my case the lambda function version) you insert this:
holder.itemView.setOnClickListener(v -> {
// CODE TO INSERT
if (prevElement != null)
prevElement.setBackgroundColor(Color.TRANSPARENT);
v.setBackgroundColor(R.color.selected);
prevElement = v;
// DO SOMETHING
...
});
As you can see, the first thing done is checking if the prevElement is not null (an element was clicked before this), so we change its background color to Color.TRANSPARENT (even if it's the same element clicked twice). Then, we set the background color of the View element clicked (v) is changed to R.color.selected. Finally set the element clicked to the prevElement variable, so it can be modified in the next click action.
The response from #Sudhanshu Vohra above was the best in my case and much simpler.
I did minor changes to handle the new selection and previous selection to adjust the display.
I modified it as:
//Handle selected item and previous selection
if (lastSelectedIndex != -1) {
notifyItemChanged(lastSelectedIndex);
}
notifyItemChanged(bindingAdapterPosition);
lastSelectedIndex = bindingAdapterPosition;
Now I refresh only two items, rather than the entire list and it works like a charm. Thank you.
I got it like this
public void onClick(View v){
v.findViewById(R.id.textView).setBackgroundColor(R.drawable.selector_row);
}
Thanks
я не знаю на сколько это поможет но я типа так сделал:) в адаптере #Override public void onBindViewHolder(#NonNull NoteViewHolder holder, int position) { holder.bind(sortedList.get(position)); holder.itemView.setBackgroundResource(R.drawable.bacground_button); }
I would make that when I press a button in my recyclerView that will change it background color so it will be visible that is pressed but I'm not very into android so I can't get how to do it.
I've tried some guides found online but most of them didn't worked for me, like in this guide the buttons were not getting their color and were just white.
I would do somethink like that
screen of my activity
Here is my recyclerView Adapter code:
public class RecyclerViewMenu extends RecyclerView.Adapter<RecyclerViewMenu.MenuViewHolder> {
private ArrayList<MenuConstructor> menuConstructors;
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
onItemClickListener = listener;
}
#NonNull
#Override
public MenuViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.menucard,parent,false);
return new MenuViewHolder(v, onItemClickListener);
}
RecyclerViewMenu(ArrayList<MenuConstructor> menuList){
menuConstructors = menuList;
}
#Override
public void onBindViewHolder(#NonNull final MenuViewHolder holder, final int position) {
MenuConstructor currentItem = menuConstructors.get(position);
holder.textView.setText(currentItem.getDesk());
holder.itemView.setBackgroundColor(Color.parseColor(currentItem.getSfondoColor()));
holder.textView.setTextColor(Color.parseColor(currentItem.getFontColor()));
}
#Override
public int getItemCount() {
return menuConstructors.size();
}
public static class MenuViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
MenuViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
textView = itemView.findViewById(R.id.ButtonName);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
}
Actually if I press on of the button's it's load on the upped RecyclerView different items like if "CICCIO" is pressed upper it will load some stuff if another button is pressed it will load other items and if I use quick learner answer that's what happens
Gif of activity that change items by pressing buttons
Here after using quick learner solution it's change color but not anymore the items
Try this if you want to change the color of selected item in recycler view
public class AdapterClass extends RecyclerView.Adapter<AdapterClass.ViewHolder> {
private int selected_position = -1;
#Override
public void onBindViewHolder(PlacesLocationAdapter.ViewHolder holder, final int position) {
if (selected_position == position) {
// do your stuff here like
//Change selected item background color
} else {
// do your stuff here like
//Change unselected item background color
}
// rest of the code here
}
}
and this MenuViewHolder class should be like this
public static class MenuViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
MenuViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
textView = itemView.findViewById(R.id.ButtonName);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
selected_position = position;
notifyDataSetChanged();
}
}
}
});
}
To change the background of a clicked item in the RecyclerView you need to catch the click in the adapter using an iterface:
interface ItemClickListener {
fun onItemClickListener(item: Item, position: Int)
}
When we click we will get the item and the items position. In our bind function in the adapter we will set the on click listener on your button:
yourButton.setOnClickListener {
onClickListener.onItemClickListener(item, position)
}
In your activity you will then implement this interface:
class MainActivity : AppCompatActivity(), ItemAdapter.ItemClickListener {
Next we need to implement the background changing logic on item click. The logic is this: when the user clicks on an item, we check if the background on the clicked item is white (the item is not previously clicked) and if this condition is true, we change the background on all of the items in the RecyclerView to white (to invalidate previously clicked and marked items if there are any) and then change the background color of the clicked item to teal to mark it. And if the background of the clicked item is teal (which means the user clicks again on the same previously marked item), we change the background color on all of the items to white. First we will need to get our item background color as a ColorDrawable. We will use an iterator function to go through all of the items (children) of the RecyclerView and forEach() function to change the background on everyone of them. This method will look like this:
override fun onItemClickListener(item: Item, position: Int) {
val itemBackground: ColorDrawable =
binding.recycler[position].background as ColorDrawable
if (itemBackground.color == ContextCompat.getColor(this, R.color.white)) {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
binding.recycler[position].setBackgroundColor(
ContextCompat.getColor(this, R.color.teal_200)
)
} else {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
}
}
So now you change the background on item click, if you click the same item, you will change the background back to what it was before.
Do one thing. Put color resource id in MenuConstructor this class. And set color from this class to itemView in onBind. And whenever you click button so change color in MenuConstructor this class on perticular position.
After Changing the color in MenuConstructor this class, Just notifyDataSetChanged() call this method.
In onBind,
holder.textView.setTag(position);
holder.textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (Integer) v.getTag();
//change color in this
notifiDataSetChanged();
}
});
Recycler view item color change repeating after scrolling.
I used to change color at a particular position of the Recyclerview list. When scrolling occurs another item at the bottom has the same change. And it is repeating in pattern. How to resolve this?
holder.recycle_Listing.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemListener.connectionClicked(v,position, itemtype);
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}
});
The recycler view recycles the view in OnBindViewHolder.So when items are clicked it gets reflected in some other positions.To solve this. create a global SparseBooleanArray to store the clicked position.
private final SparseBooleanArray array=new SparseBooleanArray();
Then inside final viewholder add the clickListener and onClick store the position of the clicked item.
public class ViewHolder extends RecyclerView.ViewHolder {
public YOURVIEW view;
public ViewHolder(View v) {
super(v);
view = (YOURVIEW) v.findViewById(R.id.YOURVIEWID);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
array.put(getAdapterPosition(),true);
notifyDataSetChanged();
}
});
}
}
And in inside OnBindViewHolder,
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
if(array.get(position)){
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}else{
holder.mainlayout.setBackgroundColor(UNSELECTEDCOLOR);
}
}
I think you can set your background color in void onBindViewHolder(VH holder, int position); such as
List<Integer> selectedPosition = new ArrayList(yourDataSize);
void onBindViewHolder(VH holder, int position){
if(selectedPosition.get(position) == 1){
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}else {
holder.mainlayout.setBackgroundColor(normalColor);
}
//when the item clicked
selectedPosition.add(position,1);
}
That function returns relative position not absolute so when screen is scrolled position is replaced with a new value. use position from your list for the desired result.
It is due to recycling of viewholder of recycler view. Try this
It worked for me.
public ListViewHolder(View itemView) {
super(itemView);
this.setIsRecyclable(false);
}
try to implement this method in your adapter class, may be it's solve your problem
#Override
public void onViewRecycled(ViewHolderProduct holder) {
super.onViewRecycled(holder);
holder.mainlayout.removeAllViews();
}
Just saved every item keys in an array and that selected array also passed through my Adapter class. Even simple colour change works fine in this format. Here the code is changed as per my the requirement.
#Override
public void onBindViewHolder(final ICConversationHomeAddConnectionsAdapter.ViewHolder holder, final int position) {
JsonObject object = dataArray.get(position).getAsJsonObject();
if(selectedArray.contains(object.get("userkey").getAsString()))
{
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(2, Color.parseColor("#60B9E1"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}
else
{
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(1, Color.parseColor("#e0e0e0"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
holder.profileName.setText(object.get("name").getAsString());
holder.recycle_Listing.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.mainlayout.setSelected(!holder.mainlayout.isSelected());
if (holder.mainlayout.isSelected()) {
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(2, Color.parseColor("#60B9E1"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
// holder.mainlayout.setBackgroundColor(Color.parseColor("#11B5DA"));
} else {
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(1, Color.parseColor("#e0e0e0"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
// holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
itemListener.connectionClicked(v,position, itemtype);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#11B5DA"));
//holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
});
}
This code works fine with no repeated colour change in recycler. If any queries feel free to ask via comments or chat
Recyclerview & Room Database in Kotlin.
in Activity: //to send position to Adapter
val putPosition = 5 // what you want to be position
Handler(Looper.getMainLooper()).postDelayed({
(Recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(putPosition-1,0)
}, 1500)
//
//
// out of onCreate (for companion)
fun getVersePosition() = putPosition
in Adapter: // to get position from Activity
override fun onBindViewHolder(holder: Adapter.Holder, position: Int) {
val roomData = listData[position]
holder.setRoomData(roomData)
val activity : MainActivity.Companion = MainActivity
val getPosition : Int? = activity.companionMainActivity?.getVersePosition()
if (position == getPosition-1){
holder.itemView.setBackgroundColor(Color.parseColor("#AFB42B"))
} else {
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
}
}
SparseBooleanArray() doesn't need
and bind doesn't need too.
But it's very important
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
If omitted, repetition will occur.
I have a RecyclerView with a large list of items. This RecyclerView has OnScrollListener for endless scrolling.
When an item is selected I highlight it with a specific colour and
when unselected the colour changes to normal/white.
The issue that I am facing is after selecting a few visible items in my view when I scroll up or down to select a few more items the colour of already selected items changes to white.
I tried adding a boolean variable (isSelected) in the model class and highlight the selected item but still I am facing the same issue as earlier. Currently the recyclerView allows to select just one item from the view and after some research I figured some of the concepts were taken from this article to implement single item selection. I wonder how do I modify/tweak this code to be able to select multiple items.
I am not presenting the code as it is quite huge and is confidential but if there is any general fix for this scenario then what would it be?
Background: the application is a chat app and I am showing the sent and received texts. The user should be able to select a few specific texts and should be able to share it with someone else.
Edit: I am putting the code in my onBindViewHolder:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final PostDataColumns mPostDataColumns = data.get(position);
holder.textCardView.setBackgroundColor(mPostDataColumns.isSelected() ? getResources().getColor(R.color.long_press):
getResources().getColor(android.R.color.white));
holder.textCardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
mPostDataColumns.setSelected(!mPostDataColumns.isSelected());
if(mPostDataColumns.isSelected()) {
holder.textCardView.setBackgroundResource(R.color.long_press);
multipleSelectedPositions.add(holder.getLayoutPosition());
} else if(!mPostDataColumns.isSelected()) {
holder.textCardView.setBackgroundResource(android.R.color.white);
multipleSelectedPositions.remove(holder.getAdapterPosition());
}
//Adapter.this.onLongClick(holder, position);
return true;
}
});
holder.textCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.textCardView.setBackgroundResource(android.R.color.white);
/* clearLongSelection(holder, position, alignParentRight,
data.get(position).getReceiverUserId().length() > 5); */
}
});
}
The code which I have commented in onCLick and onLongClick were used when the requirement was to select a single item.
these are the methods which were called in onClick and onLOngClick:
public boolean clearLongSelection(ViewHolder holder, int position) {
if (selectedPosition >= 0) {
if (selectedPosition == position) {
holder.parentLayout.setBackgroundResource(android.R.color.transparent);
if (alignParentRight) {
holder.mediaCardView.setBackgroundResource(android.R.color.white);
holder.assessmentCardView.setBackgroundResource(android.R.color.white);
holder.surveyCardView.setBackgroundResource(android.R.color.white);
holder.documentCardView.setBackgroundResource(android.R.color.white);
holder.textCardView.setBackgroundResource(android.R.color.white);
} else {
holder.mediaCardView.setBackgroundResource(R.color.long_press);
holder.assessmentCardView.setBackgroundResource(R.color.long_press);
holder.surveyCardView.setBackgroundResource(R.color.long_press);
holder.documentCardView.setBackgroundResource(R.color.long_press);
holder.textCardView.setBackgroundResource(R.color.long_press);
}
selectedPosition = -1;
invalidateOptionsMenu();
getSupportActionBar().setTitle(intentData.getName());
}
return true;
}
return false;
}
public void onLongClick(ViewHolder holder, int position) {
if (selectedPosition < 0) {
holder.parentLayout.setBackgroundResource(R.color.long_press);
holder.mediaCardView.setBackgroundResource(R.color.long_press);
holder.assessmentCardView.setBackgroundResource(R.color.long_press);
holder.surveyCardView.setBackgroundResource(R.color.long_press);
holder.documentCardView.setBackgroundResource(R.color.long_press);
holder.textCardView.setBackgroundResource(R.color.long_press);
selectedPosition = position;
invalidateOptionsMenu();
getSupportActionBar().setTitle("1 Selected");
} else {
}
}
The variable selectedPosition in onClick and clearLongSelection is initialised in the class as instance variable- selectedPosition = -1.
Use SparseBooleanArray to keep track of selected items in recycler view adapter
Initialize the SparseBooleanArray as private memeber variable
private SparseBooleanArray mClickedItems=new SparseBooleanArray();
Then inside your click function while clicking any item,store the clicked item position as true.
mClickedItems.put(getAdapterPosition(),true);
notifyDataSetChanged();
Then in the onBindViewHolder check if the position is already selected or not like this
if(mClickedItems.get(position)==true){
//Show selected color
}else {
//show unselected color
}
I have a recyclerview which shows several(8) cardviews. Inside each cardview, there are several elements like text, imagebutton,etc. Inside the Adapter class (RvAdapter extends RecyclerView.Adapter) I have a code as below. What I want to do is change the color of Vector (.xml) assigned to the imagebutton using the OnClickListener of the same button.
public RvAdapter(Context context,List<Person> persons) {
this.mContext=context;
this.persons=persons;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_content,parent,false);
ViewHolder vh=new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ViewHolder mHolder=holder;
mHolder.personName.setText(persons.get(position).name);
mHolder.personAge.setText(( persons.get(position).age));
mHolder.cvCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext,"card number:"+String.valueOf(position),Toast.LENGTH_SHORT).show();
}
});
//this is where the error comes
mHolder.alarmImageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this code line doesnt work
//mHolder.alarmImageButton.setColorFilter(R.color.colorAccent);
//this code line works... but it changes the imagebutton color of another card view at below of the recycler view
mHolder.alarmImageButton.setColorFilter(Color.argb(255,255,255,255));
Toast.makeText(mContext,"button:"+String.valueOf(position),Toast.LENGTH_SHORT).show();
}
});
}
RecyclerView has 8 cardviews inside it. When the imagebutton of the first cardview's imagebutton is clicked, it changes the color to white. But the error is it changes the color of 7th card's imagebutton color too. When the second cardview's imagebutton is clicked it changes the 8th card's imagebutton color too. However, the Toast works fine. It shows that click event has identify the position of each cardview and imagebutton properly.
I just couldn't figure this out properly. How the 1st card 7th card views are connected with each other ?
This is my ViewHolder class
public class ViewHolder extends RecyclerView.ViewHolder{
CardView cvCard;
TextView personName;
TextView personAge;
ImageButton alarmImageButton;
public ViewHolder(View itemView) {
super(itemView);
cvCard=(CardView)itemView.findViewById(R.id.cv_card);
personName=(TextView)itemView.findViewById(R.id.txt_person_name);
personAge=(TextView)itemView.findViewById(R.id.txt_person_age);
alarmImageButton=(ImageButton)itemView.findViewById(R.id.alarm_image_button);
}
It will be a great help if someone can help me to find this error...
I just couldn't figure this out properly. How the 1st card 7th card
views are connected with each other?
The point of using a RecyclerView is that the Views are getting recycled.
You set the colorFilter of the ImageButton, but that same button may be used in a recycled View also.
You should store the clicked state of your RecyclerView rows and set the colorFilter accordingly on each ImageButton in onBindViewHolder().
For example:
// a HashSet to store the clicked positions
private HashSet<Integer> clickedPositions = new HashSet<>();
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ViewHolder mHolder = holder;
mHolder.personName.setText(persons.get(position).name);
mHolder.personAge.setText((persons.get(position).age));
mHolder.cvCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext, "card number:" + String.valueOf(position),
Toast.LENGTH_SHORT).show();
}
});
if (clickedPositions.contains(mHolder.getAdapterPosition())) {
// the current item is clicked, change its color
mHolder.alarmImageButton.setColorFilter(Color.argb(255, 255, 255, 255));
} else {
// else change it to another color
mHolder.alarmImageButton.setColorFilter(Color.argb(0, 0, 0, 0));
}
mHolder.alarmImageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext, "button:" + String.valueOf(position),
Toast.LENGTH_SHORT).show();
// if the item is already clicked, remove it from the clicked items
if (!clickedPositions.remove(mHolder.getAdapterPosition())) {
// if not clicked, add it to the clicked items
clickedPositions.add(mHolder.getAdapterPosition());
}
// and notify the Adapter that the item is changed
notifyItemChanged(mHolder.getAdapterPosition());
}
});
}
Store the ids in a sparsebooleanarray. if the colour is changed store true otherwise false.
add these methods to your adapter.
private SparseBooleanArray mSelectedItemsIds;
//Methods to do Selection
public void toggleSelection(int position) {
selectView(position, !mSelectedItemsIds.get(position));
}
//Remove selected selections
public void removeSelection() {
mSelectedItemsIds = new SparseBooleanArray();
notifyDataSetChanged();
}
//Put or delete selected position into SparseBooleanArray
public void selectView(int position, boolean value) {
if (value)
mSelectedItemsIds.put(position, value);
else
mSelectedItemsIds.delete(position);
// notifyItemChangedAtPosition(position);
notifyDataSetChanged();
}
then call it as and when required.