Skip to content

Commit

Permalink
[Tizen] Add CollectionView Tizen Renderer (xamarin#259)
Browse files Browse the repository at this point in the history
* Tizen CollectionView implementation initial commit

 - first working version
 - only support LinearLayout(listview)

* Add more feature

 * Implement INotifyCollectionChanged
 * Implement EmptyView

* Implemnt SnapPoints, ScrollTo

* Fix MakeVisible option

* Add GridLayoutManager (xamarin#271)

* Fix GetScrollCanvasSize issue

* Enhance ItemAdaptor

 - Measure Item size with data binding
  Item size is determined with first item
 - Extract ItemDefaultTemplateAdaptor from ItemTemplateAdaptor

* Remove debug message

* Implement SelectableItemsView

* Fix ghost view issue

* Fix TV profile button effect issue

* Clean up code space and refactoring ScrollTo method
  • Loading branch information
myroot committed Feb 24, 2019
1 parent f84fab4 commit 9976315
Show file tree
Hide file tree
Showing 12 changed files with 1,659 additions and 2 deletions.
482 changes: 482 additions & 0 deletions Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;

using ESize = ElmSharp.Size;
using XLabel = Xamarin.Forms.Label;

namespace Xamarin.Forms.Platform.Tizen.Native
{
public class EmptyItemAdaptor : ItemTemplateAdaptor
{
static DataTemplate s_defaultEmptyTemplate = new DataTemplate(typeof(EmptyView));
public EmptyItemAdaptor(ItemsView itemsView, IEnumerable items, DataTemplate template) : base(itemsView, items, template)
{
}

public static EmptyItemAdaptor Create(ItemsView itemsView)
{
DataTemplate template = itemsView.EmptyViewTemplate ?? s_defaultEmptyTemplate;
var empty = new List<object>
{
itemsView.EmptyView ?? new object()
};
return new EmptyItemAdaptor(itemsView, empty, template);
}

public override ElmSharp.Size MeasureItem(int widthConstraint, int heightConstraint)
{
return new ESize(widthConstraint, heightConstraint);
}

class EmptyView : StackLayout
{
public EmptyView()
{
HorizontalOptions = LayoutOptions.FillAndExpand;
VerticalOptions = LayoutOptions.FillAndExpand;
Children.Add(
new XLabel
{
Text = "No items found",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
HorizontalTextAlignment = Xamarin.Forms.TextAlignment.Center,
VerticalTextAlignment = Xamarin.Forms.TextAlignment.Center,
}
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ElmSharp;
using ESize = ElmSharp.Size;

namespace Xamarin.Forms.Platform.Tizen.Native
{
public class GridLayoutManager : ICollectionViewLayoutManager
{
ESize _allocatedSize;
ESize _scrollCanvasSize;
bool _isLayouting;
Rect _last;
Dictionary<int, RealizedItem> _realizedItem = new Dictionary<int, RealizedItem>();

public GridLayoutManager(bool isHorizontal, int span = 1)
{
IsHorizontal = isHorizontal;
Span = span;
}

public int Span { get; internal set; }

public bool IsHorizontal { get; }

public ICollectionViewController CollectionView { get; set; }

public void SizeAllocated(ESize size)
{
Reset();
_allocatedSize = size;
_scrollCanvasSize = new ESize(0, 0);
}

public ESize GetScrollCanvasSize()
{
if (_scrollCanvasSize.Width > 0 && _scrollCanvasSize.Height > 0)
return _scrollCanvasSize;

var itemCount = CollectionView.Count;
var itemSize = CollectionView.GetItemSize();
if (IsHorizontal)
{
return _scrollCanvasSize = new ESize((int)Math.Ceiling(itemCount / (double)Span) * itemSize.Width , _allocatedSize.Height);
}
else
{
return _scrollCanvasSize = new ESize(_allocatedSize.Width, (int)Math.Ceiling(itemCount / (double)Span) * itemSize.Height);
}
}

bool ShouldRearrange(Rect viewport)
{
if (_isLayouting)
return false;
if (_last.Size != viewport.Size)
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)
return true;

return false;
}

public void LayoutItems(Rect bound, bool force)
{
// TODO : need to optimization. it was frequently called with similar bound value.
if (!ShouldRearrange(bound) && !force)
{
return;
}
_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);

foreach (var index in _realizedItem.Keys.ToList())
{
if (index < startIndex || index > endIndex)
{
CollectionView.UnrealizeView(_realizedItem[index].View);
_realizedItem.Remove(index);
}
}

var parent = CollectionView.ParentPosition;
for (int i = startIndex; i <= endIndex; i++)
{
EvasObject itemView = null;
if (!_realizedItem.ContainsKey(i))
{
var view = CollectionView.RealizeView(i);

_realizedItem[i] = new RealizedItem
{
View = view,
Index = i,
};
itemView = view;
}
else
{
itemView = _realizedItem[i].View;
}

var itemBound = GetItemBound(i);
itemBound.X += parent.X;
itemBound.Y += parent.Y;
itemView.Geometry = itemBound;
}
_isLayouting = false;
}

public void UpdateSpan(int span)
{
Span = span;
_scrollCanvasSize = new ESize(0, 0);
CollectionView.RequestLayoutItems();
}

public void ItemInserted(int inserted)
{
var items = _realizedItem.Keys.OrderByDescending(key => key);
foreach (var index in items)
{
if (index >= inserted)
{
_realizedItem[index + 1] = _realizedItem[index];
}
}
if (_realizedItem.ContainsKey(inserted))
{
_realizedItem.Remove(inserted);
}
else
{
var last = items.LastOrDefault();
if (last >= inserted)
{
_realizedItem.Remove(last);
}
}

_scrollCanvasSize = new ESize(0, 0);
}

public void ItemRemoved(int removed)
{
if (_realizedItem.ContainsKey(removed))
{
CollectionView.UnrealizeView(_realizedItem[removed].View);
_realizedItem.Remove(removed);
}

var items = _realizedItem.Keys.OrderBy(key => key);
foreach (var index in items)
{
if (index > removed)
{
_realizedItem[index - 1] = _realizedItem[index];
}
}

var last = items.LastOrDefault();
if (last > removed)
{
_realizedItem.Remove(last);
}

_scrollCanvasSize = new ESize(0, 0);
}

public void ItemUpdated(int index)
{
if (_realizedItem.ContainsKey(index))
{
var bound = _realizedItem[index].View.Geometry;
CollectionView.UnrealizeView(_realizedItem[index].View);
var view = CollectionView.RealizeView(index);
_realizedItem[index].View = view;
view.Geometry = bound;
}
}

public Rect GetItemBound(int index)
{
var size = CollectionView.GetItemSize();
if (IsHorizontal)
{
size.Height = _allocatedSize.Height / Span;
}
else
{
size.Width = _allocatedSize.Width / Span;
}

int rowIndex = index / Span;
int colIndex = index % Span;
var colSize = IsHorizontal ? size.Height : size.Width;

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);
}

public void Reset()
{
foreach (var realizedItem in _realizedItem.Values)
{
CollectionView.UnrealizeView(realizedItem.View);
}
_realizedItem.Clear();
_scrollCanvasSize = new ESize(0, 0);
}

int GetStartIndex(Rect bound, int itemSize)
{
return ViewPortStartPoint(bound) / itemSize * Span;
}

int GetEndIndex(Rect bound, int itemSize)
{
return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize) * Span;
}

int ViewPortStartPoint(Rect viewPort)
{
return IsHorizontal ? viewPort.X : viewPort.Y;
}

int ViewPortEndPoint(Rect viewPort)
{
return ViewPortStartPoint(viewPort) + ViewPortSize(viewPort);
}

int ViewPortSize(Rect viewPort)
{
return IsHorizontal ? viewPort.Width : viewPort.Height;
}

class RealizedItem
{
public ViewHolder View { get; set; }
public int Index { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using ElmSharp;
using ESize = ElmSharp.Size;

namespace Xamarin.Forms.Platform.Tizen.Native
{
public interface ICollectionViewLayoutManager
{
ICollectionViewController CollectionView { get; set; }

bool IsHorizontal { get; }

void SizeAllocated(ESize size);

ESize GetScrollCanvasSize();

void LayoutItems(Rect bound, bool force = false);

Rect GetItemBound(int index);

void ItemInserted(int index);

void ItemRemoved(int index);

void ItemUpdated(int index);

void Reset();
}
}
Loading

0 comments on commit 9976315

Please sign in to comment.