optimize: flow measure and layout

This commit is contained in:
Tsanie Lily 2020-05-16 22:45:22 +08:00
parent 623410b47c
commit 7bb3c34fd5
5 changed files with 100 additions and 26 deletions

View File

@ -7,6 +7,7 @@ using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.UI.Theme;
using Pixiview.Utils;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Pixiview.Illust
@ -613,12 +614,13 @@ namespace Pixiview.Illust
if (now == null)
{
now = collection;
IllustCollection = now;
}
else
{
now = new IllustCollection(now.Concat(collection));
//now = new IllustCollection(now.Concat(collection));
now.AddRange(collection);
}
IllustCollection = now;
return now;
}
@ -652,7 +654,7 @@ namespace Pixiview.Illust
Down
}
public class IllustCollection : List<IllustItem>
public class IllustCollection : List<IllustItem>, IIllustCollectionChanged
{
private static IllustCollection empty;
@ -668,6 +670,8 @@ namespace Pixiview.Illust
}
}
public event EventHandler<CollectionChangedEventArgs> CollectionChanged;
public IllustCollection() : base()
{
running = true;
@ -677,8 +681,28 @@ namespace Pixiview.Illust
running = true;
}
public void AddRange(List<IllustItem> items)
{
var e = new CollectionChangedEventArgs
{
NewStartingIndex = Count,
NewItems = items
};
base.AddRange(items);
if (MainThread.IsMainThread)
{
CollectionChanged?.Invoke(this, e);
}
else
{
MainThread.BeginInvokeOnMainThread(() =>
CollectionChanged?.Invoke(this, e));
}
}
private readonly object sync = new object();
private volatile bool running;
public bool Running
{
get => running;

View File

@ -307,10 +307,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
protected override bool CheckRefresh()

View File

@ -72,10 +72,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
protected override bool CheckRefresh()

View File

@ -82,10 +82,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
protected override bool CheckRefresh()

View File

@ -1,6 +1,6 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
@ -45,15 +45,27 @@ namespace Pixiview.UI
public double ColumnWidth { get; private set; }
private bool freezed;
private double maximumHeight;
private readonly Dictionary<View, Rectangle> cachedLayout = new Dictionary<View, Rectangle>();
protected override void LayoutChildren(double x, double y, double width, double height)
{
if (freezed)
{
return;
}
var column = Column;
if (column <= 0)
{
return;
}
var source = ItemsSource;
if (source == null || source.Count <= 0)
{
return;
}
App.DebugPrint($"layout children: ({x}, {y}, {width}, {height})");
var columnSpacing = ColumnSpacing;
var rowSpacing = RowSpacing;
@ -73,15 +85,31 @@ namespace Pixiview.UI
col = i;
}
}
item.Layout(new Rectangle(
var rect = new Rectangle(
col * (columnWidth + columnSpacing),
columnHeights[col],
columnWidth,
measured.Request.Height));
measured.Request.Height);
if (cachedLayout.TryGetValue(item, out var v))
{
if (v != rect)
{
item.Layout(rect);
}
}
else
{
cachedLayout.Add(item, rect);
item.Layout(rect);
}
columnHeights[col] += measured.Request.Height + rowSpacing;
}
}
private double lastWidth = -1;
private SizeRequest lastSizeRequest;
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var column = Column;
@ -89,6 +117,11 @@ namespace Pixiview.UI
{
return base.OnMeasure(widthConstraint, heightConstraint);
}
if (lastWidth == widthConstraint)
{
return lastSizeRequest;
}
lastWidth = widthConstraint;
var columnSpacing = ColumnSpacing;
var rowSpacing = RowSpacing;
@ -112,9 +145,13 @@ namespace Pixiview.UI
}
maximumHeight = columnHeights.Max();
MaxHeightChanged?.Invoke(this, new HeightEventArgs { ContentHeight = maximumHeight });
if (maximumHeight > 0)
{
MaxHeightChanged?.Invoke(this, new HeightEventArgs { ContentHeight = maximumHeight });
}
return new SizeRequest(new Size(widthConstraint, maximumHeight));
lastSizeRequest = new SizeRequest(new Size(widthConstraint, maximumHeight));
return lastSizeRequest;
}
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(
@ -136,18 +173,21 @@ namespace Pixiview.UI
private static void OnItemsSourcePropertyChanged(BindableObject obj, object oldValue, object newValue)
{
var flowLayout = (FlowLayout)obj;
if (oldValue is INotifyCollectionChanged oldNotify)
if (oldValue is IIllustCollectionChanged oldNotify)
{
oldNotify.CollectionChanged -= flowLayout.OnCollectionChanged;
}
flowLayout.lastWidth = -1;
if (newValue == null)
{
flowLayout.cachedLayout.Clear();
flowLayout.Children.Clear();
//flowLayout.UpdateChildrenLayout();
//flowLayout.InvalidateLayout();
flowLayout.InvalidateLayout();
}
else if (newValue is IList newList)
{
flowLayout.freezed = true;
flowLayout.cachedLayout.Clear();
flowLayout.Children.Clear();
for (var i = 0; i < newList.Count; i++)
{
@ -159,25 +199,30 @@ namespace Pixiview.UI
}
}
if (newValue is INotifyCollectionChanged newNotify)
if (newValue is IIllustCollectionChanged newNotify)
{
newNotify.CollectionChanged += flowLayout.OnCollectionChanged;
}
flowLayout.freezed = false;
flowLayout.UpdateChildrenLayout();
flowLayout.InvalidateLayout();
}
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
private void OnCollectionChanged(object sender, CollectionChangedEventArgs e)
{
lastWidth = -1;
if (e.OldItems != null)
{
freezed = true;
cachedLayout.Clear();
var index = e.OldStartingIndex;
for (var i = index + e.OldItems.Count - 1; i >= index; i--)
{
Children.RemoveAt(i);
}
freezed = false;
UpdateChildrenLayout();
InvalidateLayout();
}
@ -187,6 +232,7 @@ namespace Pixiview.UI
return;
}
freezed = true;
var start = e.NewStartingIndex;
for (var i = 0; i < e.NewItems.Count; i++)
{
@ -197,12 +243,25 @@ namespace Pixiview.UI
Children.Insert(start + i, view);
}
}
freezed = false;
UpdateChildrenLayout();
InvalidateLayout();
}
}
public interface IIllustCollectionChanged
{
event EventHandler<CollectionChangedEventArgs> CollectionChanged;
}
public class CollectionChangedEventArgs : EventArgs
{
public int OldStartingIndex { get; set; }
public IList OldItems { get; set; }
public int NewStartingIndex { get; set; }
public IList NewItems { get; set; }
}
public class HeightEventArgs : EventArgs
{
public double ContentHeight { get; set; }