2

I want to add autocomplete text-box to the list-view(custom object) in xamarin.android.

I have a listview which is being populated from string array. I would like to populate my listview using custom object.

The code below works but for string array. Any help to implement my adapter for custom object would help.

Here is the code:

Main.axml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <EditText
        android:id="@+id/search"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:hint="Enter search query" />
    <Button
        android:id="@+id/dosearch"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Filter list" />
    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

MainActivity

public class MainActivity : Activity
    {
        Button _button;
        ListView _listview;
        EditText _filterText;
        FilterableAdapter _adapter;

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

            // Set our view from the "main" layout resource
            SetContentView (Resource.Layout.Main);

            _button = FindViewById<Button> (Resource.Id.dosearch);
            _listview = FindViewById<ListView> (Resource.Id.list);
            _filterText = FindViewById<EditText> (Resource.Id.search);
            _adapter = new FilterableAdapter (this, Android.Resource.Layout.SimpleListItem1, GetItems());
            _listview.Adapter = _adapter;

            _button.Click += delegate {
                // filter the adapter here
                _adapter.Filter.InvokeFilter(_filterText.Text);
            };

            _filterText.TextChanged += (object sender, Android.Text.TextChangedEventArgs e) => {
                // filter on text changed
                var searchTerm = _filterText.Text;
                if (String.IsNullOrEmpty(searchTerm)) {
                    _adapter.ResetSearch();
                } else {
                    _adapter.Filter.InvokeFilter(searchTerm);
                }
            };
        }

        string[] GetItems ()
        {
           string[] names = new string[] { "abc", "xyz", "yyy", "aaa" };
           return names;
        }
    }

    public class FilterableAdapter : ArrayAdapter, IFilterable
    {
        LayoutInflater inflater;
        Filter filter;
        Activity context;
        public string[] AllItems;
        public string[] MatchItems;

        public FilterableAdapter (Activity context, int txtViewResourceId, string[] items) : base(context, txtViewResourceId, items)
        {
            inflater = context.LayoutInflater;
            filter = new SuggestionsFilter(this);
            AllItems = items;
            MatchItems = items;
        }

        public override int Count {
            get {
                return MatchItems.Length;
            }
        }

        public override Java.Lang.Object GetItem (int position)
        {
            return MatchItems[position];
        }

        public override View GetView (int position, View convertView, ViewGroup parent)
        {
            View view = convertView;
            if (view == null)
                view = inflater.Inflate(Android.Resource.Layout.SimpleDropDownItem1Line, null);

            view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = MatchItems[position];

            return view;
        }

        public override Filter Filter {
            get {
                return filter;
            }
        }

        public void ResetSearch() {
            MatchItems = AllItems;
            NotifyDataSetChanged ();
        }

        class SuggestionsFilter : Filter
        {
            readonly FilterableAdapter _adapter;

            public SuggestionsFilter (FilterableAdapter adapter) : base() {
                _adapter = adapter;
            }

            protected override Filter.FilterResults PerformFiltering (Java.Lang.ICharSequence constraint)
            {
                FilterResults results = new FilterResults();
                if (!String.IsNullOrEmpty (constraint.ToString ())) {
                    var searchFor = constraint.ToString ();
                    Console.WriteLine ("searchFor:" + searchFor);
                    var matchList = new List<string> ();

                    var matches = 
                        from i in _adapter.AllItems
                        where i.IndexOf (searchFor, StringComparison.InvariantCultureIgnoreCase) >= 0
                        select i;

                    foreach (var match in matches) {
                        matchList.Add (match);
                    }

                    _adapter.MatchItems = matchList.ToArray ();
                    Console.WriteLine ("resultCount:" + matchList.Count);

                    Java.Lang.Object[] matchObjects;
                    matchObjects = new Java.Lang.Object[matchList.Count];
                    for (int i = 0; i < matchList.Count; i++) {
                        matchObjects [i] = new Java.Lang.String (matchList [i]);
                    }

                    results.Values = matchObjects;
                    results.Count = matchList.Count;
                } else {
                    _adapter.ResetSearch ();
                }
                return results;
            }

            protected override void PublishResults (Java.Lang.ICharSequence constraint, Filter.FilterResults results)
            {
                _adapter.NotifyDataSetChanged();
            }
        }
    }

I would like to have my getItem method like this:

 public List<ListObject> GetItems()
 {
     List<ListObject> model = new List<ListObject>();
     model.Add(new ListObject { id = 1, Name = "abc" });
     model.Add(new ListObject { id = 1, Name = "bcd" });
     model.Add(new ListObject { id = 1, Name = "def" });
     model.Add(new ListObject { id = 1, Name = "fgh" });
     return model;
 }

The main reason for doing this is I want my list to be filtered based on the data typed in edittext. And then take the selected Id and do the remaining computation.

Kes Walker
  • 1,154
  • 2
  • 10
  • 24
Arti
  • 2,993
  • 11
  • 68
  • 121

2 Answers2

0

Filterable adapter with custom object

I write a demo about how to search through RecyclerView, in my demo I populate my RecyclerView with List<Chemical>. For more detail information, you could read my answer : Searching Through RecyclerView

My Chemical class :

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

When populate the RecyclerView :

var chemicals = new List<Chemical>
{
    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},
};

_adapter = new RecyclerViewAdapter(this,chemicals);

EDIT :

Since you are using ListView, you could refer to Cheesebaron's SearchView-Sample, I think this is what you want.

Don't forget to add the ObjectExtensions class in your project.

York Shen
  • 9,014
  • 1
  • 16
  • 40
0

I have found the desired result. I changed my adapter, this worked for me:

    public class FilterableAdapter : ArrayAdapter, IFilterable
    {
        LayoutInflater inflater;
        Filter filter;
        Activity context;
        public List<ListObject> AllItems;
        public List<ListObject> MatchItems;

        public FilterableAdapter(Activity context, int txtViewResourceId, List<ListObject> items) : base(context, txtViewResourceId, items)
        {
            inflater = context.LayoutInflater;
            filter = new SuggestionsFilter(this);
            AllItems = items;
            MatchItems = items;
        }

        public override int Count
        {
            get
            {
                return MatchItems.Count;
            }
        }

        public override Java.Lang.Object GetItem(int position)
        {
            return null;
        }

        public ListObject GetMatchedItem(int position)
        {
            return MatchItems[position];
        }


        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            View view = convertView;
            if (view == null)
                view = inflater.Inflate(Resource.Layout.custom_listView, null);

            view.FindViewById<TextView>(Resource.Id.list_content).Text = MatchItems[position].PICPropertyName;

            return view;
        }

        public override Filter Filter
        {
            get
            {
                return filter;
            }
        }

        public void ResetSearch()
        {
            MatchItems = AllItems;
            NotifyDataSetChanged();
        }

        class SuggestionsFilter : Filter
        {
            readonly FilterableAdapter _adapter;

            public SuggestionsFilter(FilterableAdapter adapter) : base()
            {
                _adapter = adapter;
            }

            protected override Filter.FilterResults PerformFiltering(Java.Lang.ICharSequence constraint)
            {
                FilterResults results = new FilterResults();
                if (!String.IsNullOrEmpty(constraint.ToString()))
                {
                    var searchFor = constraint.ToString();
                    Console.WriteLine("searchFor:" + searchFor);
                    var matchList = new List<ListObject>();
                    var matches = _adapter.AllItems.Where(i => i.name.Contains(searchFor));
                    foreach (var match in matches)
                    {
                        matchList.Add(match);
                    }

                    _adapter.MatchItems = matchList;
                    Console.WriteLine("resultCount:" + matchList.Count);

                    List<ListObject> matchObjects = new List<ListObject>();
                    for (int i = 0; i < matchList.Count; i++)
                    {
                        matchObjects.Add(matchList[i]);
                    }

                    results.Count = matchList.Count;
                }
                else
                {
                    _adapter.ResetSearch();
                }
                return results;
            }

            protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
            {
                _adapter.NotifyDataSetChanged();
            }
        }
    }

    public class ListObject {
        public int id { get; set; }
        public int name { get; set; }
    }
Arti
  • 2,993
  • 11
  • 68
  • 121