Searching Through RecyclerView - Xamarin.Droid

前端 未结 1 1630
遇见更好的自我
遇见更好的自我 2020-12-11 14:08

I have been searching for the Xamarin way of searching through a recyclerView. Can anyone kindly refer me to a demo on how to do that in the Xamarin way?

相关标签:
1条回答
  • 2020-12-11 14:34

    I wrote up a simple demo about how to implement this feature, effect like this. You can see it in this GitHub Repository.

    For more information, you could read the document : Filtering ListView with SearchView in Xamarin.Android and Xaver Kapeller's answer about filter a RecyclerView with a SearchView.

    Thanks for Xaver Kapeller's answer, his answer about Searching Through RecyclerView was great, so I decide translate it to Xamarin to help more people.

    1. Setting up the SearchView

    In the folder res/menu create a new file called main.xml. In it add an item and set the actionViewClass to android.support.v7.widget.SearchView. Since you are using the support library you have to use the namespace of the support library to set the actionViewClass attribute. Your xml file should look something like this:

     <?xml version="1.0" encoding="utf-8"?>
     <menu xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">
         <item android:id="@+id/action_search"
               android:title="Search"
               android:icon="@android:drawable/ic_menu_search"
               app:showAsAction="always|collapseActionView"
               app:actionViewClass="android.support.v7.widget.SearchView" />
      </menu>
    

    In your Activity you have to inflate this menu xml like usual, then you can look for the MenuItem which contains the SearchView and add a delegate on QueryTextChange which we are going to use to listen for changes to the text entered into the SearchView:

    public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.main, menu);
    
            var item = menu.FindItem(Resource.Id.action_search);
            var searchView = MenuItemCompat.GetActionView(item);
            _searchView = searchView.JavaCast<Android.Support.V7.Widget.SearchView>();
    
            _searchView.QueryTextChange += (s, e) => _adapter.Filter.InvokeFilter(e.NewText);
    
            _searchView.QueryTextSubmit += (s, e) =>
            {
                // Handle enter/search button on keyboard here
                Toast.MakeText(this, "Searched for: " + e.Query, ToastLength.Short).Show();
                e.Handled = true;
            };
    
            MenuItemCompat.SetOnActionExpandListener(item, new SearchViewExpandListener(_adapter));
    
            return true;
        }
    
        private class SearchViewExpandListener : Java.Lang.Object, MenuItemCompat.IOnActionExpandListener
        {
            private readonly IFilterable _adapter;
    
            public SearchViewExpandListener(IFilterable adapter)
            {
                _adapter = adapter;
            }
    
            public bool OnMenuItemActionCollapse(IMenuItem item)
            {
                _adapter.Filter.InvokeFilter("");
                return true;
            }
    
            public bool OnMenuItemActionExpand(IMenuItem item)
            {
                return true;
            }
        }
    
    1. Setting up the Adapter

    First, add a model class that will be used for this sample :

    public class Chemical
    {
        public string Name { get; set; }
    
        public int DrawableId { get; set; }
    }
    

    It's just your basic model which will display a text in the RecyclerView. This is the layout that display the layout:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:padding="5dp">
        <ImageView
            android:id="@+id/chemImage"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentLeft="true"
            android:layout_margin="5dp" />
        <TextView
            android:id="@+id/chemName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@+id/chemImage"
            android:layout_centerInParent="true"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp" />
     </RelativeLayout>
    

    This is the ViewHolder for the ChemicalHolder class:

        public class ChemicalHolder : RecyclerView.ViewHolder
        {
            public ImageView Image { get; private set; }
            public TextView Caption { get; private set; }
    
            public ChemicalHolder(View itemView) : base(itemView)
            {
                Image = itemView.FindViewById<ImageView>(Resource.Id.chemImage);
                Caption = itemView.FindViewById<TextView>(Resource.Id.chemName);
            }
        }
    

    3. Implementing the RecyclerView.Adapter

    public class RecyclerViewAdapter : RecyclerView.Adapter, IFilterable
    {
        private List<Chemical> _originalData;
        private List<Chemical> _items;
        private readonly Activity _context;
    
        public Filter Filter { get; private set; }
    
        public RecyclerViewAdapter(Activity activity, IEnumerable<Chemical> chemicals)
        {
            _items = chemicals.OrderBy(s => s.Name).ToList();
            _context = activity;
    
            Filter = new ChemicalFilter(this);
        }
    
        public override long GetItemId(int position)
        {
            return position;
        }
    
    
        public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
        {
            View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.Chemical, parent, false);
            ChemicalHolder vh = new ChemicalHolder(itemView);
            return vh;
        }
    
        public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
        {
            ChemicalHolder vh = holder as ChemicalHolder;
    
            var chemical = _items[position];
    
            vh.Image.SetImageResource(chemical.DrawableId);
            vh.Caption.Text = chemical.Name;
        }
    
        public override int ItemCount
        {
            get { return _items.Count; }
        }
    
        public class ChemicalHolder{...
    
        private class ChemicalFilter{//Implement the Filter logic
     }
    
    1. Implementing the filter logic

      private class ChemicalFilter : Filter
      {
          private readonly RecyclerViewAdapter _adapter;
          public ChemicalFilter(RecyclerViewAdapter adapter)
          {
              _adapter = adapter;
          }
      
          protected override FilterResults PerformFiltering(ICharSequence constraint)
          {
              var returnObj = new FilterResults();
              var results = new List<Chemical>();
              if (_adapter._originalData == null)
                  _adapter._originalData = _adapter._items;
      
              if (constraint == null) return returnObj;
      
              if (_adapter._originalData != null && _adapter._originalData.Any())
              {
                  // Compare constraint to all names lowercased. 
                  // It they are contained they are added to results.
                  results.AddRange(
                      _adapter._originalData.Where(
                          chemical => chemical.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)
                  _adapter._items = values.ToArray<Java.Lang.Object>()
                      .Select(r => r.ToNetObject<Chemical>()).ToList();
      
              _adapter.NotifyDataSetChanged();
      
              // Don't do this and see GREF counts rising
              constraint.Dispose();
              results.Dispose();
          }
      }
      
    2. Use it to implement this feature

          SetContentView(Resource.Layout.Main);
          SupportActionBar.SetDisplayShowHomeEnabled(true);
      
          var chemicals = new List<Chemical>
          {
              new Chemical {Name = "Niacin", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Biotin", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Chromichlorid", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Natriumselenit", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Manganosulfate", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Natriummolybdate", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Ergocalciferol", DrawableId = Resource.Drawable.Icon},
              new Chemical {Name = "Cyanocobalamin", DrawableId = Resource.Drawable.Icon},
          };
      
          _recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
          _adapter = new RecyclerViewAdapter(this,chemicals);
          _LayoutManager = new LinearLayoutManager(this);
          _recyclerView.SetLayoutManager(_LayoutManager);
          _recyclerView.SetAdapter(_adapter);//
      
    0 讨论(0)
提交回复
热议问题