Reputation: 139
I got trouble filtering data in recylerview
through searchview
action bar. I got an project example and trying to implement the method. But the data won't filtering.
I put this on my fragment (search view)
public override void OnCreateOptionsMenu(IMenu menu,MenuInflater menuInflater)
{
//SearchMenu
menuInflater.Inflate(Resource.Menu.nav_search, menu);
var searchItem = menu.FindItem(Resource.Id.action_search);
var provider = MenuItemCompat.GetActionView(searchItem);
mSearchView = provider.JavaCast<Android.Support.V7.Widget.SearchView>();
mSearchView.QueryTextChange += (s, e) => mLocationsAdapter.Filter.InvokeFilter(e.NewText);
mSearchView.QueryTextSubmit += (s, e) =>
{
Toast.MakeText(this.Activity, "You searched: " + e.Query, ToastLength.Short).Show();
e.Handled = true;
};
MenuItemCompat.SetOnActionExpandListener(searchItem, new SearchViewExpandListener(mLocationsAdapter));
}
And this is my adapter:
namespace ShopDiaryApp.Adapter
{
public class LocationsRecycleAdapter : RecyclerView.Adapter,IFilterable
{
private readonly Activity mActivity;
private List<LocationViewModel> mLocations;
private List<LocationViewModel> mFilteredLocations;
private int mSelectedPosition = -1;
public LocationsRecycleAdapter(List<LocationViewModel> locations, Activity activity)
{
this.mLocations = locations;
this.mActivity = activity;
Filter = new LocationFilter(this);
}
public override int ItemCount => this.mLocations.Count;
public Filter Filter { get; private set; }
public event EventHandler<int> ItemClick;
private void OnClick(int position)
{
this.ItemClick?.Invoke(this, position);
NotifyItemChanged(position);
mSelectedPosition = position;
NotifyItemChanged(position);
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if (this.mLocations.Count > 0)
{
var vh = holder as ViewHolder;
if (vh != null)
{
var location = this.mLocations[position];
vh.LocationName.Text = location.Name;
vh.LocationAddress.Text = location.Address;
vh.LocationDescription.Text = location.Description;
vh.ItemView.Selected = (mSelectedPosition == position);
}
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var v = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.AdapterLocations, parent, false);
var vh = new ViewHolder(v, this.OnClick);
return vh;
}
public class ViewHolder : RecyclerView.ViewHolder
{
public ViewHolder(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public ViewHolder(View itemView, Action<int> listener)
: base(itemView)
{
this.LocationName = itemView.FindViewById<TextView>(Resource.Id.textviewLocationsAdapterName);
this.LocationAddress = itemView.FindViewById<TextView>(Resource.Id.textviewLocationAdapterAddress);
this.LocationDescription = itemView.FindViewById<TextView>(Resource.Id.textviewLocationAdapterDescription);
itemView.Click += (sender, e) => listener(this.LayoutPosition);
}
public TextView LocationName { get; }
public TextView LocationAddress { get; }
public TextView LocationDescription { get; }
}
private class LocationFilter : Filter
{
private readonly LocationsRecycleAdapter mLocationAdapter;
public LocationFilter(LocationsRecycleAdapter adapter)
{
mLocationAdapter = adapter;
}
protected override FilterResults PerformFiltering(ICharSequence constraint)
{
var returnObj = new FilterResults();
var results = new List<LocationViewModel>();
if (mLocationAdapter.mLocations == null)
mLocationAdapter.mLocations = mLocationAdapter.mFilteredLocations;
if (constraint == null) return returnObj;
if (mLocationAdapter.mLocations != null && mLocationAdapter.mLocations.Any())
{
// Compare constraint to all names lowercased.
// It they are contained they are added to results.
results.AddRange(
mLocationAdapter.mLocations.Where(
location => location.Name.ToLower().Contains(constraint.ToString())));
}
// Nasty piece of .NET to Java wrapping, be careful with this!
returnObj.Values = FromArray(results.Select(r => r.ToJavaObject()).ToArray());
returnObj.Count = results.Count;
constraint.Dispose();
return returnObj;
}
protected override void PublishResults(ICharSequence constraint, FilterResults results)
{
using (var values = results.Values)
mLocationAdapter.mFilteredLocations = values.ToArray<Java.Lang.Object>()
.Select(r => r.ToNetObject<LocationViewModel>()).ToList();
mLocationAdapter.NotifyDataSetChanged();
// Don't do this and see GREF counts rising
//constraint.Dispose();
//results.Dispose();
}
}
}
}
In protected override void PublishResults(ICharSequence constraint, FilterResults results)
I got the filtered data. But the recyler view wont update (not filtered). Am I missing something?? Thanks for help :)
Upvotes: 0
Views: 104
Reputation: 3916
I think that you should be using mFilteredLocations
in OnBindViewHolder
:
if (this.mFilteredLocations.Count > 0)
...
var location = this.mFilteredLocations[position];
So that your mLocations
stays as your original list of locations and what you show on your RecyclerView
is always your filtered list.
Obviously at the beginning both mFilteredLocations
and mLocations
have to have the same items.
public LocationsRecycleAdapter(List<LocationViewModel> locations, Activity activity)
{
this.mLocations = locations;
this.mFilteredLocations = locations;
this.mActivity = activity;
Filter = new LocationFilter(this);
}
And on your filter you are changing mFilteredLocations
and notify the data set changed so that all data is refreshed. But this will only take effect if you do the aforementioned changes.
Here you have another related question/answer but on Android if it can serve you as a further guidance.
HIH
Upvotes: 1