【问题标题】:Xamarin.Forms Autocomplete CrossPlatformXamarin.Forms 自动完成跨平台
【发布时间】:2016-11-19 22:55:01
【问题描述】:

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


(来源:codetheory.in

您能否指导如何在 Xamarin.Forms 中实现它?我想用 Entry Control 来实现它

TIA

【问题讨论】:

标签: xamarin xamarin.forms xlabs xamarin.forms.labs


【解决方案1】:

你没有包含你想要的,只是某种自动完成。

我会为项目列表指出一般的手动方式:

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

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

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

here 中查找适用于 Android 的 AutoCompleteTextView。 查看 hereherehere 以获取有关 Xamarin 表单中自动完成功能的帮助。

【讨论】:

  • 我已经通过 AutoCompleteTextView 但 Xamarin.iOS 中没有提供 AutoComplete?你对此有什么想法吗?
  • @ManojSethi 这与手动方式中描述的原理相同。这些组件在 Xamarin.iOS 中也同样可用。 :)
【解决方案2】:

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

Google Place API with Autocomplete in Xamarin Android

Xamarin.iOS Location Autocomplete by using Google Place API

【讨论】:

  • 抱歉,这不是 Xamarin 表单,这是问题所在。都是原生安卓。
  • 它与自定义渲染器有关,因此它也与 Xamarin.Forms 相关。
【解决方案3】:

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

【讨论】:

  • NestedScrollView 在哪里?仅适用于 Android 或 PCL?
  • 啊...您应该创建一个自定义视图 NestedScrollView,并为其创建渲染器(仅限 Android)。然后,设置原生属性 NestedScrollingEnabled = true。(请参阅developer.xamarin.com/api/property/…)。如果您没有此类,则无法滚动自动完成的选择列表。
  • @Quý Nguyễn Nam,你有 NestedScrollView 的代码
  • @QuýNguyễnNam 字符串扩展方法“ConvertToNonMark”应该做什么?根据谷歌的说法,你是唯一一个拥有这个名字的函数的人!
  • @Wes "ConvertToNonMark" 是我的功能(您可以忽略它或需要定义您的语言是否包含标记(例如:法国、越南语...) NestedScrollView 是从 ScrollView 扩展的自定义 XF 控件. 在Android中(iOS中不需要),你需要创建一个继承自ScrollViewRenderer的Ne​​stedScrollViewRenderer类,然后设置NestedScrollingEnabled = true。就是这样!你可以选择链接:developer.xamarin.com/api/property/…
【解决方案4】:

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

【讨论】:

  • 不错的插件!但是iOS版本看起来有点奇怪。 :)
  • 自那以后发生了很多变化,但请随时在 github 存储库中记录问题,并详细说明您希望看到的内容,我们可以从那里获取。
【解决方案5】:

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

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

【讨论】:

【解决方案6】:

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

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

我遇到了一个问题,即 autocompleteview 中的文本不会从源绑定更新或使用 https://github.com/XLabs/Xamarin-Forms-Labs autocompleteview 从后面的代码设置。这会暂时排除显示建议的障碍

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

【讨论】:

    【解决方案7】:

    我正在使用这个库SupportWidgetXF

    它是跨平台的。

    【讨论】:

    • 你能再解释一下吗?
    猜你喜欢
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 2018-02-20
    • 2017-12-15
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多