flow layout

This commit is contained in:
Tsanie Lily 2020-05-05 01:54:37 +08:00
parent 66f0c1ba1b
commit 5e403a86d7

194
Pixiview/UI/FlowLayout.cs Normal file
View File

@ -0,0 +1,194 @@
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using Xamarin.Forms;
namespace Pixiview.UI
{
public class FlowLayout : Layout<View>
{
public static readonly BindableProperty ColumnProperty = BindableProperty.Create(
nameof(Column), typeof(int), typeof(FlowLayout), 2, propertyChanged: OnColumnPropertyChanged);
public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(
nameof(RowSpacing), typeof(double), typeof(FlowLayout), 10.0);
public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
nameof(ColumnSpacing), typeof(double), typeof(FlowLayout), 10.0);
private static void OnColumnPropertyChanged(BindableObject obj, object oldValue, object newValue)
{
var flowLayout = (FlowLayout)obj;
if (oldValue is int column && column != flowLayout.Column)
{
//flowLayout.UpdateChildrenLayout();
flowLayout.InvalidateLayout();
}
}
public int Column
{
get => (int)GetValue(ColumnProperty);
set => SetValue(ColumnProperty, value);
}
public double RowSpacing
{
get => (double)GetValue(RowSpacingProperty);
set => SetValue(RowSpacingProperty, value);
}
public double ColumnSpacing
{
get => (double)GetValue(ColumnSpacingProperty);
set => SetValue(ColumnSpacingProperty, value);
}
public double ColumnWidth { get; private set; }
private double maximumHeight;
protected override void LayoutChildren(double x, double y, double width, double height)
{
var column = Column;
if (column <= 0)
{
return;
}
var columnSpacing = ColumnSpacing;
var rowSpacing = RowSpacing;
var columnHeights = new double[column];
var columnSpacingTotal = columnSpacing * (column - 1);
var columnWidth = (width - columnSpacingTotal) / column;
ColumnWidth = columnWidth;
foreach (var item in Children)
{
var measured = item.Measure(columnWidth, height, MeasureFlags.IncludeMargins);
var col = 0;
for (var i = 1; i < column; i++)
{
if (columnHeights[i] < columnHeights[col])
{
col = i;
}
}
item.Layout(new Rectangle(
col * (columnWidth + columnSpacing),
columnHeights[col],
columnWidth,
measured.Request.Height));
columnHeights[col] += measured.Request.Height + rowSpacing;
}
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var column = Column;
if (column <= 0)
{
return base.OnMeasure(widthConstraint, heightConstraint);
}
var columnSpacing = ColumnSpacing;
var rowSpacing = RowSpacing;
var columnHeights = new double[column];
var columnSpacingTotal = columnSpacing * (column - 1);
var columnWidth = (widthConstraint - columnSpacingTotal) / column;
ColumnWidth = columnWidth;
foreach (var item in Children)
{
var measured = item.Measure(columnWidth, heightConstraint, MeasureFlags.IncludeMargins);
var col = 0;
for (var i = 1; i < column; i++)
{
if (columnHeights[i] < columnHeights[col])
{
col = i;
}
}
columnHeights[col] += measured.Request.Height + rowSpacing;
}
maximumHeight = columnHeights.Max();
return new SizeRequest(new Size(widthConstraint, maximumHeight));
}
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(
nameof(ItemTemplate), typeof(DataTemplate), typeof(FlowLayout));
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
nameof(ItemsSource), typeof(IList), typeof(FlowLayout), propertyChanged: OnItemsSourcePropertyChanged);
public DataTemplate ItemTemplate
{
get => (DataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
public IList ItemsSource
{
get => (IList)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
private static void OnItemsSourcePropertyChanged(BindableObject obj, object oldValue, object newValue)
{
var flowLayout = (FlowLayout)obj;
if (oldValue is INotifyCollectionChanged oldNotify)
{
oldNotify.CollectionChanged -= flowLayout.OnCollectionChanged;
}
if (newValue is IList newList)
{
flowLayout.Children.Clear();
for (var i = 0; i < newList.Count; i++)
{
var child = flowLayout.ItemTemplate.CreateContent();
if (child is View view)
{
view.BindingContext = newList[i];
flowLayout.Children.Add(view);
}
}
if (newValue is INotifyCollectionChanged newNotify)
{
newNotify.CollectionChanged += flowLayout.OnCollectionChanged;
}
flowLayout.UpdateChildrenLayout();
flowLayout.InvalidateLayout();
}
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
var index = e.OldStartingIndex;
for (var i = index + e.OldItems.Count - 1; i >= index; i--)
{
Children.RemoveAt(i);
}
UpdateChildrenLayout();
InvalidateLayout();
}
if (e.NewItems == null)
{
return;
}
var start = e.NewStartingIndex;
for (var i = 0; i < e.NewItems.Count; i++)
{
var child = ItemTemplate.CreateContent();
if (child is View view)
{
view.BindingContext = e.NewItems[i];
Children.Insert(start + i, view);
}
}
UpdateChildrenLayout();
InvalidateLayout();
}
}
}