I would like to have a multiselect spinner with a search option for it. I have already implemented a spinner with a search option. I would like some help with multiselect.
Here is my code:
Adapter class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ViewModels;
namespace Adapters
{
public class FilterableAdapter : ArrayAdapter, IFilterable
{
LayoutInflater inflater;
Filter filter;
Activity context;
public List<FilterableListViewModel> AllItems;
public List<FilterableListViewModel> MatchItems;
public FilterableAdapter(Activity context, int txtViewResourceId, List<FilterableListViewModel> 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 FilterableListViewModel 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.Drawable.filterList_view, null);
view.FindViewById<TextView>(Resource.Id.list_itemContent).Text = MatchItems[position].displayName;
//if (!string.IsNullOrEmpty(MatchItems[position].subItem))
//{
// view.FindViewById<TextView>(Resource.Id.list_subItemContent).Text = MatchItems[position].subItem;
//}
//else
//{
// view.FindViewById<TextView>(Resource.Id.list_subItemContent).Visibility = ViewStates.Gone;
//}
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<FilterableListViewModel>();
//var matches = _adapter.AllItems.Where(i => i.title.ToLower().Contains(searchFor.ToLower()) || string.IsNullOrEmpty(i.subText) ? 1 == 1 : i.subText.ToLower().Contains(searchFor.ToLower()));
var matches = _adapter.AllItems.Where(i => i.item.ToLower().Contains(searchFor.ToLower()) || (i.subItem != null && i.subItem.ToLower().Contains(searchFor.ToLower())));// !string.IsNullOrEmpty(i.subText) ? i.subText.ToLower().Contains(searchFor.ToLower()) : null);
foreach (var match in matches)
{
matchList.Add(match);
}
_adapter.MatchItems = matchList;
Console.WriteLine("resultCount:" + matchList.Count);
List<FilterableListViewModel> matchObjects = new List<FilterableListViewModel>();
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();
}
}
}
}
FilterableListViewModel.cs:
public class FilterableListViewModel
{
public string id { get; set; }
public string item { get; set; }
public string subItem { get; set; }
public string displayName { get; set; }
}
filterList_view.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/ListView_style">
<TextView
android:id="@+id/list_itemContent"
android:textColor="#000000"
android:text="sample"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginRight="@dimen/_15sdp"
android:layout_marginLeft="@dimen/_15sdp"
android:paddingTop="@dimen/_4sdp" />
</LinearLayout>
Activity:
[Activity(Label = "SuburbListActivity")]
public class SuburbListActivity : Activity
{
private FilterableAdapter _adapter;
private ListView _list;
private EditText _filterText;
private ISuburbs suburbInfo;
Android.App.ProgressDialog progress;
public ILog Log { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.filter_list);
ActionBar.SetHomeButtonEnabled(true);
ActionBar.SetDisplayHomeAsUpEnabled(true);
ActionBar.SetDisplayOptions(ActionBarDisplayOptions.ShowCustom, ActionBarDisplayOptions.ShowCustom);
// ActionBar.SetBackgroundDrawable(new ColorDrawable(Android.Graphics.Color.Black));
LayoutInflater inflater = (LayoutInflater)GetSystemService(Context.LayoutInflaterService);
View v = inflater.Inflate(Resource.Layout.layout_actionbar_centerTitle, null);
ActionBar.LayoutParams p = new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MatchParent,
ViewGroup.LayoutParams.MatchParent,
GravityFlags.Center);
var title = ((TextView)v.FindViewById(Resource.Id.title));
title.Text = "Select Suburb";
ActionBar.SetCustomView(v, p);
ActionBar.SetDisplayShowTitleEnabled(true);
ActionBar.SetDisplayHomeAsUpEnabled(true);
progress= new Android.App.ProgressDialog(this);
progress.SetProgressStyle(Android.App.ProgressDialogStyle.Spinner);
progress.SetMessage(WaitMessage);
progress.SetCancelable(false);
progress.Show();
InitializeComponant();
InitializeEvents();
BindData();
}
private void InitializeComponant()
{
suburbInfo = Dependencies.Container.Get<ISuburbs>();
Log = Dependencies.Container.Get<ILog>();
_list = FindViewById<ListView>(Resource.Id.filterList);
_filterText = FindViewById<EditText>(Resource.Id.search);
}
/// <summary>
/// initialize evnts
/// </summary>
private void InitializeEvents()
{
_list.ItemClick += List_ItemClick;
}
private void List_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var listItem = _adapter.GetMatchedItem(e.Position);
//var serviceRoleId = listItem.id;
//var hasPIC = listItem.subItem;
Intent myIntent = new Intent(this, typeof(RegisterActivity));
myIntent.PutExtra("suburbId", listItem.id);
myIntent.PutExtra("suburbName", listItem.item);
myIntent.PutExtra("postcode", listItem.subItem);
SetResult(Result.Ok, myIntent);
Finish();
}
private async Task BindData()
{
//var serviceRoleList = await serviceInfo.GetAllAsync();
RegistrationFacade regFacade = new RegistrationFacade(Log, null, null, suburbInfo);
List<FilterableListViewModel> states = await regFacade.GetSuburbs();
//List<FilterableListViewModel> testmodel = new List<FilterableListViewModel>();
//testmodel.Add(new FilterableListViewModel { subItem = "abc, goa, panjim", id = "1", item = "abc" });
//testmodel.Add(new FilterableListViewModel { subItem = "qwe, maha, vasco", id = "1", item = "qwe" });
//testmodel.Add(new FilterableListViewModel { subItem = "rty, karna, panjim", id = "1", item = "rty" });
//testmodel.Add(new FilterableListViewModel { subItem = "uio, assa, marg", id = "1", item = "ttt" });
_adapter = new FilterableAdapter(this, Resource.Drawable.filterList_view, states);
_list.Adapter = _adapter;
_filterText.TextChanged += (object sender, Android.Text.TextChangedEventArgs e) =>
{
var searchTerm = _filterText.Text;
if (String.IsNullOrEmpty(searchTerm))
{
_adapter.ResetSearch();
}
else
{
_adapter.Filter.InvokeFilter(searchTerm);
}
};
progress.Hide();
}
}
Any help on how I can modify my code to get multiselect list?
This is what I have already:
Here is my adapter after Umair's
suggestion:
public class FilterableMultiselectAdapter : ArrayAdapter, IFilterable
{
LayoutInflater inflater;
Filter filter;
Activity context;
public List<FilterableListViewModel> AllItems;
public List<FilterableListViewModel> MatchItems;
public FilterableMultiselectAdapter(Activity context, int txtViewResourceId, List<FilterableListViewModel> 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 FilterableListViewModel 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.Drawable.filterMultiselectList_view, null);
view.FindViewById<CheckedTextView>(Resource.Id.chkTV).Text = MatchItems[position].displayName;
return view;
}
public override Filter Filter
{
get
{
return filter;
}
}
public void ResetSearch()
{
MatchItems = AllItems;
NotifyDataSetChanged();
}
class SuggestionsFilter : Filter
{
readonly FilterableMultiselectAdapter _adapter;
public SuggestionsFilter(FilterableMultiselectAdapter 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<FilterableListViewModel>();
//var matches = _adapter.AllItems.Where(i => i.title.ToLower().Contains(searchFor.ToLower()) || string.IsNullOrEmpty(i.subText) ? 1 == 1 : i.subText.ToLower().Contains(searchFor.ToLower()));
var matches = _adapter.AllItems.Where(i => i.item.ToLower().Contains(searchFor.ToLower()) || (i.subItem != null && i.subItem.ToLower().Contains(searchFor.ToLower())));// !string.IsNullOrEmpty(i.subText) ? i.subText.ToLower().Contains(searchFor.ToLower()) : null);
foreach (var match in matches)
{
matchList.Add(match);
}
_adapter.MatchItems = matchList;
Console.WriteLine("resultCount:" + matchList.Count);
List<FilterableListViewModel> matchObjects = new List<FilterableListViewModel>();
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();
}
}
}
You can use CheckedTextView
for selecting multiple items from the spinner dropdown.
<?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">
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/chkTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="?android:attr/listChoiceIndicatorMultiple"
android:drawableStart="?android:attr/listChoiceIndicatorMultiple"
android:clickable="true"
android:focusable="true"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/action_settings"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold" />
</RelativeLayout>
and then in your adapter class's getView
method do something like this:
viewHolder.chkTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (viewHolder.chkTV.isChecked()) {
viewHolder.chkTV.setChecked(false);
if (mModel != null) {
addItemIntoUnCheckedList(mModel.getId());
}
} else {
viewHolder.chkTV.setChecked(true);
if (mModel != null) {
addItemIntoCheckedList(mModel.getId());
}
}
}
});
private void addItemIntoCheckedList(int id) {
Model phyFac = new Model();
phyFac.Id = ID;
// do your model work here for the checkedItem
if (UnCheckedByUser != null && UnCheckedByUser.size() > 0) {
for (int i = 0; i < UnCheckedByUser.size(); i++) {
if (UnCheckedByUser.get(i).getId() == phyFac.getId()) {
UnCheckedByUser.remove(i);
}
}
}
checkedListOfFacilities.add(phyFac);
}
private void addItemIntoUnCheckedList(int Id) {
Model unCheckedPhyFac = new Model();
unCheckedPhyFac.Id = ID;
unCheckedPhyFac.Id = Id;
if (checkedList != null && checkedList.size() > 0) {
for (int i = 0; i < checkedList.size(); i++) {
if (checkedList.get(i).getId() == unCheckedPhyFac.getId()) {
checkedList.remove(i);
}
}
}
UnCheckedByUser.add(unCheckedPhyFac);
}
Now at the end you will get both the unchecked and checked lists using this approach and you can do whatever you want to do with those lists.