[Tizen] Enhancement CollectionView implementation on Tizen (#6181)
authorSeungkeun Lee <sngn.lee@samsung.com>
Thu, 23 May 2019 00:21:38 +0000 (09:21 +0900)
committerRui Marinho <me@ruimarinho.net>
Thu, 23 May 2019 00:21:38 +0000 (01:21 +0100)
* Implement ItemsView.ItemSizingStrategy property

* CollectionView - Optimization Item size calculation with cache

* Implement ItemsView.ItemSizingStrategy property on GridItemsLayout

 - refactoring CollectionView

* CollectionView - Support DataTemplateSelector

* Fix FrameRenderer

 - Inherit from LayoutRenderer because Frame is subclass of Layout

* Fix minor layout issue on CollectionView

* Allow CollectionView items to resize with their content

* Fix miss typo and remove comment

16 files changed:
Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs
Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg [new file with mode: 0644]
Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg [new file with mode: 0644]
Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg [new file with mode: 0644]
Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/EmptyItemAdaptor.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemAdaptor.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/ItemTemplateAdaptor.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/RecyclerPool.cs
Xamarin.Forms.Platform.Tizen/Native/CollectionView/ViewHolder.cs
Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs
Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs

index 4a28a1b..53b658f 100644 (file)
@@ -21,6 +21,7 @@ namespace Xamarin.Forms.ControlGallery.Tizen
                {
                        var app = new MainApplication();
                        FormsMaps.Init("HERE", "write-your-API-key-here");
+                       global::Xamarin.Forms.Platform.Tizen.Forms.SetFlags("CollectionView_Experimental", "Shell_Experimental");
                        global::Xamarin.Forms.Platform.Tizen.Forms.Init(app);
                        FormsMaterial.Init();
                        app.Run(args);
diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg
new file mode 100644 (file)
index 0000000..023797c
Binary files /dev/null and b/Xamarin.Forms.ControlGallery.Tizen/res/FlowerBuds.jpg differ
diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg
new file mode 100644 (file)
index 0000000..6c4ea3d
Binary files /dev/null and b/Xamarin.Forms.ControlGallery.Tizen/res/Fruits.jpg differ
diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg
new file mode 100644 (file)
index 0000000..5f299b4
Binary files /dev/null and b/Xamarin.Forms.ControlGallery.Tizen/res/Legumes.jpg differ
diff --git a/Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg b/Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg
new file mode 100644 (file)
index 0000000..297964d
Binary files /dev/null and b/Xamarin.Forms.ControlGallery.Tizen/res/Vegetables.jpg differ
index 573ea9f..225be2e 100644 (file)
@@ -1,11 +1,12 @@
 using System;
+using System.Linq;
 using System.Collections.Specialized;
+using System.Collections.Generic;
 using ElmSharp;
 using EBox = ElmSharp.Box;
 using EScroller = ElmSharp.Scroller;
 using ESize = ElmSharp.Size;
 using EPoint = ElmSharp.Point;
-using System.Collections.Generic;
 
 namespace Xamarin.Forms.Platform.Tizen.Native
 {
@@ -15,6 +16,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                ICollectionViewLayoutManager _layoutManager;
                ItemAdaptor _adaptor;
                EBox _innerLayout;
+               EvasObject _emptyView;
 
                Dictionary<ViewHolder, int> _viewHolderIndexTable = new Dictionary<ViewHolder, int>();
                ViewHolder _lastSelectedViewHolder;
@@ -92,12 +94,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        {
                                OnAdaptorChanging();
                                _adaptor = value;
-                               _adaptor.CollectionView = this;
                                OnAdaptorChanged();
                        }
                }
 
-               int ICollectionViewController.Count => Adaptor?.Count ?? 0;
+               int ICollectionViewController.Count
+               {
+                       get
+                       {
+                               if (Adaptor == null || Adaptor is IEmptyAdaptor)
+                                       return 0;
+                               return Adaptor.Count;
+                       }
+               }
 
                EPoint ICollectionViewController.ParentPosition => new EPoint
                {
@@ -189,21 +198,31 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        ScrollTo(Adaptor.GetItemIndex(item), position, animate);
                }
 
-               void ICollectionViewController.RequestLayoutItems() => RequestLayoutItems();
+               public void ItemMeasureInvalidated(int index)
+               {
+                       LayoutManager?.ItemMeasureInvalidated(index);
+               }
 
+               void ICollectionViewController.RequestLayoutItems() => RequestLayoutItems();
 
                ESize ICollectionViewController.GetItemSize()
                {
+                       return (this as ICollectionViewController).GetItemSize(LayoutManager.IsHorizontal ? AllocatedSize.Width * 100 : AllocatedSize.Width, LayoutManager.IsHorizontal ? AllocatedSize.Height : AllocatedSize.Height * 100);
+               }
+
+               ESize ICollectionViewController.GetItemSize(int widthConstraint, int heightConstraint)
+               {
                        if (Adaptor == null)
                        {
                                return new ESize(0, 0);
                        }
+
                        if (_itemSize.Width > 0 && _itemSize.Height > 0)
                        {
                                return _itemSize;
                        }
 
-                       _itemSize = Adaptor.MeasureItem(AllocatedSize.Width, AllocatedSize.Height);
+                       _itemSize = Adaptor.MeasureItem(widthConstraint, heightConstraint);
                        _itemSize.Width = Math.Max(_itemSize.Width, 10);
                        _itemSize.Height = Math.Max(_itemSize.Height, 10);
 
@@ -214,22 +233,32 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return _itemSize;
                }
 
+               ESize ICollectionViewController.GetItemSize(int index, int widthConstraint, int heightConstraint)
+               {
+                       if (Adaptor == null)
+                       {
+                               return new ESize(0, 0);
+                       }
+                       return Adaptor.MeasureItem(index, widthConstraint, heightConstraint);
+               }
+
                ViewHolder ICollectionViewController.RealizeView(int index)
                {
                        if (Adaptor == null)
                                return null;
 
-                       var holder = _pool.GetRecyclerView();
+                       var holder = _pool.GetRecyclerView(Adaptor.GetViewCategory(index));
                        if (holder != null)
                        {
                                holder.Show();
                        }
                        else
                        {
-                               var content = Adaptor.CreateNativeView(this);
+                               var content = Adaptor.CreateNativeView(index, this);
                                holder = new ViewHolder(this);
                                holder.RequestSelected += OnRequestItemSelection;
                                holder.Content = content;
+                               holder.ViewCategory = Adaptor.GetViewCategory(index);
                                _innerLayout.PackEnd(holder);
                        }
 
@@ -268,9 +297,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                void ICollectionViewController.UnrealizeView(ViewHolder view)
                {
                        _viewHolderIndexTable.Remove(view);
+                       Adaptor.UnBinding(view.Content);
                        view.ResetState();
                        view.Hide();
                        _pool.AddRecyclerView(view);
+                       if (_lastSelectedViewHolder == view)
+                       {
+                               _lastSelectedViewHolder = null;
+                       }
+               }
+
+               void ICollectionViewController.ContentSizeUpdated()
+               {
+                       OnInnerLayout();
                }
 
                protected virtual EScroller CreateScroller(EvasObject parent)
@@ -308,7 +347,6 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                }
 
-
                void OnLayoutManagerChanging()
                {
                        _layoutManager?.Reset();
@@ -319,6 +357,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        if (_layoutManager == null)
                                return;
 
+                       _itemSize = new ESize(-1, -1);
                        _layoutManager.CollectionView = this;
                        _layoutManager.SizeAllocated(AllocatedSize);
                        RequestLayoutItems();
@@ -326,6 +365,11 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                void OnAdaptorChanging()
                {
+                       if (Adaptor is IEmptyAdaptor)
+                       {
+                               RemoveEmptyView();
+                       }
+
                        _layoutManager?.Reset();
                        if (Adaptor != null)
                        {
@@ -340,32 +384,67 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                return;
 
                        _itemSize = new ESize(-1, -1);
+                       Adaptor.CollectionView = this;
                        (Adaptor as INotifyCollectionChanged).CollectionChanged += OnCollectionChanged;
-
+                       
+                       LayoutManager?.ItemSourceUpdated();
                        RequestLayoutItems();
 
-                       if (LayoutManager != null)
+                       if (Adaptor is IEmptyAdaptor)
                        {
-                               var itemSize = (this as ICollectionViewController).GetItemSize();
+                               CreateEmptyView();
                        }
                }
 
                void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
                {
+                       // CollectionChanged could be called when Apaptor was changed on CollectionChanged event
+                       if (Adaptor is IEmptyAdaptor)
+                       {
+                               return;
+                       }
+
                        if (e.Action == NotifyCollectionChangedAction.Add)
                        {
                                int idx = e.NewStartingIndex;
+                               if (idx == -1)
+                               {
+                                       idx = Adaptor.Count - e.NewItems.Count;
+                               }
                                foreach (var item in e.NewItems)
                                {
+                                       foreach (var viewHolder in _viewHolderIndexTable.Keys.ToList())
+                                       {
+                                               if (_viewHolderIndexTable[viewHolder] >= idx)
+                                               {
+                                                       _viewHolderIndexTable[viewHolder]++;
+                                               }
+                                       }
                                        LayoutManager.ItemInserted(idx++);
                                }
                        }
                        else if (e.Action == NotifyCollectionChangedAction.Remove)
                        {
                                int idx = e.OldStartingIndex;
-                               foreach (var item in e.OldItems)
+
+                               // Can't tracking remove if there is no data of old index
+                               if (idx == -1)
+                               {
+                                       LayoutManager.ItemSourceUpdated();
+                               }
+                               else
                                {
-                                       LayoutManager.ItemRemoved(idx);
+                                       foreach (var item in e.OldItems)
+                                       {
+                                               LayoutManager.ItemRemoved(idx);
+                                               foreach (var viewHolder in _viewHolderIndexTable.Keys.ToList())
+                                               {
+                                                       if (_viewHolderIndexTable[viewHolder] > idx)
+                                                       {
+                                                               _viewHolderIndexTable[viewHolder]--;
+                                                       }
+                                               }
+                                       }
                                }
                        }
                        else if (e.Action == NotifyCollectionChangedAction.Move)
@@ -375,11 +454,20 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                        else if (e.Action == NotifyCollectionChangedAction.Replace)
                        {
-                               LayoutManager.ItemUpdated(e.NewStartingIndex);
+                               // Can't tracking if there is no information old data
+                               if (e.OldItems.Count > 1 || e.NewStartingIndex == -1)
+                               {
+                                       LayoutManager.ItemSourceUpdated();
+                               }
+                               else
+                               {
+                                       LayoutManager.ItemUpdated(e.NewStartingIndex);
+                               }
                        }
                        else if (e.Action == NotifyCollectionChangedAction.Reset)
                        {
                                LayoutManager.Reset();
+                               LayoutManager.ItemSourceUpdated();
                        }
                        RequestLayoutItems();
                }
@@ -424,6 +512,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                void OnInnerLayout()
                {
+                       // OnInnerLayout was called when child item was added
+                       // so, need to check scroll canvas size
                        var size = _layoutManager.GetScrollCanvasSize();
                        _innerLayout.MinimumWidth = size.Width;
                        _innerLayout.MinimumHeight = size.Height;
@@ -456,6 +546,26 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                        Scroller.SetPageSize(itemSize.Width, itemSize.Height);
                }
+
+               void CreateEmptyView()
+               {
+                       _emptyView = Adaptor.CreateNativeView(this);
+                       _emptyView.Show();
+                       Adaptor.SetBinding(_emptyView, 0);
+                       _emptyView.Geometry = Geometry;
+                       _emptyView.MinimumHeight = Geometry.Height;
+                       _emptyView.MinimumWidth = Geometry.Width;
+                       Scroller.SetContent(_emptyView, true);
+                       _innerLayout.Hide();
+               }
+
+               void RemoveEmptyView()
+               {
+                       _innerLayout.Show();
+                       Scroller.SetContent(_innerLayout, true);
+                       Adaptor.RemoveNativeView(_emptyView);
+                       _emptyView = null;
+               }
        }
 
        public interface ICollectionViewController
@@ -471,6 +581,12 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                int Count { get; }
 
                ESize GetItemSize();
+
+               ESize GetItemSize(int widthConstraint, int heightConstraint);
+
+               ESize GetItemSize(int index, int widthConstraint, int heightConstraint);
+
+               void ContentSizeUpdated();
        }
 
        public enum CollectionViewSelectionMode
@@ -478,5 +594,4 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                None,
                Single,
        }
-
 }
index 5aa6d6d..abee190 100644 (file)
@@ -6,7 +6,7 @@ using XLabel = Xamarin.Forms.Label;
 
 namespace Xamarin.Forms.Platform.Tizen.Native
 {
-       public class EmptyItemAdaptor : ItemTemplateAdaptor
+       public class EmptyItemAdaptor : ItemTemplateAdaptor, IEmptyAdaptor
        {
                static DataTemplate s_defaultEmptyTemplate = new DataTemplate(typeof(EmptyView));
                public EmptyItemAdaptor(ItemsView itemsView, IEnumerable items, DataTemplate template) : base(itemsView, items, template)
index 56f0090..2ac17e2 100644 (file)
@@ -14,13 +14,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                Rect _last;
                Dictionary<int, RealizedItem> _realizedItem = new Dictionary<int, RealizedItem>();
 
-               public GridLayoutManager(bool isHorizontal, int span = 1)
+               List<int> _itemSizes;
+               List<bool> _cached;
+               List<int> _accumulatedItemSizes;
+               bool _hasUnevenRows;
+               int _baseItemSize;
+
+               public GridLayoutManager(bool isHorizontal, int span = 1) : this(isHorizontal, span, ItemSizingStrategy.MeasureFirstItem) { }
+
+               public GridLayoutManager(bool isHorizontal, int span, ItemSizingStrategy sizingStrategy)
                {
                        IsHorizontal = isHorizontal;
                        Span = span;
+                       _hasUnevenRows = sizingStrategy == ItemSizingStrategy.MeasureAllItems;
                }
 
-               public int Span { get; internal set; }
+               public int Span { get; private set; }
 
                public bool IsHorizontal { get; }
 
@@ -30,23 +39,63 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                {
                        Reset();
                        _allocatedSize = size;
-                       _scrollCanvasSize = new ESize(0, 0);
+                       InitializeMeasureCache();
                }
 
                public ESize GetScrollCanvasSize()
                {
+                       if (CollectionView.Count == 0 || _allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                               return _allocatedSize;
+
                        if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0)
                                return _scrollCanvasSize;
 
-                       var itemCount = CollectionView.Count;
-                       var itemSize = CollectionView.GetItemSize();
+                       int totalItemSize = 0;
+                       if (_hasUnevenRows)
+                       {
+                               totalItemSize = _accumulatedItemSizes[_accumulatedItemSizes.Count - 1];
+                       }
+                       else
+                       {
+                               totalItemSize = (int)Math.Ceiling(CollectionView.Count / (double)Span) * BaseItemSize;
+                       }
+
                        if (IsHorizontal)
                        {
-                               return _scrollCanvasSize = new ESize((int)Math.Ceiling(itemCount / (double)Span) * itemSize.Width , _allocatedSize.Height);
+                               _scrollCanvasSize = new ESize(totalItemSize , _allocatedSize.Height);
                        }
                        else
                        {
-                               return _scrollCanvasSize = new ESize(_allocatedSize.Width, (int)Math.Ceiling(itemCount / (double)Span) * itemSize.Height);
+                               _scrollCanvasSize = new ESize(_allocatedSize.Width, totalItemSize);
+                       }
+
+                       return _scrollCanvasSize;
+               }
+
+               int BaseItemSize
+               {
+                       get
+                       {
+                               if (_baseItemSize == 0)
+                               {
+                                       if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                                               return 0;
+
+                                       var itembound = CollectionView.GetItemSize(ItemWidthConstraint, ItemHeightConstraint);
+                                       _baseItemSize = IsHorizontal ? itembound.Width : itembound.Height;
+                               }
+                               return _baseItemSize;
+                       }
+               }
+
+               int ItemWidthConstraint => IsHorizontal ? _allocatedSize.Width * 100 : ColumnSize;
+               int ItemHeightConstraint => IsHorizontal ? ColumnSize : _allocatedSize.Height * 100;
+
+               int ColumnSize
+               {
+                       get
+                       {
+                               return IsHorizontal ? _allocatedSize.Height / Span : _allocatedSize.Width / Span;
                        }
                }
 
@@ -58,8 +107,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                return true;
 
                        var diff = IsHorizontal ? Math.Abs(_last.X - viewport.X) : Math.Abs(_last.Y - viewport.Y);
-                       var margin = IsHorizontal ? CollectionView.GetItemSize().Width : CollectionView.GetItemSize().Height;
-                       if (diff > margin)
+                       if (diff > BaseItemSize)
                                return true;
 
                        return false;
@@ -74,13 +122,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                        _isLayouting = true;
                        _last = bound;
-
-                       var size = CollectionView.GetItemSize();
-                       var itemSize = IsHorizontal ? size.Width : size.Height;
-
+                       
                        int padding = Span * 2;
-                       int startIndex = Math.Max(GetStartIndex(bound, itemSize) - padding, 0);
-                       int endIndex = Math.Min(GetEndIndex(bound, itemSize) + padding, CollectionView.Count - 1);
+                       int startIndex = Math.Max(GetStartIndex(bound) - padding, 0);
+                       int endIndex = Math.Min(GetEndIndex(bound) + padding, CollectionView.Count - 1);
 
                        foreach (var index in _realizedItem.Keys.ToList())
                        {
@@ -122,7 +167,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                public void UpdateSpan(int span)
                {
                        Span = span;
-                       _scrollCanvasSize = new ESize(0, 0);
+                       InitializeMeasureCache();
                        CollectionView.RequestLayoutItems();
                }
 
@@ -149,6 +194,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                }
                        }
 
+                       UpdateInsertedSize(inserted);
+
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
@@ -175,6 +222,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                _realizedItem.Remove(last);
                        }
 
+                       UpdateRemovedSize(removed);
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
@@ -192,29 +240,76 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                public Rect GetItemBound(int index)
                {
-                       var size = CollectionView.GetItemSize();
-                       if (IsHorizontal)
+                       int rowIndex = index / Span;
+                       int columnIndex = index % Span;
+                       var columnSize = ColumnSize;
+
+                       int rowStartPoint = 0;
+                       int columnStartPoint = 0;
+                       int itemSize = 0;
+
+                       if (!_hasUnevenRows)
                        {
-                               size.Height = _allocatedSize.Height / Span;
+                               itemSize = BaseItemSize;
+                               rowStartPoint = rowIndex * BaseItemSize;
+                               columnStartPoint = columnIndex * columnSize;
                        }
-                       else
+                       else if (_cached[index])
                        {
-                               size.Width = _allocatedSize.Width / Span;
+                               var updatedMaxItemSize = GetMaxItemSize(index);
+                               itemSize = _itemSizes[index];
+                               rowStartPoint = _accumulatedItemSizes[rowIndex] - updatedMaxItemSize + (updatedMaxItemSize - itemSize) / 2;
+                               columnStartPoint = columnSize * columnIndex;
                        }
+                       else
+                       {
+                               var oldMaxItemSize = GetMaxItemSize(index);
 
-                       int rowIndex = index / Span;
-                       int colIndex = index % Span;
-                       var colSize = IsHorizontal ? size.Height : size.Width;
+                               var measured = CollectionView.GetItemSize(index, ItemWidthConstraint, ItemHeightConstraint);
+                               itemSize = IsHorizontal ? measured.Width : measured.Height;
 
-                       return
-                               IsHorizontal ?
-                               new Rect(rowIndex * size.Width, colIndex * size.Height, size.Width, size.Height) :
-                               new Rect(colIndex * size.Width, rowIndex * size.Height, size.Width, size.Height);
+                               if (itemSize != _itemSizes[index])
+                               {
+                                       _itemSizes[index] = itemSize;
+                               }
+
+                               var updatedMaxItemSize = GetMaxItemSize(index);
+                               if (oldMaxItemSize != updatedMaxItemSize)
+                               {
+                                       UpdateAccumulatedItemSize(rowIndex, updatedMaxItemSize - oldMaxItemSize);
+                                       int columnStart = (index / Span) * Span;
+                                       for (int toUpdate = columnStart; toUpdate < index; toUpdate++)
+                                       {
+                                               if (_realizedItem.ContainsKey(toUpdate))
+                                               {
+                                                       var updated = _realizedItem[toUpdate].View.Geometry;
+                                                       if (IsHorizontal)
+                                                       {
+                                                               updated.X += (updatedMaxItemSize - oldMaxItemSize) / 2;
+                                                       }
+                                                       else
+                                                       {
+                                                               updated.Y += (updatedMaxItemSize - oldMaxItemSize) / 2;
+                                                       }
+                                                       _realizedItem[toUpdate].View.Geometry = updated;
+                                               }
+                                       }
+                                       CollectionView.ContentSizeUpdated();
+                               }
+                               rowStartPoint = _accumulatedItemSizes[rowIndex] - updatedMaxItemSize + (updatedMaxItemSize - itemSize) / 2;
+                               columnStartPoint = columnSize * columnIndex;
+
+                               _cached[index] = true;
+                       }
+
+                       return IsHorizontal ?
+                               new Rect(rowStartPoint, columnStartPoint, itemSize, columnSize) :
+                               new Rect(columnStartPoint, rowStartPoint, columnSize, itemSize);
                }
 
                public void Reset()
                {
-                       foreach (var realizedItem in _realizedItem.Values)
+                       foreach (var realizedItem in _realizedItem.Values.ToList())
                        {
                                CollectionView.UnrealizeView(realizedItem.View);
                        }
@@ -222,16 +317,156 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
+               public void ItemSourceUpdated()
+               {
+                       InitializeMeasureCache();
+               }
+
+               public void ItemMeasureInvalidated(int index)
+               {
+                       if (_realizedItem.ContainsKey(index))
+                       {
+                               CollectionView.RequestLayoutItems();
+                       }
+                       if (_hasUnevenRows)
+                       {
+                               if (_cached.Count > index)
+                                       _cached[index] = false;
+                       }
+               }
+
+               void InitializeMeasureCache()
+               {
+                       _baseItemSize = 0;
+                       _scrollCanvasSize = new ESize(0, 0);
+                       _last = new Rect(0, 0, 0, 0);
+
+                       if (!_hasUnevenRows)
+                               return;
+
+                       if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                               return;
+
+                       int n = CollectionView.Count;
+                       _itemSizes = new List<int>();
+                       _cached = new List<bool>();
+                       _accumulatedItemSizes = new List<int>();
+
+                       for (int i = 0; i < n; i++)
+                       {
+                               _cached.Add(false);
+                               _itemSizes.Add(BaseItemSize);
+                               if (i % Span == 0)
+                               {
+                                       int accIndex = i / Span;
+                                       _accumulatedItemSizes.Add((accIndex > 0 ? _accumulatedItemSizes[accIndex - 1] : 0) + _itemSizes[i]);
+                               }
+                       }
+               }
+
+               void BuildAccumulatedSize()
+               {
+                       _accumulatedItemSizes = new List<int>();
+                       int n = _itemSizes.Count;
+                       for (int i = 0; i < n; i++)
+                       {
+                               int accIndex = i / Span;
+                               int prevSize = accIndex > 0 ? _accumulatedItemSizes[accIndex - 1] : 0;
+                               if (i % Span == 0)
+                               {
+                                       _accumulatedItemSizes.Add(prevSize);
+                               }
+                               int columnMax = _accumulatedItemSizes[accIndex] - prevSize;
+                               if (columnMax < _itemSizes[i])
+                               {
+                                       _accumulatedItemSizes[accIndex] += (_itemSizes[i] - columnMax);
+                               }
+                       }
+               }
+
+               void UpdateInsertedSize(int inserted)
+               {
+                       if (!_hasUnevenRows)
+                               return;
+
+                       _cached.Insert(inserted, false);
+                       _itemSizes.Insert(inserted, BaseItemSize);
+                       BuildAccumulatedSize();
+               }
+
+               void UpdateRemovedSize(int removed)
+               {
+                       if (!_hasUnevenRows)
+                               return;
+
+                       _itemSizes.RemoveAt(removed);
+                       _cached.RemoveAt(removed);
+                       BuildAccumulatedSize();
+               }
+
+               void UpdateAccumulatedItemSize(int index, int diff)
+               {
+                       for (int i = index; i < _accumulatedItemSizes.Count; i++)
+                       {
+                               _accumulatedItemSizes[i] += diff;
+                       }
+
+                       if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0)
+                       {
+                               if (IsHorizontal)
+                               {
+                                       _scrollCanvasSize.Width += diff;
+                               }
+                               else
+                               {
+                                       _scrollCanvasSize.Height += diff;
+                               }
+                       }
+               }
+
+               int GetMaxItemSize(int index)
+               {
+                       int columnStart = (index / Span) * Span;
+                       int columnEnd = columnStart + Span - 1;
+                       int max = 0;
+                       for (int i = columnStart; i <= columnEnd && i < _itemSizes.Count; i++)
+                       {
+                               max = Math.Max(max, _itemSizes[i]);
+                       }
+                       return max;
+               }
+
+
                int GetStartIndex(Rect bound, int itemSize)
                {
                        return ViewPortStartPoint(bound) / itemSize * Span;
                }
 
+               int GetStartIndex(Rect bound)
+               {
+                       if (!_hasUnevenRows)
+                       {
+                               return GetStartIndex(bound, BaseItemSize);
+                       }
+
+                       return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortStartPoint(bound)) * Span;
+               }
+
                int GetEndIndex(Rect bound, int itemSize)
                {
                        return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize) * Span;
                }
 
+               int GetEndIndex(Rect bound)
+               {
+                       if (!_hasUnevenRows)
+                       {
+                               return GetEndIndex(bound, BaseItemSize);
+                       }
+
+                       return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortEndPoint(bound)) * Span;
+               }
+
                int ViewPortStartPoint(Rect viewPort)
                {
                        return IsHorizontal ? viewPort.X : viewPort.Y;
@@ -247,6 +482,32 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return IsHorizontal ? viewPort.Width : viewPort.Height;
                }
 
+               static int FindFirstGreaterOrEqualTo(IList<int> data, int value)
+               {
+                       if (data.Count == 0)
+                               return 0;
+
+                       int start = 0;
+                       int end = data.Count - 1;
+                       while (start < end)
+                       {
+                               int mid = (start + end) / 2;
+                               if (data[mid] < value)
+                               {
+                                       start = mid + 1;
+                               }
+                               else
+                               {
+                                       end = mid - 1;
+                               }
+                       }
+                       if (data[start] < value)
+                       {
+                               start++;
+                       }
+                       return start;
+               }
+
                class RealizedItem
                {
                        public ViewHolder View { get; set; }
index dc7bf73..4b4c626 100644 (file)
@@ -23,6 +23,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                void ItemUpdated(int index);
 
+               void ItemSourceUpdated();
+
                void Reset();
+
+               void ItemMeasureInvalidated(int index);
        }
 }
index 80ba766..f29abed 100644 (file)
@@ -7,6 +7,7 @@ using ESize = ElmSharp.Size;
 
 namespace Xamarin.Forms.Platform.Tizen.Native
 {
+       public interface IEmptyAdaptor { }
 
        public abstract class ItemAdaptor : INotifyCollectionChanged
        {
@@ -89,12 +90,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return _itemsSource.IndexOf(item);
                }
 
+               public virtual object GetViewCategory(int index)
+               {
+                       return this;
+               }
+
                public abstract EvasObject CreateNativeView(EvasObject parent);
 
+               public abstract EvasObject CreateNativeView(int index, EvasObject parent);
+
                public abstract void RemoveNativeView(EvasObject native);
 
                public abstract void SetBinding(EvasObject view, int index);
+               public abstract void UnBinding(EvasObject view);
 
                public abstract ESize MeasureItem(int widthConstraint, int heightConstraint);
+
+               public abstract ESize MeasureItem(int index, int widthConstraint, int heightConstraint);
        }
 }
index 46338e1..a03ea39 100644 (file)
@@ -1,5 +1,7 @@
+using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Globalization;
 using ElmSharp;
 using ESize = ElmSharp.Size;
 using XLabel = Xamarin.Forms.Label;
@@ -8,43 +10,43 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 {
        public class ItemDefaultTemplateAdaptor : ItemTemplateAdaptor
        {
+               class ToTextConverter : IValueConverter
+               {
+                       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+                       {
+                               return value?.ToString() ?? string.Empty;
+                       }
+
+                       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
+               }
+
                public ItemDefaultTemplateAdaptor(ItemsView itemsView) : base(itemsView)
                {
                        ItemTemplate = new DataTemplate(() =>
                        {
+                               var label = new XLabel
+                               {
+                                       TextColor = Color.Black,
+                               };
+                               label.SetBinding(XLabel.TextProperty, new Binding(".", converter: new ToTextConverter()));
+
                                return new StackLayout
                                {
                                        BackgroundColor = Color.White,
                                        Padding = 30,
                                        Children =
                                        {
-                                               new XLabel()
+                                               label
                                        }
                                };
                        });
                }
-               public override void SetBinding(EvasObject native, int index)
-               {
-                       ((GetTemplatedView(native) as StackLayout).Children[0] as XLabel).Text = this[index].ToString();
-               }
-
-               public override ESize MeasureItem(int widthConstraint, int heightConstraint)
-               {
-                       var view = (View)ItemTemplate.CreateContent();
-                       if (Count > 0)
-                       {
-                               ((view as StackLayout).Children[0] as XLabel).Text = this[0].ToString();
-                       }
-                       var renderer = Platform.GetOrCreateRenderer(view);
-                       var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request;
-                       renderer.Dispose();
-                       return request.ToPixel();
-               }
        }
 
        public class ItemTemplateAdaptor : ItemAdaptor
        {
                Dictionary<EvasObject, View> _nativeFormsTable = new Dictionary<EvasObject, View>();
+               Dictionary<object, View> _dataBindedViewTable = new Dictionary<object, View>();
                ItemsView _itemsView;
 
                public ItemTemplateAdaptor(ItemsView itemsView) : base(itemsView.ItemsSource)
@@ -66,9 +68,26 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return _nativeFormsTable[evasObject];
                }
 
-               public override EvasObject CreateNativeView(EvasObject parent)
+               public override object GetViewCategory(int index)
                {
-                       var view = ItemTemplate.CreateContent() as View;
+                       if (ItemTemplate is DataTemplateSelector selector)
+                       {
+                               return selector.SelectTemplate(this[index], _itemsView);
+                       }
+                       return base.GetViewCategory(index);
+               }
+
+               public override EvasObject CreateNativeView(int index, EvasObject parent)
+               {
+                       View view = null;
+                       if (ItemTemplate is DataTemplateSelector selector)
+                       {
+                               view = selector.SelectTemplate(this[index], _itemsView).CreateContent() as View;
+                       }
+                       else
+                       {
+                               view = ItemTemplate.CreateContent() as View;
+                       }
                        var renderer = Platform.GetOrCreateRenderer(view);
                        var native = Platform.GetOrCreateRenderer(view).NativeView;
                        view.Parent = _itemsView;
@@ -78,10 +97,16 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return native;
                }
 
+               public override EvasObject CreateNativeView(EvasObject parent)
+               {
+                       return CreateNativeView(0, parent);
+               }
+
                public override void RemoveNativeView(EvasObject native)
                {
                        if (_nativeFormsTable.TryGetValue(native, out View view))
                        {
+                               ResetBindedView(view);
                                Platform.GetRenderer(view)?.Dispose();
                                _nativeFormsTable.Remove(native);
                        }
@@ -91,22 +116,70 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                {
                        if (_nativeFormsTable.TryGetValue(native, out View view))
                        {
+                               ResetBindedView(view);
                                view.BindingContext = this[index];
+                               _dataBindedViewTable[this[index]] = view;
+
+                               view.MeasureInvalidated += OnItemMeasureInvalidated;
+                       }
+               }
+
+               public override void UnBinding(EvasObject native)
+               {
+                       if (_nativeFormsTable.TryGetValue(native, out View view))
+                       {
+                               view.MeasureInvalidated -= OnItemMeasureInvalidated;
+                               ResetBindedView(view);
                        }
                }
 
                public override ESize MeasureItem(int widthConstraint, int heightConstraint)
                {
-                       var view = ItemTemplate.CreateContent() as View;
-                       var renderer = Platform.GetOrCreateRenderer(view);
-                       view.Parent = _itemsView;
-                       if (Count > 0)
-                               view.BindingContext = this[0];
-                       var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request;
-                       renderer.Dispose();
+                       return MeasureItem(0, widthConstraint, heightConstraint);
+               }
+
+               public override ESize MeasureItem(int index, int widthConstraint, int heightConstraint)
+               {
+                       if (_dataBindedViewTable.TryGetValue(this[index], out View createdView) && createdView != null)
+                       {
+                               return createdView.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request.ToPixel();
+                       }
 
-                       return request.ToPixel();
+                       View view = null;
+                       if (ItemTemplate is DataTemplateSelector selector)
+                       {
+                               view = selector.SelectTemplate(this[index], _itemsView).CreateContent() as View;
+                       }
+                       else
+                       {
+                               view = ItemTemplate.CreateContent() as View;
+                       }
+                       using (var renderer = Platform.GetOrCreateRenderer(view))
+                       {
+                               view.Parent = _itemsView;
+                               if (Count > index)
+                                       view.BindingContext = this[index];
+                               var request = view.Measure(Forms.ConvertToScaledDP(widthConstraint), Forms.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request;
+                               return request.ToPixel();
+                       }
                }
 
+               void ResetBindedView(View view)
+               {
+                       if (view.BindingContext != null && _dataBindedViewTable.ContainsKey(view.BindingContext))
+                       {
+                               _dataBindedViewTable[view.BindingContext] = null;
+                       }
+               }
+
+               void OnItemMeasureInvalidated(object sender, EventArgs e)
+               {
+                       var data = (sender as View)?.BindingContext ?? null;
+                       int index = GetItemIndex(data);
+                       if (index != -1)
+                       {
+                               CollectionView.ItemMeasureInvalidated(index);
+                       }
+               }
        }
 }
index d5c2fc4..b13834f 100644 (file)
@@ -13,10 +13,19 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                bool _isLayouting;
                Rect _last;
                Dictionary<int, RealizedItem> _realizedItem = new Dictionary<int, RealizedItem>();
+               List<int> _itemSizes;
+               List<bool> _cached;
+               List<int> _accumulatedItemSizes;
 
-               public LinearLayoutManager(bool isHorizontal)
+               bool _hasUnevenRows;
+               int _baseItemSize;
+
+               public LinearLayoutManager(bool isHorizontal) : this(isHorizontal, ItemSizingStrategy.MeasureFirstItem) { }
+
+               public LinearLayoutManager(bool isHorizontal, ItemSizingStrategy sizingStrategy)
                {
                        IsHorizontal = isHorizontal;
+                       _hasUnevenRows = sizingStrategy == ItemSizingStrategy.MeasureAllItems;
                }
 
                public bool IsHorizontal { get; }
@@ -27,28 +36,64 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                {
                        Reset();
                        _allocatedSize = size;
-                       _scrollCanvasSize = new ESize(0, 0);
+                       InitializeMeasureCache();
                }
 
                ESize _scrollCanvasSize;
 
                public ESize GetScrollCanvasSize()
                {
+                       if (CollectionView.Count == 0 || _allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                       {
+                               return _allocatedSize;
+                       }
+                               
+
                        if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0)
                                return _scrollCanvasSize;
 
-                       var itemCount = CollectionView.Count;
-                       var itemSize = CollectionView.GetItemSize();
+                       int totalItemSize = 0;
+
+                       if (_hasUnevenRows)
+                       {
+                               totalItemSize = _accumulatedItemSizes[_accumulatedItemSizes.Count - 1];
+                       }
+                       else
+                       {
+                               totalItemSize = BaseItemSize * CollectionView.Count;
+                       }
+
                        if (IsHorizontal)
                        {
-                               return _scrollCanvasSize = new ESize(itemCount * itemSize.Width, _allocatedSize.Height);
+                               _scrollCanvasSize = new ESize(totalItemSize, _allocatedSize.Height);
                        }
                        else
                        {
-                               return _scrollCanvasSize = new ESize(_allocatedSize.Width, itemCount * itemSize.Height);
+                               _scrollCanvasSize = new ESize(_allocatedSize.Width, totalItemSize);
+                       }
+
+                       return _scrollCanvasSize;
+               }
+
+               int BaseItemSize
+               {
+                       get
+                       {
+                               if (_baseItemSize == 0)
+                               {
+                                       if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                                               return 0;
+
+                                       var itemBound = CollectionView.GetItemSize(ItemWidthConstraint, ItemHeightConstraint);
+                                       _baseItemSize = IsHorizontal ? itemBound.Width : itemBound.Height;
+                               }
+                               return _baseItemSize;
                        }
                }
 
+               int ItemWidthConstraint => IsHorizontal ? _allocatedSize.Width * 100 : _allocatedSize.Width;
+               int ItemHeightConstraint => IsHorizontal ? _allocatedSize.Height : _allocatedSize.Height * 100;
+
                bool ShouldRearrange(Rect viewport)
                {
                        if (_isLayouting)
@@ -57,8 +102,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                return true;
 
                        var diff = IsHorizontal ? Math.Abs(_last.X - viewport.X) : Math.Abs(_last.Y - viewport.Y);
-                       var margin = IsHorizontal ? CollectionView.GetItemSize().Width : CollectionView.GetItemSize().Height;
-                       if (diff > margin)
+                       if (diff > BaseItemSize)
                                return true;
 
                        return false;
@@ -71,17 +115,15 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        {
                                return;
                        }
+
                        _isLayouting = true;
                        _last = bound;
 
-                       var size = CollectionView.GetItemSize();
-                       var itemSize = IsHorizontal ? size.Width : size.Height;
-                       int startIndex = Math.Max(GetStartIndex(bound, itemSize) - 2, 0);
-                       int endIndex = Math.Min(GetEndIndex(bound, itemSize) + 2, CollectionView.Count - 1);
+                       int startIndex = Math.Max(GetStartIndex(bound) - 2, 0);
+                       int endIndex = Math.Min(GetEndIndex(bound) + 2, CollectionView.Count - 1);
 
                        foreach (var index in _realizedItem.Keys.ToList())
                        {
-
                                if (index < startIndex || index > endIndex)
                                {
                                        CollectionView.UnrealizeView(_realizedItem[index].View);
@@ -138,6 +180,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                }
                        }
 
+                       UpdateInsertedSize(inserted);
+
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
@@ -164,6 +208,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                                _realizedItem.Remove(last);
                        }
 
+                       UpdateRemovedSize(removed);
+
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
@@ -181,24 +227,47 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                public Rect GetItemBound(int index)
                {
-                       var size = CollectionView.GetItemSize();
-                       if (IsHorizontal)
+                       int itemSize = 0;
+                       int startPoint = 0;
+
+                       if (!_hasUnevenRows)
+                       {
+                               itemSize = BaseItemSize;
+                               startPoint = itemSize * index;
+                       }
+                       else if (index >= _itemSizes.Count)
                        {
-                               size.Height = _allocatedSize.Height;
+                               return new Rect(0, 0, 0, 0);
+                       }
+                       else if (_cached[index])
+                       {
+                               itemSize = _itemSizes[index];
+                               startPoint = _accumulatedItemSizes[index] - itemSize;
                        }
                        else
                        {
-                               size.Width = _allocatedSize.Width;
+                               var measured = CollectionView.GetItemSize(index, ItemWidthConstraint, ItemHeightConstraint);
+                               itemSize = IsHorizontal ? measured.Width : measured.Height;
+
+                               if (itemSize != _itemSizes[index])
+                               {
+                                       UpdateAccumulatedItemSize(index, itemSize - _itemSizes[index]);
+                                       _itemSizes[index] = itemSize;
+
+                                       CollectionView.ContentSizeUpdated();
+                               }
+                               startPoint = _accumulatedItemSizes[index] - itemSize;
+                               _cached[index] = true;
                        }
-                       return
-                               IsHorizontal ?
-                               new Rect(index * size.Width, 0, size.Width, size.Height) :
-                               new Rect(0, index * size.Height, size.Width, size.Height);
+
+                       return IsHorizontal ?
+                               new Rect(startPoint, 0, itemSize, _allocatedSize.Height) :
+                               new Rect(0, startPoint, _allocatedSize.Width, itemSize);
                }
 
                public void Reset()
                {
-                       foreach (var realizedItem in _realizedItem.Values)
+                       foreach (var realizedItem in _realizedItem.Values.ToList())
                        {
                                CollectionView.UnrealizeView(realizedItem.View);
                        }
@@ -206,16 +275,78 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        _scrollCanvasSize = new ESize(0, 0);
                }
 
+               public void ItemSourceUpdated()
+               {
+                       InitializeMeasureCache();
+               }
+
+               public void ItemMeasureInvalidated(int index)
+               {
+                       if (_realizedItem.ContainsKey(index))
+                       {
+                               CollectionView.RequestLayoutItems();
+                       }
+                       if (_hasUnevenRows)
+                       {
+                               if (_cached.Count > index)
+                                       _cached[index] = false;
+                       }
+               }
+
+               void InitializeMeasureCache()
+               {
+                       _baseItemSize = 0;
+                       _scrollCanvasSize = new ESize(0, 0);
+
+                       if (!_hasUnevenRows)
+                               return;
+
+                       if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+                               return;
+
+                       int n = CollectionView.Count;
+                       _itemSizes = new List<int>();
+                       _cached = new List<bool>();
+                       _accumulatedItemSizes = new List<int>();
+
+                       for (int i = 0; i < n; i++)
+                       {
+                               _cached.Add(false);
+                               _itemSizes.Add(BaseItemSize);
+                               _accumulatedItemSizes.Add((i > 0 ? _accumulatedItemSizes[i - 1] : 0) + _itemSizes[i]);
+                       }
+               }
+
                int GetStartIndex(Rect bound, int itemSize)
                {
                        return ViewPortStartPoint(bound) / itemSize;
                }
 
+               int GetStartIndex(Rect bound)
+               {
+                       if (!_hasUnevenRows)
+                       {
+                               return GetStartIndex(bound, BaseItemSize);
+                       }
+
+                       return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortStartPoint(bound));
+               }
+
                int GetEndIndex(Rect bound, int itemSize)
                {
                        return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize);
                }
 
+               int GetEndIndex(Rect bound)
+               {
+                       if (!_hasUnevenRows)
+                       {
+                               return GetEndIndex(bound, BaseItemSize);
+                       }
+
+                       return FindFirstGreaterOrEqualTo(_accumulatedItemSizes, ViewPortEndPoint(bound));
+               }
+
                int ViewPortStartPoint(Rect viewPort)
                {
                        return IsHorizontal ? viewPort.X : viewPort.Y;
@@ -231,6 +362,76 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return IsHorizontal ? viewPort.Width : viewPort.Height;
                }
 
+               void UpdateAccumulatedItemSize(int index, int diff)
+               {
+                       for (int i = index; i < _accumulatedItemSizes.Count; i++)
+                       {
+                               _accumulatedItemSizes[i] += diff;
+                       }
+
+                       if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0)
+                       {
+
+                               if (IsHorizontal)
+                               {
+                                       _scrollCanvasSize.Width += diff;
+                               }
+                               else
+                               {
+                                       _scrollCanvasSize.Height += diff;
+                               }
+                       }
+               }
+
+               void UpdateRemovedSize(int removed)
+               {
+                       if (!_hasUnevenRows)
+                               return;
+                       var removedSize = _itemSizes[removed];
+                       _itemSizes.RemoveAt(removed);
+                       UpdateAccumulatedItemSize(removed, -removedSize);
+                       _accumulatedItemSizes.RemoveAt(removed);
+                       _cached.RemoveAt(removed);
+               }
+
+               void UpdateInsertedSize(int inserted)
+               {
+                       if (!_hasUnevenRows)
+                               return;
+
+                       _cached.Insert(inserted, false);
+                       _itemSizes.Insert(inserted, BaseItemSize);
+                       _accumulatedItemSizes.Insert(inserted, 0);
+                       _accumulatedItemSizes[inserted] = inserted > 0 ? _accumulatedItemSizes[inserted - 1] : 0;
+                       UpdateAccumulatedItemSize(inserted, BaseItemSize);
+               }
+
+               static int FindFirstGreaterOrEqualTo(IList<int> data, int value)
+               {
+                       if (data.Count == 0)
+                               return 0;
+
+                       int start = 0;
+                       int end = data.Count - 1;
+                       while (start < end)
+                       {
+                               int mid = (start + end) / 2;
+                               if (data[mid] < value)
+                               {
+                                       start = mid + 1;
+                               }
+                               else
+                               {
+                                       end = mid - 1;
+                               }
+                       }
+                       if (data[start] < value)
+                       {
+                               start++;
+                       }
+                       return start;
+               }
+
                class RealizedItem
                {
                        public ViewHolder View { get; set; }
index 37f2e4f..d39f64c 100644 (file)
@@ -1,5 +1,5 @@
 using System.Collections.Generic;
-using ElmSharp;
+using System.Linq;
 
 namespace Xamarin.Forms.Platform.Tizen.Native
 {
@@ -21,6 +21,14 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        _pool.AddLast(view);
                }
 
+               public ViewHolder GetRecyclerView(object category)
+               {
+                       var holder = _pool.Where(d => d.ViewCategory == category).FirstOrDefault();
+                       if (holder != null)
+                               _pool.Remove(holder);
+                       return holder;
+               }
+
                public ViewHolder GetRecyclerView()
                {
                        if (_pool.First != null)
index 53e6887..00be58f 100644 (file)
@@ -28,6 +28,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        Initialize(parent);
                }
 
+               public object ViewCategory { get; set; }
                public EColor FocusedColor { get; set; }
                public EColor SelectedColor { get; set; }
 
index 3f17f31..1db24c8 100644 (file)
@@ -3,7 +3,7 @@ using EColor = ElmSharp.Color;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
-       public class FrameRenderer : ViewRenderer<Frame, Native.Canvas>
+       public class FrameRenderer : LayoutRenderer
        {
                const int _thickness = 2;
                const int _shadow_shift = 2;
@@ -21,7 +21,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        RegisterPropertyHandler(Frame.HasShadowProperty, UpdateShadowVisibility);
                }
 
-               protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
+               protected override void OnElementChanged(ElementChangedEventArgs<Layout> e)
                {
                        if (Control == null)
                        {
@@ -105,15 +105,15 @@ namespace Xamarin.Forms.Platform.Tizen
 
                void UpdateColor()
                {
-                       if (Element.BorderColor.IsDefault)
+                       if ((Element as Frame).BorderColor.IsDefault)
                                _frame.Color = s_DefaultColor;
                        else
-                               _frame.Color = Element.BorderColor.ToNative();
+                               _frame.Color = (Element as Frame).BorderColor.ToNative();
                }
 
                void UpdateShadowVisibility()
                {
-                       if (Element.HasShadow)
+                       if ((Element as Frame).HasShadow)
                                _shadow.Show();
                        else
                                _shadow.Hide();
index 01e8ec7..5dbc27b 100644 (file)
@@ -14,6 +14,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        RegisterPropertyHandler(ItemsView.ItemsSourceProperty, UpdateItemsSource);
                        RegisterPropertyHandler(ItemsView.ItemTemplateProperty, UpdateAdaptor);
                        RegisterPropertyHandler(ItemsView.ItemsLayoutProperty, UpdateItemsLayout);
+                       RegisterPropertyHandler(ItemsView.ItemSizingStrategyProperty, UpdateSizingStrategy);
                        RegisterPropertyHandler(SelectableItemsView.SelectedItemProperty, UpdateSelectedItem);
                        RegisterPropertyHandler(SelectableItemsView.SelectionModeProperty, UpdateSelectionMode);
                }
@@ -143,12 +144,21 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        if (Element.ItemsLayout != null)
                        {
-                               Control.LayoutManager = Element.ItemsLayout.ToLayoutManager();
+                               Control.LayoutManager = Element.ItemsLayout.ToLayoutManager(Element.ItemSizingStrategy);
                                Control.SnapPointsType = (Element.ItemsLayout as ItemsLayout)?.SnapPointsType ?? SnapPointsType.None;
                                Element.ItemsLayout.PropertyChanged += OnLayoutPropertyChanged;
                        }
                }
 
+               void UpdateSizingStrategy(bool initialize)
+               {
+                       if (initialize)
+                       {
+                               return;
+                       }
+                       Control.LayoutManager = Element.ItemsLayout.ToLayoutManager(Element.ItemSizingStrategy);
+               }
+
                void OnLayoutPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                        if (e.PropertyName == nameof(ItemsLayout.SnapPointsType))
@@ -164,14 +174,14 @@ namespace Xamarin.Forms.Platform.Tizen
 
        static class ItemsLayoutExtension
        {
-               public static ICollectionViewLayoutManager ToLayoutManager(this IItemsLayout layout)
+               public static ICollectionViewLayoutManager ToLayoutManager(this IItemsLayout layout, ItemSizingStrategy sizing = ItemSizingStrategy.MeasureFirstItem)
                {
                        switch (layout)
                        {
                                case ListItemsLayout listItemsLayout:
-                                       return new LinearLayoutManager(listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal);
+                                       return new LinearLayoutManager(listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, sizing);
                                case GridItemsLayout gridItemsLayout:
-                                       return new GridLayoutManager(gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, gridItemsLayout.Span);
+                                       return new GridLayoutManager(gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, gridItemsLayout.Span, sizing);
                                default:
                                        break;
                        }