Для одного из текущих проектов понадобилось сделать контрол для множественного выбора элементов из списка.
Делать это отдельной Activity как-то не хотелось, но в Android нет готового компонента для этого. Максимум, что можно сделать, это использовать AlertDialog с множественным выбором. Обычный же Spinner позволяет выбрать только один элемент.
И вот, после гугления было найдено неплохое решение на StackOverflow, которое позволяет совместить внешний вид Spinner’а и функционал AlertDialog’а. Решение для Java можно посмотреть здесь. Это решение мне не очень понравилось из-за того, что работает только со строками + необходимо использовать дополнительный метод для установки элементов списка для выбора, вместо того, чтобы нормально испольщовать Adapter для этого.
После небольших допиливаний получился контрол, который, все-таки, можно использовать с адаптером. Правда, при этом теряется возможность хранить ссылку на объект адаптера где-то снаружи контрола и делать ему NotifyDataSetChanged()
и NotifyDataSetInvalidated()
, но для спиннера это очень редко используемый функционал, так что можно назвать решение приемлемым.
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 Android.Util; namespace SampleWidgets.Android.Views { public class MultiSpinner : Spinner, IDialogInterfaceOnMultiChoiceClickListener, IDialogInterfaceOnCancelListener { public class MultiSpinnerSelectionEventArgs : EventArgs { public MultiSpinnerSelectionEventArgs(bool[] selected) { Selected = selected; } public bool[] Selected { get; private set; } } public delegate void ItemsSelectedHandler(object sender, MultiSpinnerSelectionEventArgs args); public event ItemsSelectedHandler ItemsSelected; private ISpinnerAdapter RealAdapter; private bool[] selected; public MultiSpinner(IntPtr a, JniHandleOwnership b) : base(a, b) { } public MultiSpinner(Context context) : base(context) { } public MultiSpinner(Context context, IAttributeSet attrs) : base(context, attrs) { } public MultiSpinner(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) { } public void OnClick(IDialogInterface dialog, int which, bool isChecked) { selected[which] = isChecked; } private ISpinnerAdapter CreateLabelAdapter() { List<string> names = new List<string>(); int count = RealAdapter != null ? RealAdapter.Count : 0; for (int i = 0; i < count; i++) { if (selected[i]) names.Add(RealAdapter.GetItem(i).ToString()); } string label = string.Join(", ", names); if (label.Length == 0) label = Context.GetString(Resource.String.LabelNone); return new ArrayAdapter<string>(Context, Android.Resource.Layout.sherlock_spinner_item, new string[] { label }); } public void OnCancel(IDialogInterface dialog) { base.Adapter = CreateLabelAdapter(); if (ItemsSelected != null) ItemsSelected(this, new MultiSpinnerSelectionEventArgs(selected)); } public override bool PerformClick() { AlertDialog.Builder builder = new AlertDialog.Builder(Context); List<string> names = new List<string>(); int count = RealAdapter != null ? RealAdapter.Count : 0; if (count > 0) { for (int i = 0; i < count; i++) { names.Add(RealAdapter.GetItem(i).ToString()); } builder.SetMultiChoiceItems(names.ToArray(), selected, this); builder.SetPositiveButton(global::Android.Resource.String.Ok, delegate(object o, DialogClickEventArgs e) { (o as AlertDialog).Cancel(); }); builder.SetOnCancelListener(this); builder.Show(); } return true; } public override ISpinnerAdapter Adapter { get { return RealAdapter; } set { selected = new bool[value.Count]; RealAdapter = value; base.Adapter = CreateLabelAdapter(); } } } }
В коде можно заметить странного вида конструктор public MultiSpinner(IntPtr a, JniHandleOwnership b)
. Без него Mono for Android не хочет компилировать этот компонент т.к. в нем переопределены виртуальные методы и свойства. Решение проблемы с компиляцией было найдено на том же StackOverflow в этом топике. Ответ, который помечен как решение проблемы, хоть и описывает суть проблемы, но не предлагает реального решения. Зато добавление конструктора реально помогает.