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

View File

@ -307,10 +307,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e) 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() protected override bool CheckRefresh()

View File

@ -72,10 +72,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e) 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() protected override bool CheckRefresh()

View File

@ -82,10 +82,7 @@ namespace Pixiview.Illust
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e) 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() protected override bool CheckRefresh()

View File

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