简体   繁体   中英

Xamarin Forms Layout create overlapping views

I am trying to create an Autocomplete control in Xamarin.Forms , so far I have created a ContentView with following Xaml.

<ContentView.Content>
        <AbsoluteLayout BackgroundColor="Red" x:Name="absLayout" HeightRequest="30">
            <Entry BackgroundColor="Green" x:Name="entryView" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All" Focused="Handle_Focused" Unfocused="Handle_Unfocused"/>
            <ListView BackgroundColor="Blue" Margin="0,30,0,0" IsVisible="false" x:Name="listView" HeightRequest="200">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Text="{Binding .}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </AbsoluteLayout>
    </ContentView.Content>

This will be used on ContentPage , along with other Entry and Label controls.

When someone clicks on Entry then, corresponding ListView must be shown to user just below the Entry , by overlapping other controls, instead of pushing down other controls below it.

I have achieved overlapping of controls, but not correctly. ListView does shows list to user but other controls overlap it. ListView gets a lower Z-Index and other controls get higher Z-Index.

Need help to fix this.

Edit 1-

Screens for what I want to achieve. (This a Spinner control in Android) 单击控件之前 单击控件后 I want to do something similar with my control.

PS - follow this SO answer for root cause this behaviour.

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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM