Skip to content

Custom Suggestions Adapter

Federico Navarrete edited this page Aug 15, 2019 · 17 revisions

Prerequisites:

Basic implementation tutorial


Step 1. Define your data model.

public class Product
{
    public string Title { get; set; }
    public string SubTitle { get; set; }

    public Product(string title, string subTitle)
    {
        Title = title;
        SubTitle = subTitle;
    }
}

Step 2. Create a ViewHolder.

This class inherits from the RecyclerView.ViewHolder. The ViewHolder will perform the same functions as in RecyclerView. If you have some doubts about it, you can check it here.

public class SuggestionHolder : RecyclerView.ViewHolder
{
    public TextView Title { get; set; }
    public TextView SubTitle { get; set; }
    public ImageView ImgThumbnail { get; set; }

    public SuggestionHolder(View itemView) : base(itemView)
    {
        Title = itemView.FindViewById<TextView>(Resource.Id.title);
        SubTitle = itemView.FindViewById<TextView>(Resource.Id.subtitle);
        ImgThumbnail = itemView.FindViewById<ImageView>(Resource.Id.imgThumbnail);
        itemView.Click += ItemView_Click;
    }

    private void ItemView_Click(object sender, System.EventArgs e)
    {

    }
}

Step 3. Create an XML Template.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	android:orientation="horizontal" android:layout_width="match_parent"
	android:padding="16dp"
	android:layout_height="80dp"
	android:background="?attr/selectableItemBackground"
	android:clickable="true">

	<ImageView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/imgThumbnail" />

	<LinearLayout
		android:orientation="vertical"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:layout_marginLeft="16dp">

		<TextView
			android:text="TextView"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:id="@+id/title"
			android:textAppearance="@style/TextAppearance.AppCompat.Medium"
			android:textColor="@android:color/black"
			android:textStyle="normal|bold" />

		<TextView
			android:text="TextView"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:id="@+id/subtitle"
			android:layout_marginTop="4dp" />
	</LinearLayout>
</LinearLayout>

Step 4. Create an Adapter

This adapter is going to inherit from the SuggestionsAdapter and it possesses the following methods to override:

  • GetSingleViewHeight()
  • OnCreateViewHolder(...)
  • OnBindSuggestionHolder(...)

And other public methods, such as:

  • AddSuggestion(string r)
  • SetSuggestions(List<string> suggestions)
  • ClearSuggestions()
  • DeleteSuggestion(int position, string r)

This is an example of the class to implement:

public partial class CustomSuggestionsAdapter : SuggestionsAdapter
{
    public CustomSuggestionsAdapter(LayoutInflater inflater) : base(inflater)
    {

    }

    public override int GetSingleViewHeight()
    {
        return 0;
    }

    public override void OnBindSuggestionHolder(string suggestion, RecyclerView.ViewHolder holder, int position)
    {

    }

    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        return null;
    }
}

Now, you need to define the following actions:

1. Copy your Data Model.

private List<Product> Products { get; set; }

The data can be assigned as follow:

public void UpdateSuggestions(List<Product> products)
{
    Products = products;
}

This must be triggered after the SuggestionsAdapter has been initialized.

2. OnCreateViewHolder - in this method you need to create and return an instance of your SuggestionHolder.

public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
    using (var view = Inflater.Inflate(Resource.Layout.item_custom_suggestion, parent, false))
    {
        return new SuggestionHolder(view);
    }
}

3. OnBindSuggestionHolder - this method is used to get the data and from the suggestions and bind them to the SuggestionsHolder. Also, you need to cast your ViewHolder to your type.

public override void OnBindSuggestionHolder(string suggestion, RecyclerView.ViewHolder holder, int position)
{
    var sH = holder as SuggestionHolder;

    var currentProduct = Products.First(x => x.Title == suggestion);

    sH.Title.Text = currentProduct.Title;
    sH.SubTitle.Text = currentProduct.SubTitle;
}

4. GetSingleViewHeight - this method is used by the MaterialSearchBar to calculate the total height of the suggestions list. Note that View-the elements in this list must have the same height. Imagine that our View has a height 60dp:

public override int GetSingleViewHeight()
{
    return 60;
}

Step 5. Define the Filter.

This class is used for filtering the data to display.

public partial class CustomSuggestionsAdapter
{
    public class CustomFilter : Filter
    {
        public CustomSuggestionsAdapter Adapter { get; set; }
        private List<string> Suggestions_backup { get; set; }

        public CustomFilter(CustomSuggestionsAdapter adapter)
        {
            Adapter = adapter;
        }

        protected override FilterResults PerformFiltering(ICharSequence constraint)
        {
            if (Suggestions_backup == null)
            {
                Suggestions_backup = Suggestions_clone;
            }

            FilterResults results = new FilterResults();
            string term = constraint.ToString();

            if (!string.IsNullOrEmpty(term))
            {
                Suggestions = Suggestions_backup.Where(x => x.ToLower().Contains(term.ToLower())).Distinct().ToList();
            }
            else
            {
                Suggestions = Suggestions_backup;
            }
            results.Values = GetJavaObjects(Suggestions);
            return results;
        }

        private Object[] GetJavaObjects(List<string> suggestions)
        {
            Object[] resultsValues = new Object[suggestions.Count()];
            for (var i = 0; i < suggestions.Count; i++)
            {
                resultsValues[i] = suggestions[i];
            }
            return resultsValues;
        }

        protected override void PublishResults(ICharSequence constraint, FilterResults results)
        {
            Suggestions.Clear();
            foreach (var item in (Object[])results.Values)
            {
                if (!string.IsNullOrEmpty(item.ToString()))
                {
                    Suggestions.Add(item.ToString());
                }
            }
            Adapter.SetSuggestions(Suggestions);

            Adapter.NotifyDataSetChanged();
        }
    }
}

Step 6. Setting up MaterialSearchBar

private static CustomSuggestionsAdapter CustomSuggestionsAdapter { get; set; }

private List<Product> Products = new List<Product>() {
	new Product("Simvastatin", "Red"),
	new Product("Carrot Daucus carota", "Orange"),
	new Product("Sodium Fluoride", "Orange"),
	new Product("White Kidney Beans", "Mandarin"),
	new Product("Salicylic Acid", "Green"),
	new Product("cetirizine hydrochloride", "Purple"),
	new Product("Mucor racemosus", "Red"),
	new Product("Thymol", "Brown"),
	new Product("TOLNAFTATE", "Brown"),
	new Product("Albumin Human", "Red")
};

private MaterialSearchBar MSearchBar { get; set; }

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

    MSearchBar = FindViewById<MaterialSearchBar>(Resource.Id.searchBar);

    MSearchBar.SetOnSearchActionListener(this);

    MSearchBar.AddTextChangeListener(new MaterialSearchBarListener());

    LayoutInflater inflater = (LayoutInflater)GetSystemService(LayoutInflaterService);
    CustomSuggestionsAdapter = new CustomSuggestionsAdapter(inflater);
    CustomSuggestionsAdapter.Filter = new CustomFilter(CustomSuggestionsAdapter);

    List<string> suggestions = Products.Select(x => x.Title).ToList();
    CustomSuggestionsAdapter.SetSuggestions(suggestions);
    CustomSuggestionsAdapter.UpdateSuggestions(Products);

    MSearchBar.SetCustomSuggestionAdapter(CustomSuggestionsAdapter);
}

private class MaterialSearchBarListener : Java.Lang.Object, ITextWatcher
{
    public void AfterTextChanged(IEditable s)
    {

    }

    public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
    {

    }

    public void OnTextChanged(ICharSequence s, int start, int before, int count)
    {
        CustomSuggestionsAdapter.Filter.InvokeFilter(s);
    }
}

Clone this wiki locally