Recycler view item colour change repeating after scrolling - android

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.

Related

how to set color the selected Item of Recycler View From Activity? [duplicate]

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

Change recyclerView item background onClick?

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

Handling click button in each RecyclerView and update TextView value

When button clicked, i must update a TextView in same position and I have done it, but 9th and 10th position of RecyclerView follow first position and second position. In other word, if I clicked first button position, First position of TextView is updated, but, 9th position of TextView also updated, It should be not updated. How to solve this?
I follow this link
here is my Adapter
class ProductsByStoreAdapter extends RecyclerView.Adapter<ProductsByStoreAdapter.ViewHolder> {
private ArrayList<Products> products;
ProductsByStoreAdapter(ArrayList<Products> productses) {
this.products = productses;
//products = CenterRepository.getCenterRepository()
//.getListOfProductsInShoppingList();
}
#Override
public ProductsByStoreAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.products_card_item, viewGroup, false);
return new ProductsByStoreAdapter.ViewHolder(view);
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView tv_product_name, tv_product_price, tv_product_quantity;
private ImageView im_product_image;
private ImageButton button_add_product, button_min_product;
private EditText e_note;
private LinearLayout layout_note;
ViewHolder(View view) {
super(view);
im_product_image = (ImageView)view.findViewById(R.id.product_image);
tv_product_name = (TextView)view.findViewById(R.id.product_name);
tv_product_price = (TextView)view.findViewById(R.id.product_price);
tv_product_quantity = (TextView)view.findViewById(R.id.product_quantity);
e_note = (EditText)view.findViewById(R.id.e_note);
layout_note = (LinearLayout)view.findViewById(R.id.layout_note);
this.button_add_product = (ImageButton)view.findViewById(R.id.button_add_product);
button_min_product = (ImageButton)view.findViewById(R.id.button_min_product);
}
}
#Override
public void onBindViewHolder(final ProductsByStoreAdapter.ViewHolder viewHolder, final int position) {
Glide.with(viewHolder.im_product_image.getContext())
.load(products.get(position).getImage_uri())
.centerCrop()
.crossFade()
//.placeholder(R.drawable.placeholder_main)
.into(viewHolder.im_product_image);
CurrencyFormats currencyFormat = new CurrencyFormats();
viewHolder.tv_product_name.setText(products.get(position).getName());
viewHolder.tv_product_price.setText(currencyFormat.toRupiah(products.get(position).getPrice()));
//viewHolder.tv_product_quantity.setText("0");
viewHolder.button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = (products).get(position);
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
viewHolder.button_min_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Products tempObj = (products).get(position);
viewHolder.tv_product_quantity.setText(CenterRepository
.getCenterRepository().getListOfProductsInShoppingList()
.get(indexOfTempInShopingList).getQuantity());
((ProductsByStoreActivity)view.getContext()).updateItemCount(false);
}
}
}else {
}
}
});
}
#Override
public int getItemCount() {
//return products.size();
return products == null ? 0 : products.size();
}}
You need to move your onclick listener into onCreateViewHolder.
final ProductsByStoreAdapter.ViewHolder viewHolder = new ProductsByStoreAdapter.ViewHolder(view);
button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = products.get(viewHolder.getAdapterPosition(););
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
return viewHolder;
You can do the same with the other onclicklistener
Update: You do not setText to your product_quantity textview in the BindView function, unless a button is clicked. this means its value will be recycled from other items. you should check with an if statement what is the quantity of the item and present it even without clicking.
Old and not correct answer:
I am not sure if this is the problem, but its an easy check, so try it out. There are 2 positions - the adapter position, and the layout position. I think maybe the position you are using (the one that came from the onBind function) is the latter. You want the adapter position, so try using getAdapterPosition() like this:
Products tempObj = (products).get(getAdapterPosition());
add below line to resolve the problem of 9th and 10th position of item
#Override
public int getItemViewType(int position)
{
return position;
}

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*/
}
}
}
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); }

Android recyclerview issue with animations

I'm trying to make cardviews in recyclerview expand. I got the expanding part working, but when adding transition to it, some visual bugs start to occur. The transitions works fine, when there are no off-screen items, but when I add more than (in my case) 4 items to the recyclerview, it starts to occur.
GIF with 4 items
GIF with more than 4 items
The cardview expanding works fine with more than 4 items when I disable the transition animation. I think the problem is related to positions changing, but I can't find any solution to the problem.
The guide I used to implement the cardview expanding can be found here: https://stackoverflow.com/a/38623873/6673949
And my complete recyclerview adapter
public class BasketRecyclerAdapter extends RecyclerView.Adapter<BasketRecyclerAdapter.CustomViewHolder> {
private String letter;
private Context mContext;
private ColorGenerator generator = ColorGenerator.MATERIAL;
private List<Basket> baskets;
private int mExpandedPosition = -1;
private RecyclerView r1;
public BasketRecyclerAdapter(Context context, List<Basket> baskets, RecyclerView r1) {
this.mContext = context;
this.baskets = baskets;
this.r1 = r1;
}
#Override
public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.basket_menu_item, null);
CustomViewHolder viewHolder = new CustomViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(final BasketRecyclerAdapter.CustomViewHolder holder, final int position) {
String basketName = baskets.get(position).getBasketName();
holder.basketName.setText(basketName);
letter = "" + basketName.charAt(0);
TextDrawable drawable = TextDrawable.builder()
.buildRound(letter, generator.getColor(basketName));
holder.imageLetter.setImageDrawable(drawable);
final boolean isExpanded = position == mExpandedPosition;
holder.expandedLayout.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? -1:position;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
TransitionManager.beginDelayedTransition(r1);
}
notifyDataSetChanged();
}
});
}
What can be done to solve the problem? Thanks.
Edit: I tried to get it to work by using just ListView instead of RecyclerView, but ListView adapter doesn't expand with same code - will try to figure out why.
Edit2: Got it working by using another library called "ExpandableLayout" but still can't seem to figure out why is it not working without additional libraries.
I had similar issue.
Add in adapter:
#Override
public long getItemId(int position) {
return position;
}
And myAdapter.setHasStableIds(true);
I had the same problem. This is how I worked it out and works like you showed on GIF with 4 items:)
In your custom adapter add notifyItemChanged(position) and notifyItemChanged(previousExpandedPosition) instead of notifyDataSetChanged():
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
final boolean isExpanded = position==mExpandedPosition;
holder.expandedLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
if (isExpanded)
mPreviousExpandedPosition = position;
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? -1:position;
TransitionManager.beginDelayedTransition(mRecyclerView);
notifyItemChanged(mPreviousExpandedPosition);
notifyItemChanged(position);
}
});
}
Declare global variables mExpandedPosition and mPreviousExpandedPosition and initialize them to -1.
Pass the mRecyclerView as a parameter in the constructor for the adapter.
In your fragment or activity set mRecyclerView.setItemAnimator(null) after setting adapter for recycle view, like this:
mRecyclerView.setAdapter(mRecyclerViewAdapter);
mRecyclerView.setItemAnimator(null);
This should solve your problem.
I had a similar issue. When there were no items offscreen it worked fine, but when you have items offscreen the animation does not work properly. The solution was to not update every item in the adapter using notifyDataSetChanged(). You need to only update the position you want to expand AND the position you want to collapse. My guess is that the ViewHolders for items off screen are messing up the animation.
final boolean isExpanded = position == mExpandedPosition;
vh.expandableView.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
vh.itemView.setActivated(isExpanded);
vh.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// collapse any currently expanded items
if (mExpandedPosition != RecyclerView.NO_POSITION) {
notifyItemChanged(mExpandedPosition);
}
//Update expanded position
mExpandedPosition = isExpanded ? -1 : position;
TransitionManager.beginDelayedTransition(mRecyclerView);
//Expand new item clicked
notifyItemChanged(position);
}
});

Categories

Resources