web-dev-qa-db-ja.com

Xamarin.Formsオートコンプリートクロスプラットフォーム

私はXamarin.Formsプラットフォームの初心者です。私が前進するのを助けてくれることを願っています。以下のようなxamarin.formsにオートコンプリートのようなコントロールが欲しい

Autocomplete

Xamarin.Formsでそれをどのように実現できるかをご案内いただけますか?エントリーコントロールで実現したい

TIA

12
Manoj Sethi

希望どおりのものが含まれていません。ある種のオートコンプリートが含まれています。

アイテムのリストに対して手動で行う一般的な方法を箇条書きにします。

  1. TextBoxを使用して、ユーザーがテキストを入力できるようにします。
  2. リストを使用して、オブジェクト名などの検索可能なプロパティとともにすべてのオブジェクトを収集します。
  3. ユーザーがTextBoxに何かを入力すると、アプリはTextBoxに入力された文字列をリストで検索する必要があります。
  4. 入力された文字列値に従って、候補がTextBoxの下のListViewに表示されます。
  5. ユーザーが提案であるListView項目をクリックすると、クリックされた項目からオブジェクトNameを取得して、TextBoxにオートコンプリートします。

上記の大まかな手順なしでオートコンプリートを行う一般的な方法は、Android AutoCompleteTextViewを使用することです。

Xamarinフォームでは、基本的なロジックを使用してそれを行うことができます。

AndroidのAutoCompleteTextViewについては こちら を参照してください。 Xamarinフォームのオートコンプリートのヘルプについては、 ここここ および ここ を参照してください。

8
Imdad

プロジェクトにAutocompleteViewを実装しました。参考にできます。

public class AutoCompleteView : ContentView
{
    public static readonly BindableProperty SuggestionsProperty = BindableProperty.Create(nameof(Suggestions), typeof(IEnumerable), typeof(AutoCompleteView), null, BindingMode.OneWay, null, OnSuggestionsChanged);
    public static readonly BindableProperty SearchTextProperty = BindableProperty.Create(nameof(SearchText), typeof(string), typeof(AutoCompleteView), null, BindingMode.TwoWay, null, OnSearchTextChanged);
    public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(AutoCompleteView), null, BindingMode.OneWay, null, OnPlaceholderChanged);
    public static readonly BindableProperty MaximumVisibleSuggestionItemsProperty = BindableProperty.Create(nameof(MaximumVisibleSuggestionItems), typeof(int), typeof(AutoCompleteView), 4);
    public static readonly BindableProperty SuggestionItemTemplateProperty = BindableProperty.Create(nameof(SuggestionItemTemplate), typeof(DataTemplate), typeof(AutoCompleteView), null, BindingMode.OneWay, null, OnSuggestionItemTemplateChanged);
    public static readonly BindableProperty DisplayPropertyNameProperty = BindableProperty.Create(nameof(DisplayPropertyName), typeof(string), typeof(AutoCompleteView));

    public IEnumerable Suggestions
    {
        get { return (IEnumerable)GetValue(SuggestionsProperty); }
        set { SetValue(SuggestionsProperty, value); }
    }

    public string SearchText
    {
        get { return (string)GetValue(SearchTextProperty); }
        set { SetValue(SearchTextProperty, value); }
    }

    public string Placeholder
    {
        get { return (string)GetValue(PlaceholderProperty); }
        set { SetValue(PlaceholderProperty, value); }
    }

    public int MaximumVisibleSuggestionItems
    {
        get { return (int)GetValue(MaximumVisibleSuggestionItemsProperty); }
        set { SetValue(MaximumVisibleSuggestionItemsProperty, value); }
    }

    public DataTemplate SuggestionItemTemplate
    {
        get { return (DataTemplate)GetValue(SuggestionItemTemplateProperty); }
        set { SetValue(SuggestionItemTemplateProperty, value); }
    }

    public string DisplayPropertyName
    {
        get { return (string)GetValue(DisplayPropertyNameProperty); }
        set { SetValue(DisplayPropertyNameProperty, value); }
    }

    public ItemsStack SuggestionsListView { get; private set; }
    public Entry SearchEntry { get; private set; }
    public IEnumerable OriginSuggestions { get; private set; }
    public NestedScrollView SuggestionWrapper { get; private set; }
    public Grid Container { get; private set; }

    public bool IsSelected { get; private set; }
    public int TotalNumberOfTypings { get; private set; }

    private static void OnSuggestionsChanged(object bindable, object oldValue, object newValue)
    {
        var autoCompleteView = bindable as AutoCompleteView;

        var suggestions = (IEnumerable)newValue;
        autoCompleteView.OriginSuggestions = suggestions;

        suggestions = autoCompleteView.FilterSuggestions(suggestions, autoCompleteView.SearchText);
        autoCompleteView.SuggestionsListView.ItemsSource = suggestions;
    }

    private static void OnSearchTextChanged(object bindable, object oldValue, object newValue)
    {
        var autoCompleteView = bindable as AutoCompleteView;

        var suggestions = autoCompleteView.OriginSuggestions;
        if (newValue != null)
        {
            suggestions = autoCompleteView.FilterSuggestions(suggestions, autoCompleteView.SearchText);
            // assign when initializing with data
            if (autoCompleteView.SearchEntry.Text != autoCompleteView.SearchText)
            {
                autoCompleteView.SearchEntry.Text = autoCompleteView.SearchText;
            }
        }
        autoCompleteView.SuggestionsListView.ItemsSource = suggestions;

        if (Device.OS == TargetPlatform.Android)
        {
            // update the layout -> only do this when user is typing instead of selection an item from suggestions list 
            // -> prevent duplicated update layout
            if (!autoCompleteView.IsSelected)
            {
                autoCompleteView.UpdateLayout();
            }
            else
            {
                autoCompleteView.IsSelected = false;
            }
        }
    }

    private static void OnSuggestionItemTemplateChanged(object bindable, object oldValue, object newValue)
    {
        var autoCompleteView = bindable as AutoCompleteView;

        if (autoCompleteView.SuggestionsListView != null)
        {
            autoCompleteView.SuggestionsListView.ItemTemplate = autoCompleteView.SuggestionItemTemplate;
        }
    }

    public IEnumerable FilterSuggestions(IEnumerable suggestions, string keyword)
    {
        if (string.IsNullOrEmpty(keyword) || suggestions == null) return suggestions;

        var searchWords = keyword.ConvertToNonMark().ToLower().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
        var result = suggestions.Cast<object>();
        foreach (var item in searchWords)
        {
            if (!string.IsNullOrEmpty(DisplayPropertyName))
            {
                result = result.Where(x => x.GetType().GetRuntimeProperty(DisplayPropertyName).GetValue(x).ToString().ConvertToNonMark().ToLower().Contains(item)).ToList();
            }
            else
            {
                result = result.Where(x => x.ToString().ConvertToNonMark().ToLower().Contains(item)).ToList();
            }
        }

        return result;
    }

    private static void OnPlaceholderChanged(object bindable, object oldValue, object newValue)
    {
        var autoCompleteView = bindable as AutoCompleteView;
        autoCompleteView.SearchEntry.Placeholder = newValue?.ToString();
    }

    public void UpdateLayout()
    {
        var expectedHeight = this.getExpectedHeight();
        Container.HeightRequest = expectedHeight;
        Container.ForceLayout();
    }

    private void SearchEntry_TextChanged(object sender, TextChangedEventArgs e)
    {
        TotalNumberOfTypings++;
        Device.StartTimer(TimeSpan.FromMilliseconds(1000), () => {
            TotalNumberOfTypings--;
            if (TotalNumberOfTypings == 0)
            {
                SearchText = e.NewTextValue;
            }
            return false;
        });
    }

    private void SearchEntry_Focused(object sender, FocusEventArgs e)
    {
        UpdateLayout();
        IsSelected = false;
    }

    private void SearchEntry_Unfocused(object sender, FocusEventArgs e)
    {
        Container.HeightRequest = 50;
        Container.ForceLayout();
    }

    private void SuggestionsListView_ItemSelected(object sender, ItemTappedEventArgs e)
    {
        IsSelected = true;
        SearchEntry.Text = !string.IsNullOrEmpty(DisplayPropertyName) ? e.Item?.GetType()?.GetRuntimeProperty(DisplayPropertyName)?.GetValue(e.Item)?.ToString() : e.Item?.ToString();
        Container.HeightRequest = 50;
        Container.ForceLayout();
    }

    private void OverlapContentView_Tapped(object sender, TappedEventArgs e)
    {
        UpdateLayout();
        IsSelected = false;

     }

    private int getExpectedHeight()
    {
        var items = SuggestionsListView.ItemsSource as IList;
        int wrapperHeightRequest = items != null ?
            (items.Count >= MaximumVisibleSuggestionItems ? MaximumVisibleSuggestionItems * 40 : items.Count * 40) : 0;
        if (Device.OS == TargetPlatform.Android)
        {
            return wrapperHeightRequest + 50;
        }
        return MaximumVisibleSuggestionItems * 40 + 50;
    }

    public AutoCompleteView()
    {
        Container = new Grid();
        SearchEntry = new Entry();
        SuggestionsListView = new ItemsStack();
        SuggestionWrapper = new NestedScrollView();

        // init Grid Layout
        Container.RowSpacing = 0;
        Container.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star });
        Container.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Star });
        Container.RowDefinitions.Add(new RowDefinition() { Height = 50 });
        Container.HeightRequest = 50;

        // init Search Entry
        SearchEntry.HorizontalOptions = LayoutOptions.Fill;
        SearchEntry.VerticalOptions = LayoutOptions.Fill;
        SearchEntry.TextChanged += SearchEntry_TextChanged;
        SearchEntry.Unfocused += SearchEntry_Unfocused;
        SearchEntry.Focused += SearchEntry_Focused;

        // init Suggestions ListView
        SuggestionsListView.BackgroundColor = Color.White;
        SuggestionsListView.ItemTapped += SuggestionsListView_ItemSelected;
        SuggestionsListView.VerticalOptions = LayoutOptions.End;
        SuggestionsListView.Spacing = 1;

        // suggestions Listview's wrapper
        SuggestionWrapper.VerticalOptions = LayoutOptions.Fill;
        SuggestionWrapper.Orientation = ScrollOrientation.Vertical;
        SuggestionWrapper.BackgroundColor = Color.White;
        SuggestionWrapper.Content = SuggestionsListView;

        Container.Children.Add(SuggestionWrapper);
        Container.Children.Add(SearchEntry, 0, 1);

        this.Content = Container;
    }
}

使用例:

<customControls:AutoCompleteView SearchText="{Binding User.UniversitySchool}" Suggestions="{Binding Schools}" DisplayPropertyName="Name" Placeholder="Please choose your school">
                    <customControls:AutoCompleteView.SuggestionItemTemplate>
                        <DataTemplate>
                            <ContentView Padding="10">
                                <Label Text="{Binding Name}" HeightRequest="20" LineBreakMode="HeadTruncation" Style="{StaticResource MainContentLabel}" />
                            </ContentView>
                        </DataTemplate>
                    </customControls:AutoCompleteView.SuggestionItemTemplate>
                </customControls:AutoCompleteView>

このビューでは、ItemStackコントロールを使用しました。これを参照できます: https://Gist.github.com/NVentimiglia/2723411428cdbb72fac6

2

IOSで動作するXamarin.Formsカスタムコントロールがあります。AndroidおよびUWP。カスタムレンダラーを使用して、カバーの下にネイティブUIを提供します。優れたネイティブエクスペリエンスを提供し、ドロップダウンが開いたときにコントロールの高さを変更しなかったすべてのコントロール。NuGetパッケージへのすべてのドキュメント+リファレンスは、こちらから入手できます: https:// github .com/dotMorten/XamarinFormsControls/tree/master/AutoSuggestBox

1
dotMorten

これらの記事を読み、カスタムレンダラーを使用してXamarin.Formsにソリューションを実装してみてください。

Xamarin Androidのオートコンプリートを使用したGoogle Place API

Google Place APIを使用したXamarin.iOS Location Autocomplete

1

このライブラリを使用しています SupportWidgetXF

クロスプラットフォームです。

0
Dev

私は、Imdadの回答に従って、独自の提案/オートコンプリートを作成しようとしました。候補がリストビューに表示されたときに上部に表示するか、展開する必要があるという私の基準に妨げられました。スペースを永続的に使用するリストビューはありません。

あなたは試すことができます https://github.com/XamFormsExtended/Xfx.Controls しかし、私はそれにいくつかの問題を経験しました。上に表示

Autocompleteviewのテキストがソースバインディングから更新されない、または https://github.com/XLabs/Xamarin-Forms-Labs autocompleteviewでコードビハインドから設定されないという問題が発生しました。これにより、一時的に邪魔なものが押し出され、提案が表示されます。

私は個人的にこのソリューションを使いました https://github.com/dotMorten/XamarinFormsControls/tree/master/AutoSuggestBox

0
lolelo

SyncFusion AutoCompleteプラグインでこれを簡単に実現できます。これにより、カスタムレンダリングを実行するのではなく、実行するさまざまなオプションが提供されます。

リファレンス: https://help.syncfusion.com/xamarin/sfautocomplete/getting-started