diff --git a/Pixiview/Illust/IllustCollectionPage.cs b/Pixiview/Illust/IllustCollectionPage.cs index 1e07d89..87a3114 100644 --- a/Pixiview/Illust/IllustCollectionPage.cs +++ b/Pixiview/Illust/IllustCollectionPage.cs @@ -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 + public class IllustCollection : List, IIllustCollectionChanged { private static IllustCollection empty; @@ -668,6 +670,8 @@ namespace Pixiview.Illust } } + public event EventHandler CollectionChanged; + public IllustCollection() : base() { running = true; @@ -677,8 +681,28 @@ namespace Pixiview.Illust running = true; } + public void AddRange(List 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; diff --git a/Pixiview/Illust/RankingPage.xaml.cs b/Pixiview/Illust/RankingPage.xaml.cs index a17a7ba..626783d 100644 --- a/Pixiview/Illust/RankingPage.xaml.cs +++ b/Pixiview/Illust/RankingPage.xaml.cs @@ -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() diff --git a/Pixiview/Illust/RelatedIllustsPage.xaml.cs b/Pixiview/Illust/RelatedIllustsPage.xaml.cs index e96adcd..b7f835f 100644 --- a/Pixiview/Illust/RelatedIllustsPage.xaml.cs +++ b/Pixiview/Illust/RelatedIllustsPage.xaml.cs @@ -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() diff --git a/Pixiview/Illust/UserIllustPage.xaml.cs b/Pixiview/Illust/UserIllustPage.xaml.cs index e3110ec..1abfdb0 100644 --- a/Pixiview/Illust/UserIllustPage.xaml.cs +++ b/Pixiview/Illust/UserIllustPage.xaml.cs @@ -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() diff --git a/Pixiview/UI/FlowLayout.cs b/Pixiview/UI/FlowLayout.cs index 943acb8..39eddeb 100644 --- a/Pixiview/UI/FlowLayout.cs +++ b/Pixiview/UI/FlowLayout.cs @@ -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 cachedLayout = new Dictionary(); 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 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; }