15

我是 Xamarin.Forms 平台的新手。我希望你能帮助我继续前进。我想在 xamarin.forms 中有一个类似自动完成的控件,如下所示

自动完成
(来源:codetheory.in

您能否指导如何在 Xamarin.Forms 中实现它?我想用入口控制来实现它

TIA

4

7 回答 7

11

您还没有包含您想要的内容,只是某种自动完成功能。

对于项目列表,我将指出手动执行此操作的一般方法:

  1. 使用 TextBox 允许用户输入文本。
  2. 使用列表收集所有对象及其可搜索属性,例如对象名称。
  3. 当用户在 TextBox 中键入内容时,应用程序应在 List 中搜索在 TextBox 中输入的字符串。
  4. 根据键入的字符串值,建议应显示在 TextBox 下的 ListView 中。
  5. 用户单击 ListView 项目,这是一个建议,然后通过将对象名称从单击的项目中获取到 TextBox 来自动完成。

在没有上述冗长粗略过程的情况下进行自动完成的一般方法是使用 Android AutoCompleteTextView。

您仍然可以使用基本逻辑在 Xamarin Forms 中执行此操作。

在此处查找适用于 Android 的 AutoCompleteTextView。Xamarin Forms中查看自动完成功能帮助

于 2016-07-17T14:28:01.357 回答
6

我有一个可用于 iOS、Android 和 UWP 的 Xamarin.Forms 自定义控件。它使用自定义渲染器在幕后提供原生 UI。我之所以构建它,是因为我没有找到任何提供良好原生体验的控件,并且在打开下拉菜单时没有更改控件的高度。NuGet 包的所有 doc + 参考可在此处获得: https ://github.com/dotMorten/XamarinFormsControls/tree/master/AutoSuggestBox

于 2018-07-01T03:49:32.073 回答
2

我在我的项目中实现了一个 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

于 2017-06-24T10:00:40.703 回答
1

请阅读这些文章并尝试使用自定义渲染器在 Xamarin.Forms 上实施解决方案。

Xamarin Android 中具有自动完成功能的 Google Place API

使用 Google Place API 自动完成 Xamarin.iOS 位置

于 2016-07-17T15:15:18.687 回答
0

您可以使用 SyncFusion AutoComplete 插件轻松实现此目的。这为您提供了各种选择,而不是进行自定义渲染。

参考:https ://help.syncfusion.com/xamarin/sfautocomplete/getting-started

于 2018-07-02T05:20:51.920 回答
0

我试图按照 Imdad 的回答建立自己的建议/自动完成功能。我的标准阻碍了我,当建议填充列表视图时,它必须显示在顶部或展开。没有列表视图永久占用空间。

您可以尝试https://github.com/XamFormsExtended/Xfx.Controls 但我遇到了一些问题。它显示在顶部

我遇到了一个问题,即自动完成视图中的文本不会从源绑定更新或使用https://github.com/XLabs/Xamarin-Forms-Labs自动完成视图从后面的代码中设置。这推动了暂时显示建议的方式

我亲自去了这个解决方案https://github.com/dotMorten/XamarinFormsControls/tree/master/AutoSuggestBox

于 2018-10-19T15:14:48.640 回答
0

我正在使用这个库SupportWidgetXF

它是跨平台的。

于 2019-05-06T09:57:12.647 回答