Changing drawable in RecyclerView updates all rows

3.8k views Asked by At

I am trying to get the 'like' button functionality in my application just like to Facebook. What i want is that whenever the user clicks on the like button, the color of the drawable on the button should change from grey to blue. I am using RecyclerView for the feeds. Since, RecyclerView enforces to use viewholder pattern, the color of all the like buttons are changed thereafter. I would really appreciate some help to solve me this problem.

This is the code for Adapter

public class FacebookTimelineAdapter : RecyclerView.Adapter
{
    Context mContext { get; set;}
    List<FacebookModel> mData;
    LayoutInflater inflater;
    FacebookViewHolder holder;

    public FacebookTimelineAdapter (Context context,List<FacebookModel> data)
    {
        this.mContext = context;
        this.mData = data;
        this.inflater = LayoutInflater.From (context);
    }

    #region implemented abstract members of Adapter
    public override void OnBindViewHolder (RecyclerView.ViewHolder viewholder, int position)
    {
        holder = viewholder as FacebookViewHolder;
        holder.userName.Text = mData [position].userName;
        holder.timestamp.Text = mData [position].timestamp;

        if(!(mData[position].profilePictureId==0) || !(mData[position].profilePictureId==null))
        holder.profilePicture.SetImageDrawable (mContext.Resources.GetDrawable(mData[position].profilePictureId));

        if (mData [position].feedMessage == "") {
            holder.feedMessage.Visibility = ViewStates.Gone;
        } else {
            holder.feedMessage.Visibility = ViewStates.Visible;
            holder.feedMessage.Text = mData [position].feedMessage;
        }


        if ((mData [position].feedUrl == "") || (mData [position].feedUrl ==null)) {
            holder.feedUrl.Visibility = ViewStates.Gone;
        } else {
            holder.feedUrl.Visibility = ViewStates.Visible;
            holder.feedUrl.Text = mData [position].feedUrl;
        }

        if ((mData [position].feedImage==null) || (mData [position].feedImage==0)) {
            holder.feedImage .Visibility = ViewStates.Gone;
        } else {
            holder.feedImage .Visibility = ViewStates.Visible;
            holder.feedImage.SetImageDrawable(mContext.Resources.GetDrawable(mData[position].feedImage)) ;
        }

        holder.like.Click += delegate {
            Drawable img = mContext.Resources.GetDrawable (Resource.Drawable.fb_comment);
            holder.like.SetCompoundDrawablesWithIntrinsicBounds (img, null, null, null);
        };



    }

    public override RecyclerView.ViewHolder OnCreateViewHolder (ViewGroup parent, int viewType)
    {

        return new FacebookViewHolder (inflater.Inflate (Resource.Layout.timeline_item_facebook,parent,false),this);

    }


    public override int ItemCount {
        get {
            return mData.Count;
        }
    }

    public void LikeClicked(int position)
    {
        Drawable img = mContext.Resources.GetDrawable (Resource.Drawable.fb_comment);
        holder.like.SetCompoundDrawablesWithIntrinsicBounds (img, null, null, null);

    }
    #endregion
}
}

This is code for ViewHolder

    public class FacebookViewHolder : RecyclerView.ViewHolder
    {
        public TextView userName,timestamp,feedUrl,feedMessage;
        public ImageView profilePicture,feedImage;
        public Button like, comment;


        public FacebookViewHolder(View item, FacebookTimelineAdapter adapter) : base(item)
        {
            userName=item.FindViewById<TextView>(Resource.Id.fb_userName);
            timestamp=item.FindViewById<TextView>(Resource.Id.fb_time);
            feedUrl=item.FindViewById<TextView>(Resource.Id.fb_feed_url);
            feedMessage=item.FindViewById<TextView>(Resource.Id.fb_feed_message);
            profilePicture=item.FindViewById<ImageView>(Resource.Id.fb_profilePicture);
            feedImage=item.FindViewById<ImageView>(Resource.Id.fb_feed_image);
            like=item.FindViewById<Button>(Resource.Id.fb_feed_like);
            comment=item.FindViewById<Button>(Resource.Id.fb_feed_comment);

        }

    }

Code is in C# since i am developing android app in Xamarin.

2

There are 2 answers

2
Marcos Vasconcelos On BEST ANSWER

When using the recycle view pattern you should 'reset' the state of the recycled view to its initial state, this way you will not have changes made for one child applied to other when the view got recycled.

0
tintin21 On

I kind of worked around the solution provided by Marcos.

This is my Adapter

public class FacebookTimelineAdapter : RecyclerView.Adapter
{
    Context mContext { get; set;}
    List<FacebookModel> mData;
    LayoutInflater inflater;
    FacebookViewHolder holder;

    public FacebookTimelineAdapter (Context context,List<FacebookModel> data)
    {
        this.mContext = context;
        this.mData = data;
        this.inflater = LayoutInflater.From (context);
    }

    #region implemented abstract members of Adapter
    public override void OnBindViewHolder (RecyclerView.ViewHolder viewholder, int position)
    {
        holder = viewholder as FacebookViewHolder;
        holder.userName.Text = mData [position].userName;
        holder.timestamp.Text = mData [position].timestamp;

        if(!(mData[position].profilePictureId==0) || !(mData[position].profilePictureId==null))
        holder.profilePicture.SetImageDrawable (mContext.Resources.GetDrawable(mData[position].profilePictureId));

        if (mData [position].feedMessage == "") {
            holder.feedMessage.Visibility = ViewStates.Gone;
        } else {
            holder.feedMessage.Visibility = ViewStates.Visible;
            holder.feedMessage.Text = mData [position].feedMessage;
        }


        if ((mData [position].feedUrl == "") || (mData [position].feedUrl ==null)) {
            holder.feedUrl.Visibility = ViewStates.Gone;
        } else {
            holder.feedUrl.Visibility = ViewStates.Visible;
            holder.feedUrl.Text = mData [position].feedUrl;
        }

        if ((mData [position].feedImage==null) || (mData [position].feedImage==0)) {
            holder.feedImage .Visibility = ViewStates.Gone;
        } else {
            holder.feedImage .Visibility = ViewStates.Visible;
            holder.feedImage.SetImageDrawable(mContext.Resources.GetDrawable(mData[position].feedImage)) ;
        }

  // Made changes in data structure to see if the post was liked or not. 

    if (mData [position].liked) {
            Drawable img = mContext.Resources.GetDrawable (Resource.Drawable.fb_liked);
            holder.like.SetCompoundDrawablesWithIntrinsicBounds (img, null, null, null);
        } else {
            Drawable img = mContext.Resources.GetDrawable (Resource.Drawable.fb_like);
            holder.like.SetCompoundDrawablesWithIntrinsicBounds (img, null, null, null);
        }

    }

    public override RecyclerView.ViewHolder OnCreateViewHolder (ViewGroup parent, int viewType)
    {

        return new FacebookViewHolder (inflater.Inflate (Resource.Layout.timeline_item_facebook,parent,false),this);

    }


    public override int ItemCount {
        get {
            return mData.Count;
        }
    }

 // On Clicking the like button notifyItemChanged() is called
    public void LikeClicked(int position)
    {
        if (mData [position].liked)
            mData [position].liked = false;
        else
            mData [position].liked = true;

        NotifyItemChanged (position);

    }

This is my ViewHolder

 public class FacebookViewHolder : RecyclerView.ViewHolder
    {
        public TextView userName,timestamp,feedUrl,feedMessage;
        public ImageView profilePicture,feedImage;
        public Button like, comment;


        public FacebookViewHolder(View item, FacebookTimelineAdapter adapter) : base(item)
        {
            userName=item.FindViewById<TextView>(Resource.Id.fb_userName);
            timestamp=item.FindViewById<TextView>(Resource.Id.fb_time);
            feedUrl=item.FindViewById<TextView>(Resource.Id.fb_feed_url);
            feedMessage=item.FindViewById<TextView>(Resource.Id.fb_feed_message);
            profilePicture=item.FindViewById<ImageView>(Resource.Id.fb_profilePicture);
            feedImage=item.FindViewById<ImageView>(Resource.Id.fb_feed_image);
            like=item.FindViewById<Button>(Resource.Id.fb_feed_like);
            comment=item.FindViewById<Button>(Resource.Id.fb_feed_comment);

           // Added Click listener in Viewholder
            like.Click += delegate {
                adapter.LikeClicked(LayoutPosition);
            }; 
        }

    }