I have a Recyclerview the items of which contain icons that I am trying to rotate by specific degrees (counter clockwise). Problem is some of the icons do not rotate at all, or after I scroll (or when the activity resumes) they are set back to their initial position. As seen in the image below, the first 3 arrows have not been rotated at all, while the 4th arrow is pointing correctly.
Below is the custom adapter
public class CustomListAdapter extends RecyclerView.Adapter<CustomListAdapter.TransportationViewHolder> {
Context ctx;
ArrayList<Transportation> transportationArrayList, copy;
public CustomListAdapter(Context ctx, ArrayList<Transportation> transportation) {
this.ctx = ctx;
this.transportationArrayList = new ArrayList<>(transportation);
}
#NonNull
#Override
public TransportationViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.transportation_item_layout, parent, false);
return new CustomListAdapter.TransportationViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull TransportationViewHolder holder, int position) {
Transportation transportation = transportationArrayList.get(position);
holder.transportationFleetTypeTextView.setText(description);
rotateHeading((float)transportation.getHeading(), holder.headingImageView);
}
#Override
public int getItemCount() {
return transportationArrayList.size();
}
public static class TransportationViewHolder extends RecyclerView.ViewHolder {
TextView transportationFleetTypeTextView;
ImageView transportationImageView, headingImageView;
public TransportationViewHolder(View itemView) {
super(itemView);
transportationFleetTypeTextView = itemView.findViewById(R.id.transportation_item_text);
transportationImageView = itemView.findViewById(R.id.transportation_item_image);
headingImageView = itemView.findViewById(R.id.heading_image);
}
public void clearAnimation() {
itemView.clearAnimation();
}
}
#Override
public long getItemId(int position) {
return position;
}
public void rotateHeading(float rotation, ImageView imageView)
{
AnimationSet animSet = new AnimationSet(true);
animSet.setInterpolator(new DecelerateInterpolator());
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
final RotateAnimation animRotate = new RotateAnimation(0f, 360f-rotation,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animRotate.setDuration(0);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);
imageView.startAnimation(animSet);
}
#Override
public void onViewDetachedFromWindow(#NonNull final TransportationViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.clearAnimation();
}
}
And below is the way I initialize the adapter (which is within a class that extends AsyncTask).
mainActivity.customListAdapter = new CustomListAdapter(mainActivity, transportationArrayList);
mainActivity.customListAdapter.setHasStableIds(true);
mainActivity.customListAdapter.notifyDataSetChanged();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mainActivity);
recyclerView = findViewById(R.id.transportationRecyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(customListAdapter);
Could someone explain to me what I am doing wrong and why this is happening?
Since the duration is zero and you only want the result of the animation and not the animation itself, I suggest that you simplify the code by replacing the line
rotateHeading((float)transportation.getHeading(), holder.headingImageView);
with
holder.headingImageView.seRotation(360 = (float)transportation.getHeading())
and deleting all the animation code.
This change may not fix the underlying issue that you are having, but it will eliminate animation as the source of the problem.
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.
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 am trying to implement my own recyclerview Animation - I would like to achieve this without using any external libraries. Here is what the theoretical animation should look like.
The user clicks an item on the List and an animation occurs which opens up another View.
On a high level with minimal code, possibly just pseudo code what would the process be in order to create some animation like that?
Also I would like to note that the animation should be able to be done in reverse as well if the user clicks the same item or another item
I am not that familiar with the RecyclerView class and would like to learn more about it and any animations associated with it.
Solution:
The way I solved this problem was to implement a listener View.OnClickListener to the ViewHolder class which extends RecyclerView.ViewHolder. So we get the following code:
public static class ExampleViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private int originalHeight = 0;
private boolean isViewExpanded = false;
private YourCustomView yourCustomView
// ..... CODE ..... //
}
The variables originalHeight and isViewExpanded are used in the animation process. In the constructor I initialize the view to the View.OnClickListener like so:
public ExampleViewHolder(View v) {
super(v);
v.setOnClickListener(this);
// Initialize other views, like TextView, ImageView, etc. here
// If isViewExpanded == false then set the visibility
// of whatever will be in the expanded to GONE
if (isViewExpanded == false) {
// Set Views to View.GONE and .setEnabled(false)
yourCustomView.setVisibility(View.GONE);
yourCustomView.setEnabled(false);
}
}
Now that the constructor has been taken care of we want to configure what happens when the user clicks on an individual RecyclerView item. The classes that will be useful here would be the ValueAnimator and the Animation objects. We override the onClick method like so to accomplish this:
#Override
public void onClick(final View view) {
// If the originalHeight is 0 then find the height of the View being used
// This would be the height of the cardview
if (originalHeight == 0) {
originalHeight = view.getHeight();
}
// Declare a ValueAnimator object
ValueAnimator valueAnimator;
if (!mIsViewExpanded) {
yourCustomView.setVisibility(View.VISIBLE);
yourCustomView.setEnabled(true);
mIsViewExpanded = true;
valueAnimator = ValueAnimator.ofInt(originalHeight, originalHeight + (int) (originalHeight * 2.0)); // These values in this method can be changed to expand however much you like
} else {
mIsViewExpanded = false;
valueAnimator = ValueAnimator.ofInt(originalHeight + (int) (originalHeight * 2.0), originalHeight);
Animation a = new AlphaAnimation(1.00f, 0.00f); // Fade out
a.setDuration(200);
// Set a listener to the animation and configure onAnimationEnd
a.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
yourCustomView.setVisibility(View.INVISIBLE);
yourCustomView.setEnabled(false);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
// Set the animation on the custom view
yourCustomView.startAnimation(a);
}
valueAnimator.setDuration(200);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
view.getLayoutParams().height = value.intValue();
view.requestLayout();
}
});
valueAnimator.start();
}
Now when you touch an individual cardview on the RecyclerView (assuming you have a CardView setup then it should expand out. Make sure to declare your customView properly in your xml file (example if you want the CardView to expand down when you touch it then properly assign the customView underneath the other views and set the visibility to gone when you declare it and then when the animation starts like so in the code above then set the visibility to Visible and enable the view.
Hope this can help someone out.
An easier alternative for #AndyRoid's answer is to use android:animateLayoutChanges="true" property. This way you don't need to write any animation code; however, this is not a way to go if you need to have a control over animation.
You still need to create an OnClickListener:
class CardTapListener implements View.OnClickListener {
#Override
public void onClick(View v) {
View someView = v.findViewById(R.id.view_to_expand);
if (someView.getVisibility() == View.GONE) {
someView.setVisibility(View.VISIBLE);
}
else if (someView.getVisibility() == View.VISIBLE){
someView.setVisibility(View.GONE);
}
}
}
Attach it to every ViewHolder:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.view_holder_layout, viewGroup, false);
v.setOnClickListener(new CardTapListener());
return new ItemViewHolder(v);
}
Don't forget to collapse views when binding a new item:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
...
// Collapse (probably opened by user previously) view
ItemViewHolder itemHolder = (ItemViewHolder) viewHolder;
itemHolder.description.setVisibility(View.GONE);
...
}
view_holder_layout.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
...
<AnyViewHere
android:visibility="gone"
android:id="#+id/view_to_expand" />
</LinearLayout>