Xamarin Forms ListView 滚动 UI 问题

Xamarin Forms ListView scroll UI problems

我将使用 Xamarin 表单创建带有预定义消息的 ChatView 之类的东西。当我点击屏幕时出现新消息(它只是添加到 Messages Observable 集合中)

我有下一个自定义单元格 (BubbleCell)

<ViewCell x:Class="stori.es.Pages.Chat.Views.BubbleCell"
      xmlns="http://xamarin.com/schemas/2014/forms"
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:controls="clr-namespace:App.Platform.Forms.Controls;assembly=App.Platform"
      x:Name="Root">
<ViewCell.View>
    <AbsoluteLayout Padding="10"
                    HorizontalOptions="FillAndExpand"
                    VerticalOptions="FillAndExpand">

        <controls:ExtendedStackLayout x:Name="MessageLayout"
                                      Padding="10"
                                      AbsoluteLayout.LayoutBounds="0,0,1,1"
                                      AbsoluteLayout.LayoutFlags="All"
                                      BorderRadius="15">

            <Label x:Name="AuthorLabel"
                   FontSize="10"
                   HorizontalOptions="EndAndExpand"
                   VerticalOptions="StartAndExpand" />

            <Grid HorizontalOptions="FillAndExpand">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Label x:Name="TextLabel"
                       FontSize="16"
                       HorizontalOptions="FillAndExpand"
                       VerticalOptions="FillAndExpand" />
            </Grid>

        </controls:ExtendedStackLayout>

    </AbsoluteLayout>

</ViewCell.View>

后面有下一个代码:

public partial class BubbleCell : ViewCell
{
    public static readonly BindableProperty IsIncomingProperty =
        BindableProperty.Create(nameof(IsIncoming), typeof(bool), typeof(BubbleCell), true);

    public static readonly BindableProperty TextProperty =
        BindableProperty.Create(nameof(Text), typeof(string), typeof(BubbleCell), string.Empty);

    public static readonly BindableProperty AuthorProperty =
        BindableProperty.Create(nameof(Author), typeof(string), typeof(BubbleCell), string.Empty);

    public static readonly BindableProperty IncomingBackgoundColorProperty =
        BindableProperty.Create(nameof(IncomingBackgoundColor), typeof(Color), typeof(BubbleCell), Color.White);

    public static readonly BindableProperty IncomingTextColorProperty =
        BindableProperty.Create(nameof(IncomingTextColor), typeof(Color), typeof(BubbleCell), Color.Black);

    public static readonly BindableProperty OutcomingBackgoundColorProperty =
        BindableProperty.Create(nameof(OutcomingBackgroundColor), typeof(Color), typeof(BubbleCell), Color.White);

    public static readonly BindableProperty OutComingTextColorProperty =
        BindableProperty.Create(nameof(OutcomingTextColor), typeof(Color), typeof(BubbleCell), Color.Black);

    public static readonly BindableProperty IncomingAuthorTextColorProperty =
        BindableProperty.Create(nameof(IncomingAuthorTextColor), typeof(Color), typeof(BubbleCell), Color.White);

    public static readonly BindableProperty OutcomingAuthorTextColorProperty =
        BindableProperty.Create(nameof(OutcomingAuthorTextColor), typeof(Color), typeof(BubbleCell), Color.Black);

    public BubbleCell()
    {
        InitializeComponent();
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        AbsoluteLayout.SetLayoutBounds(MessageLayout, IsIncoming ? new Rectangle(0, 0, 0.7,1) : new Rectangle(1, 1, 0.7,1));
        MessageLayout.BackgroundColor = LayoutBackgroundColor;
        TextLabel.Text = Text;
        AuthorLabel.Text = Author;
        TextLabel.TextColor = TextColor;
        AuthorLabel.TextColor = TextColor;
    }



    public bool IsIncoming
    {
        get => (bool) GetValue(IsIncomingProperty);
        set => SetValue(IsIncomingProperty, value);
    }

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public string Author
    {
        get => (string)GetValue(AuthorProperty);
        set => SetValue(AuthorProperty, value);
    }

    public Color IncomingBackgoundColor
    {
        get => (Color) GetValue(IncomingBackgoundColorProperty);
        set => SetValue(IncomingBackgoundColorProperty, value);
    }

    public Color IncomingTextColor
    {
        get => (Color)GetValue(IncomingTextColorProperty);
        set => SetValue(IncomingTextColorProperty, value);
    }

    public Color OutcomingBackgroundColor
    {
        get => (Color)GetValue(OutcomingBackgoundColorProperty);
        set => SetValue(OutcomingBackgoundColorProperty, value);
    }

    public Color OutcomingTextColor
    {
        get => (Color)GetValue(OutComingTextColorProperty);
        set => SetValue(OutComingTextColorProperty, value);
    }

    public Color IncomingAuthorTextColor
    {
        get => (Color)GetValue(IncomingAuthorTextColorProperty);
        set => SetValue(IncomingAuthorTextColorProperty, value);
    }

    public Color OutcomingAuthorTextColor
    {
        get => (Color)GetValue(OutcomingAuthorTextColorProperty);
        set => SetValue(OutcomingAuthorTextColorProperty, value);
    }


    [DependsOn(nameof(IsIncoming), nameof(IncomingTextColor), nameof(OutcomingTextColor))]
    public Color TextColor => IsIncoming ? IncomingTextColor : OutcomingTextColor;

    [DependsOn(nameof(IsIncoming), nameof(IncomingAuthorTextColor), nameof(OutcomingAuthorTextColor))]
    public Color AuthorTextColor => IsIncoming ? IncomingAuthorTextColor : OutcomingAuthorTextColor;

    [DependsOn(nameof(IsIncoming), nameof(IncomingBackgoundColor), nameof(OutcomingBackgroundColor))]
    public Color LayoutBackgroundColor => IsIncoming ? IncomingBackgoundColor : OutcomingBackgroundColor;

    [DependsOn(nameof(IsIncoming))]
    public LayoutOptions CellContentHorizontalOptions => IsIncoming ? LayoutOptions.StartAndExpand : LayoutOptions.EndAndExpand;

}

和 ChatListView 下一个代码:

public class ChatListView : ListView
{
    public ChatListView()
    {
        Initialize();
    }

    public ChatListView(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
    {
        Initialize();
    }

    private void Initialize()
    {
        var chatElementTapGestureRecognizer = new TapGestureRecognizer { Command = new Command(OnTapped) };
        GestureRecognizers.Add(chatElementTapGestureRecognizer);
    }

    private async void OnTapped()
    {
        var lastElement = ItemsSource.Cast<object>().LastOrDefault();

        if (lastElement == null)
        {
            return;
        }
        try
        {
            Device.BeginInvokeOnMainThread(() => ScrollTo(lastElement, ScrollToPosition.Start, true));
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
    }

接下来是 ChatListView 的用法:

<chatviews:ChatListView HasUnevenRows="True"
    HorizontalOptions="FillAndExpand"
    ItemTemplate="{StaticResource MessageTemplateSelector}"
    ItemsSource="{Binding Messages}"
    SeparatorVisibility="None"
    VerticalOptions="FillAndExpand">

    <ListView.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding TapCommand}" />
    </ListView.GestureRecognizers>

    <ListView.Effects>
        <effects:ListViewNoSelectionEffect />
    </ListView.Effects>

    <chatviews:ChatListView.Footer>
        <BoxView BackgroundColor="Transparent"
            HeightRequest="200"
            HorizontalOptions="FillAndExpand"
            IsVisible="{Binding IsEndOfStoryReached, Converter={StaticResource InverseBooleanConverter}}" />
        </chatviews:ChatListView.Footer>
    </chatviews:ChatListView>

当我在列表中有大约 20 个以上的项目时,当我动态添加新元素时,我有列表的下一个行为。我也不知道如何解决标签不正确的自动换行高度计算问题。

Here is wide gif expierence

我在设备上进行了 iPad 和测试,没有问题。看起来很神奇