Reputation: 1847
I have this selection activity I use to select elements. It's based on a superclass and it uses a RecyclerView. It's built using generics, so it's easy to make a "selector" passing a model class. It's possible to use it to make single or multiple selections.
The problem is that if I select on element and I scroll, sometimes I see also other rows selected. The behavior is strange, if I scroll down everything looks ok, if I return back enough to see again the selected rows and I restart to scroll down, I see other rows duplicated. It's only a graphical thing so if I press ok the activity returns only the right elements.
I'm quite sure it's a problem about the recycling, so I tried to set as not recyclable the selected rows with IsRecyclable, but it doesn't work.
Any suggestion?
Part of the RecyclerView.Adapter:
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup viewGroup, int position)
{
var itemView = LayoutInflater.From(viewGroup.Context).Inflate(Resource.Layout.activity_basemodel_item, viewGroup, false);
var viewHolder = new BaseModelViewHolder(itemView, OnClick, OnLongClick, DetailActivityType);
viewHolder.IsRecyclable = !SelectionEnabled;
return viewHolder;
}
public void HighLight(RecyclerView.ViewHolder viewHolder, int position)
{
if (!SelectionEnabled) { return; }
var guid = ((BaseModelViewHolder)viewHolder).Model.Id;
if (SelectedGuids.Contains(guid))
{
viewHolder.ItemView.Selected = true;
viewHolder.ItemView.SetBackgroundColor(SelectedItemBackgroundColor);
}
else
{
viewHolder.ItemView.Selected = false;
}
}
public override void OnBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
{
TModel model = DataSet[position];
String title = model.Title;
String subtitle = model.Subtitle;
((BaseModelViewHolder)viewHolder).TxtTitle.SetText(title, TextView.BufferType.Normal);
if (!String.IsNullOrEmpty(subtitle) && !subtitle.Equals(title))
{
((BaseModelViewHolder)viewHolder).TxtSubtitle.SetText(subtitle, TextView.BufferType.Normal);
}
((BaseModelViewHolder)viewHolder).Model = model;
this.HighLight(viewHolder, position);
}
Upvotes: 1
Views: 530
Reputation: 589
RecyclerViews can show weird behavior when the Model is passed inside the ViewModel:
((BaseModelViewHolder)viewHolder).Model = model; //problem
To solve this, you can do it like this:
public void HighLight(RecyclerView.ViewHolder viewHolder, int position)
{
if (!SelectionEnabled) { return; }
TModel model = DataSet[position];
var guid = model.Id;
if (SelectedGuids.Contains(guid))
{
viewHolder.ItemView.SetBackgroundColor(SelectedItemBackgroundColor);
}
else
{
viewHolder.ItemView.SetBackgroundColor(DefaultItemBackgroundColor);
}
}
public override void OnBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
{
TModel model = DataSet[position];
String title = model.Title;
String subtitle = model.Subtitle;
((BaseModelViewHolder)viewHolder).TxtTitle.SetText(title, TextView.BufferType.Normal);
if (!String.IsNullOrEmpty(subtitle) && !subtitle.Equals(title))
{
((BaseModelViewHolder)viewHolder).TxtSubtitle.SetText(subtitle, TextView.BufferType.Normal);
}
this.HighLight(viewHolder, position);
// To highlight an item when clicked:
viewHolder.ItemView.Click -= HighLight_Item;
viewHolder.ItemView.Click += HighLight_Item; //This is to avoid subscribing the event everytime the view is shown
}
To select an item:
private void HighLight_Item(object sender, EventArgs e)
{
//You need to pass the RecyclerView as an argument to the Adapter
int position = this.recyclerView.GetChildAdapterPosition((View)sender);
TModel model = DataSet[position];
var guid = model.Id;
//If already contains then remove, if doesn't contain then add
if(SelectedGuids.Contains(guid)) SelectedGuids.Remove(guid);
else SelectedGuids.Add(guid);
//This will update the item view
this.NotifyItemChanged(position);
}
Upvotes: 1