I had the exact same problem and I fixed it using a custom renderer. This lets the user type to filter the list choices and then puts the selected choice in the entry when the user picks one. In your forms project you'll want a new subclass of entry:
public class AutofillTextView : Entry
{
public static BindableProperty ChoicesSourceProperty = BindableProperty.Create(
nameof(ChoicesSource), typeof(ObservableCollection<string>), typeof(CustomPicker),
default(ObservableCollection<string>), propertyChanged: OnChoicesSourceChanged);
public ObservableCollection<string> ChoicesSource
{
get { return (ObservableCollection<string>)GetValue(ChoicesSourceProperty); }
set { SetValue(ChoicesSourceProperty, value); }
}
public AutofillTextView()
{
Items = new List<string>();
}
private void ChoicesChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Items.Add(args.NewItems[0]);
break;
default:
var itemsList = ChoicesSource.ToList();
Items = itemsList;
break;
}
OnPropertyChanged(nameof(Items));
}
private static void OnChoicesSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var textView = (AutofillTextView) bindable;
textView.ChoicesSource.CollectionChanged -= textView.ChoicesChanged;
textView.ChoicesSource.CollectionChanged += textView.ChoicesChanged;
textView.Items = newValue == null ? new List<string>() : ((ObservableCollection<string>)newValue).ToList();
}
}
You might need to expand on this, but I never remove items from the list so the switch was good enough for me. I needed to use observable collection as well, so you can simplify by switching to list. Anyway, next you need your custom renderer in droid:
[assembly: ExportRenderer(typeof(AutofillTextView), typeof(AutofillTextViewRenderer))]
namespace Default
{
public class AutofillTextViewRenderer : ViewRenderer<AutofillTextView, InstantAutoCompleteTextView>
{
protected override void OnElementChanged(ElementChangedEventArgs<AutofillTextView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var control = new InstantAutoCompleteTextView(Forms.Context);
control.InputType = Android.Text.InputTypes.ClassText | Android.Text.InputTypes.TextFlagNoSuggestions;
control.Text = Element.Text;
SetNativeControl(control);
}
if (e.OldElement != null)
{
}
if (e.NewElement != null)
{
var adapter = new ArrayAdapter<string>(Forms.Context as Android.App.Activity, Resource.Layout.AutofillTextViewItem, e.NewElement.Items.ToArray<string>());
Control.Adapter = adapter;
Control.Threshold = 0;
Control.TextChanged += Control_TextChanged;
adapter.NotifyDataSetChanged();
}
}
void Control_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
{
if (Element.Text != Control.Text)
{
Element.Text = Control.Text;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == AutofillTextView.ItemsProperty.PropertyName)
{
var adapter = new ArrayAdapter<string>(Forms.Context as Android.App.Activity, Resource.Layout.AutofillTextViewItem, Element.Items.ToArray<string>());
Control.Adapter = adapter;
adapter.NotifyDataSetChanged();
}
else if(e.PropertyName == AutofillTextView.TextProperty.PropertyName)
{
if(Control.Text != Element.Text)
{
Control.Text = Element.Text;
}
} else if(e.PropertyName == AutofillTextView.IsFocusedProperty.PropertyName) {
if(Element.IsFocused)
Control.PerformFiltering();
}
}
}
}
This will connect your custom class (AutofillTextView) to an android class (InstantAutoCompleteTextView) to replace the default entry renderer. The reason I don't use the built in AutoCompleteTextView is because it won't show the dropdown until you type at least one character. I'd prefer it shown on focus. If you don't care you can skip that. Here's that view:
public class InstantAutoCompleteTextView : AutoCompleteTextView
{
public InstantAutoCompleteTextView(Context context) : base(context) {}
public InstantAutoCompleteTextView(Context arg0, IAttributeSet arg1) : base(arg0, arg1){}
public InstantAutoCompleteTextView(Context arg0, IAttributeSet arg1, int arg2) : base(arg0, arg1, arg2){}
public override bool EnoughToFilter() {
return true;
}
public void PerformFiltering()
{
PerformFiltering(Text, 0);
}
}