首页 > 解决方案 > 如何回收/重用子视图列表而不必重新计算大小以获得更好的性能?Xamarin 表单

问题描述

我目前正在使用具有列表支持的 Stacklayout。我将此列表的数据加载到另一个线程上,然后将其添加到主线程上的此视图中。这在功能方面工作得很好,但是当项目被加载到控件时我遇到了轻微的延迟/性能问题。因此,在调用 API 之后,对主线程的分配正在发生。我加载了大约 5 个项目以确保我不会拥挤它,因为它包含图像(尺寸小 + 我使用 FFImageLoading)。

我现在所做的是缓存所有视图,然后在可能的情况下重用它。工作正常,但似乎并没有提高性能。我想的是覆盖LayoutChildren,因为即使我重用视图,添加到此堆栈布局的子项似乎也在重新计算它的大小。我可能做错了什么?

下面是我的自定义控件:

public class CustomStackLayout : StackLayout
{

    public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(CustomStackLayout),
        default(DataTemplate), BindingMode.OneWay);

    public DataTemplate ItemTemplate
    {
        get { return (DataTemplate)GetValue(ItemTemplateProperty); }
        set
        {
            SetValue(ItemTemplateProperty, value);
            OnPropertyChanged(nameof(ItemTemplate));
        }
    }

    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(CustomStackLayout), default(IEnumerable<object>), BindingMode.TwoWay, propertyChanged: ItemsSourceChanged);

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set
        {
            SetValue(ItemsSourceProperty, value);
            OnPropertyChanged(nameof(ItemsSource));
        }
    }

    private static void ItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var items = (CustomStackLayout)bindable;
        items.Children.Clear();

        if (items.ItemsSource != null)
        {
            foreach (var item in items.ItemsSource)
            {
                items.Children.Add(items.GetRowView(item));
            }
        }
    }

    protected virtual View GetRowView(object item)
    {
        // See if cached
        var model = item as CustomModel;
        if (AllViews.Any(x => x.Message.ObjectID.Equals(model.ObjectID)))
        {
            return AllViews.Where(x => x.Message.ObjectID.Equals(model.ObjectID)).FirstOrDefault().View;
        }

        object viewContent;

        // Determine if this is a straight up template or using a AllViewselector
        if (ItemTemplate is DataTemplateSelector dts)
        {
            var template = dts.SelectTemplate(item, this);
            viewContent = template.CreateContent();
        }
        else
        {
            viewContent = ItemTemplate.CreateContent();
        }

        // Cache views
        this.AllViews.Add(new CachedView() { Message = msg, View = viewContent is View ? viewContent as View : ((ViewCell)viewContent).View });

        // Bind
        var rowView = viewContent is View ? viewContent as View : ((ViewCell)viewContent).View;
        rowView.BindingContext = item;

        return rowView;
    }

    public List<CachedView> AllViews = new List<CachedView>();
}

public class CachedView
{
    public Type Type { get; set; }
    public View View { get; set; }
    public Message Message { get; set; }
}

这就是我在 xaml 中使用它的方式,它DataTemplateSelector根据数据在不同的视图之间进行选择:

<controls:CustomStackLayout ItemsSource="{Binding Data}">
     <controls:CustomStackLayout.ItemTemplate>
           <selector:MyDataTemplateSelector />
     </controls:CustomStackLayout.ItemTemplate>
 </controls:CustomStackLayout>

标签: c#xamarinxamarin.forms

解决方案


推荐阅读