flow layout
This commit is contained in:
parent
66f0c1ba1b
commit
5e403a86d7
194
Pixiview/UI/FlowLayout.cs
Normal file
194
Pixiview/UI/FlowLayout.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user