From fe75391962fe917c51c4f149f63daa562f58bf3e Mon Sep 17 00:00:00 2001 From: Troy Stanger Date: Fri, 9 Nov 2018 10:52:22 -0600 Subject: [PATCH 1/3] ListViewRenderer now properly lays out custom cell views. As a result of this fix the renderer will now respect row heights set in Xamarin.Forms and should support lists with uneven cell heights. --- Ooui.Forms/Renderers/ListViewRenderer.cs | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/Ooui.Forms/Renderers/ListViewRenderer.cs b/Ooui.Forms/Renderers/ListViewRenderer.cs index 98cf5a3..069c264 100644 --- a/Ooui.Forms/Renderers/ListViewRenderer.cs +++ b/Ooui.Forms/Renderers/ListViewRenderer.cs @@ -11,7 +11,17 @@ namespace Ooui.Forms.Renderers { public class ListViewRenderer : ViewRenderer { + const int DefaultRowHeight = 44; private bool _disposed; + IVisualElementRenderer _prototype; + + int _rowHeight; + + public int RowHeight + { + get => _rowHeight; + set => _rowHeight = value; + } public ListViewRenderer () { @@ -32,6 +42,10 @@ namespace Ooui.Forms.Renderers var list = new List (); list.Style.Overflow = "scroll"; list.Style.Padding = "0"; + // Make the list element positioned so child elements will + // be positioned relative to it. This will allow the list + // to scroll properly. + list.Style.Position = "relative"; SetNativeControl (list); } @@ -40,6 +54,8 @@ namespace Ooui.Forms.Renderers templatedItems.CollectionChanged += OnCollectionChanged; e.NewElement.ScrollToRequested += ListView_ScrollToRequested; + UpdateRowHeight(); + UpdateItems (); UpdateBackgroundColor (); } @@ -121,11 +137,22 @@ namespace Ooui.Forms.Renderers } else { var i = 0; + double offset = 0; foreach (var item in items) { var li = listItems[i]; + var nativeCell = items[i]; var children = li.Children; var rv = children.Count > 0 ? children[0] as CellElement : null; var cell = GetCell (item, rv); + var height = CalculateHeightForCell(nativeCell); + li.Style.Height = height; + var viewCell = (ViewCell)cell.Cell; + if (viewCell != null && viewCell.View != null) + { + var rect = new Rectangle(0, offset, Element.Width, height); + Layout.LayoutChildIntoBoundingRegion(viewCell.View, rect); + } + offset += height; if (rv == null) { li.AppendChild (cell); } @@ -161,6 +188,56 @@ namespace Ooui.Forms.Renderers } } + void UpdateRowHeight() + { + var rowHeight = Element.RowHeight; + if (Element.HasUnevenRows && rowHeight == -1) + RowHeight = -1; + else + RowHeight = rowHeight <= 0 ? DefaultRowHeight : rowHeight; + } + + internal double CalculateHeightForCell(Cell cell) + { + if (!Element.HasUnevenRows) + { + return RowHeight; + } + else + { + var viewCell = cell as ViewCell; + if (viewCell != null && viewCell.View != null) + { + var target = viewCell.View; + if (_prototype == null) + _prototype = Platform.CreateRenderer(target); + else + _prototype.SetElement(target); + + Platform.SetRenderer(target, _prototype); + + var req = target.Measure(Element.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + + target.ClearValue(Platform.RendererProperty); + foreach (Xamarin.Forms.Element descendant in target.Descendants()) + { + IVisualElementRenderer renderer = Platform.GetRenderer(descendant as VisualElement); + + // Clear renderer from descendent; this will not happen in Dispose as normal because we need to + // unhook the Element from the renderer before disposing it. + descendant.ClearValue(Platform.RendererProperty); + renderer?.Dispose(); + renderer = null; + } + + var height = req.Request.Height; + return height > 1 ? height : DefaultRowHeight; + } + var renderHeight = cell.RenderHeight; + return renderHeight > 0 ? renderHeight : DefaultRowHeight; + } + } + void UpdateBackgroundColor () { if (Control == null) From 1e5561c1a841d848e710ab6d2b58418ade26def1 Mon Sep 17 00:00:00 2001 From: Troy Stanger Date: Fri, 9 Nov 2018 11:51:55 -0600 Subject: [PATCH 2/3] Properly dispose of the prototype renderer. --- Ooui.Forms/Renderers/ListViewRenderer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Ooui.Forms/Renderers/ListViewRenderer.cs b/Ooui.Forms/Renderers/ListViewRenderer.cs index 069c264..4c2f1ff 100644 --- a/Ooui.Forms/Renderers/ListViewRenderer.cs +++ b/Ooui.Forms/Renderers/ListViewRenderer.cs @@ -79,6 +79,8 @@ namespace Ooui.Forms.Renderers if (disposing && !_disposed) { + ClearPrototype(); + if (Element != null) { var templatedItems = TemplatedItemsView.TemplatedItems; templatedItems.CollectionChanged -= OnCollectionChanged; @@ -89,6 +91,17 @@ namespace Ooui.Forms.Renderers } } + void ClearPrototype() + { + if (_prototype != null) + { + var element = _prototype.Element; + element?.ClearValue(Platform.RendererProperty); + _prototype?.Dispose(); + _prototype = null; + } + } + private void OnCollectionChanged (object sender, NotifyCollectionChangedEventArgs e) { UpdateItems (); From 0784b98c8e6abcb88c141135d4a9e9c04eaccd5d Mon Sep 17 00:00:00 2001 From: Troy Stanger Date: Mon, 12 Nov 2018 13:09:51 -0600 Subject: [PATCH 3/3] Resize and re-layout ViewCells when the parent ListView is resized. --- Ooui.Forms/Renderers/ListViewRenderer.cs | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Ooui.Forms/Renderers/ListViewRenderer.cs b/Ooui.Forms/Renderers/ListViewRenderer.cs index 4c2f1ff..b97f9f0 100644 --- a/Ooui.Forms/Renderers/ListViewRenderer.cs +++ b/Ooui.Forms/Renderers/ListViewRenderer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; +using System.Timers; using Xamarin.Forms; using Xamarin.Forms.Internals; using Ooui.Forms.Cells; @@ -14,6 +15,7 @@ namespace Ooui.Forms.Renderers const int DefaultRowHeight = 44; private bool _disposed; IVisualElementRenderer _prototype; + Timer _timer; int _rowHeight; @@ -68,7 +70,31 @@ namespace Ooui.Forms.Renderers base.OnElementPropertyChanged (sender, e); if (e.PropertyName == ItemsView.ItemsSourceProperty.PropertyName) - UpdateItems (); + UpdateItems(); + else if (e.PropertyName == Xamarin.Forms.ListView.RowHeightProperty.PropertyName) + { + UpdateRowHeight(); + UpdateItems(); + } + else if (e.PropertyName == VisualElement.WidthProperty.PropertyName) + { + if (_timer != null) + { + _timer.Stop(); + } + else + { + _timer = new Timer(); + _timer.Interval = 250; + _timer.Elapsed += delegate { + UpdateItems(); + }; + _timer.Enabled = true; + _timer.AutoReset = false; + } + _timer.Start(); + } + } protected override void Dispose (bool disposing)