首页 > 解决方案 > RecyclerView 过滤器在 xamarin.android 中不起作用

问题描述

我在recylerview通过searchview操作栏过滤数据时遇到了麻烦。我有一个项目示例并尝试实现该方法。但数据不会过滤。

我把它放在我的片段上(搜索视图)

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

这是我的适配器:

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

protected override void PublishResults(ICharSequence constraint, FilterResults results)我得到过滤的数据。但是回收站视图不会更新(未过滤)。我错过了什么吗?感谢帮助 :)

标签: android-recyclerviewxamarin.androidsearchview

解决方案


我认为你应该使用mFilteredLocationsin OnBindViewHolder

if (this.mFilteredLocations.Count > 0)
...
var location = this.mFilteredLocations[position];

这样您就可以mLocations保留原始位置列表,并且您在您的位置上显示的RecyclerView始终是您的过滤列表。显然,一开始两者mFilteredLocationsmLocations必须具有相同的项目。

public LocationsRecycleAdapter(List<LocationViewModel> locations, Activity activity)
{
    this.mLocations = locations;
    this.mFilteredLocations = locations;
    this.mActivity = activity;
    Filter = new LocationFilter(this);
}

在您的过滤器上,您正在更改mFilteredLocations并通知数据集已更改,以便刷新所有数据。但这只有在您进行上述更改时才会生效。

在这里,您有另一个相关的问题/答案,但在 Android 上,如果它可以为您提供进一步的指导。

HIH


推荐阅读