Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
202 views
Welcome To Ask or Share your Answers For Others

1 Answer

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...