зеркало из https://github.com/DeGsoft/maui-linux.git
* [Tizen] Add CollectionView Tizen Renderer (#259) * 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 (#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 * Fix collectionView emptyview (#277)
This commit is contained in:
Родитель
409271bbfa
Коммит
07ada19a7d
|
@ -0,0 +1,482 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
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
|
||||
{
|
||||
public class CollectionView : EBox, ICollectionViewController
|
||||
{
|
||||
RecyclerPool _pool = new RecyclerPool();
|
||||
ICollectionViewLayoutManager _layoutManager;
|
||||
ItemAdaptor _adaptor;
|
||||
EBox _innerLayout;
|
||||
|
||||
Dictionary<ViewHolder, int> _viewHolderIndexTable = new Dictionary<ViewHolder, int>();
|
||||
ViewHolder _lastSelectedViewHolder;
|
||||
int _selectedItemIndex = -1;
|
||||
CollectionViewSelectionMode _selectionMode = CollectionViewSelectionMode.None;
|
||||
|
||||
bool _requestLayoutItems = false;
|
||||
SnapPointsType _snapPoints;
|
||||
ESize _itemSize = new ESize(-1, -1);
|
||||
|
||||
public CollectionView(EvasObject parent) : base(parent)
|
||||
{
|
||||
SetLayoutCallback(OnLayout);
|
||||
Scroller = CreateScroller(parent);
|
||||
Scroller.Show();
|
||||
PackEnd(Scroller);
|
||||
Scroller.Scrolled += OnScrolled;
|
||||
|
||||
_innerLayout = new EBox(parent);
|
||||
_innerLayout.SetLayoutCallback(OnInnerLayout);
|
||||
_innerLayout.Show();
|
||||
Scroller.SetContent(_innerLayout);
|
||||
}
|
||||
|
||||
public CollectionViewSelectionMode SelectionMode
|
||||
{
|
||||
get => _selectionMode;
|
||||
set
|
||||
{
|
||||
_selectionMode = value;
|
||||
UpdateSelectionMode();
|
||||
}
|
||||
}
|
||||
|
||||
public int SelectedItemIndex
|
||||
{
|
||||
get => _selectedItemIndex;
|
||||
set
|
||||
{
|
||||
if (_selectedItemIndex != value)
|
||||
{
|
||||
_selectedItemIndex = value;
|
||||
UpdateSelectedItemIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SnapPointsType SnapPointsType
|
||||
{
|
||||
get => _snapPoints;
|
||||
set
|
||||
{
|
||||
_snapPoints = value;
|
||||
UpdateSnapPointsType(_snapPoints);
|
||||
}
|
||||
}
|
||||
|
||||
protected EScroller Scroller { get; }
|
||||
|
||||
public ICollectionViewLayoutManager LayoutManager
|
||||
{
|
||||
get => _layoutManager;
|
||||
set
|
||||
{
|
||||
OnLayoutManagerChanging();
|
||||
_layoutManager = value;
|
||||
OnLayoutManagerChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ItemAdaptor Adaptor
|
||||
{
|
||||
get => _adaptor;
|
||||
set
|
||||
{
|
||||
OnAdaptorChanging();
|
||||
_adaptor = value;
|
||||
_adaptor.CollectionView = this;
|
||||
OnAdaptorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ICollectionViewController.Count => Adaptor?.Count ?? 0;
|
||||
|
||||
EPoint ICollectionViewController.ParentPosition => new EPoint
|
||||
{
|
||||
X = Scroller.Geometry.X - Scroller.CurrentRegion.X,
|
||||
Y = Scroller.Geometry.Y - Scroller.CurrentRegion.Y
|
||||
};
|
||||
|
||||
ESize AllocatedSize { get; set; }
|
||||
|
||||
Rect ViewPort => Scroller.CurrentRegion;
|
||||
|
||||
public void ScrollTo(int index, ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true)
|
||||
{
|
||||
var itemBound = LayoutManager.GetItemBound(index);
|
||||
int itemStart;
|
||||
int itemEnd;
|
||||
int scrollStart;
|
||||
int scrollEnd;
|
||||
int itemPadding = 0;
|
||||
int itemSize;
|
||||
int viewportSize;
|
||||
|
||||
if (LayoutManager.IsHorizontal)
|
||||
{
|
||||
itemStart = itemBound.Left;
|
||||
itemEnd = itemBound.Right;
|
||||
itemSize = itemBound.Width;
|
||||
scrollStart = Scroller.CurrentRegion.Left;
|
||||
scrollEnd = Scroller.CurrentRegion.Right;
|
||||
viewportSize = AllocatedSize.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemStart = itemBound.Top;
|
||||
itemEnd = itemBound.Bottom;
|
||||
itemSize = itemBound.Height;
|
||||
scrollStart = Scroller.CurrentRegion.Top;
|
||||
scrollEnd = Scroller.CurrentRegion.Bottom;
|
||||
viewportSize = AllocatedSize.Height;
|
||||
}
|
||||
|
||||
if (position == ScrollToPosition.MakeVisible)
|
||||
{
|
||||
if (itemStart < scrollStart)
|
||||
{
|
||||
position = ScrollToPosition.Start;
|
||||
}
|
||||
else if (itemEnd > scrollEnd)
|
||||
{
|
||||
position = ScrollToPosition.End;
|
||||
}
|
||||
else
|
||||
{
|
||||
// already visible
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemSize < viewportSize)
|
||||
{
|
||||
switch (position)
|
||||
{
|
||||
case ScrollToPosition.Center:
|
||||
itemPadding = (viewportSize - itemSize) / 2;
|
||||
break;
|
||||
case ScrollToPosition.End:
|
||||
itemPadding = (viewportSize - itemSize);
|
||||
break;
|
||||
}
|
||||
itemSize = viewportSize;
|
||||
}
|
||||
|
||||
if (LayoutManager.IsHorizontal)
|
||||
{
|
||||
itemBound.X -= itemPadding;
|
||||
itemBound.Width = itemSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemBound.Y -= itemPadding;
|
||||
itemBound.Height = itemSize;
|
||||
}
|
||||
|
||||
Scroller.ScrollTo(itemBound, animate);
|
||||
}
|
||||
|
||||
public void ScrollTo(object item, ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true)
|
||||
{
|
||||
ScrollTo(Adaptor.GetItemIndex(item), position, animate);
|
||||
}
|
||||
|
||||
void ICollectionViewController.RequestLayoutItems() => RequestLayoutItems();
|
||||
|
||||
|
||||
ESize ICollectionViewController.GetItemSize()
|
||||
{
|
||||
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.Width = Math.Max(_itemSize.Width, 10);
|
||||
_itemSize.Height = Math.Max(_itemSize.Height, 10);
|
||||
|
||||
if (_snapPoints != SnapPointsType.None)
|
||||
{
|
||||
Scroller.SetPageSize(_itemSize.Width, _itemSize.Height);
|
||||
}
|
||||
return _itemSize;
|
||||
}
|
||||
|
||||
ViewHolder ICollectionViewController.RealizeView(int index)
|
||||
{
|
||||
if (Adaptor == null)
|
||||
return null;
|
||||
|
||||
var holder = _pool.GetRecyclerView();
|
||||
if (holder != null)
|
||||
{
|
||||
holder.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
var content = Adaptor.CreateNativeView(this);
|
||||
holder = new ViewHolder(this);
|
||||
holder.RequestSelected += OnRequestItemSelection;
|
||||
holder.Content = content;
|
||||
_innerLayout.PackEnd(holder);
|
||||
}
|
||||
|
||||
Adaptor.SetBinding(holder.Content, index);
|
||||
_viewHolderIndexTable[holder] = index;
|
||||
if (index == SelectedItemIndex)
|
||||
{
|
||||
OnRequestItemSelection(holder, EventArgs.Empty);
|
||||
}
|
||||
return holder;
|
||||
}
|
||||
|
||||
void OnRequestItemSelection(object sender, EventArgs e)
|
||||
{
|
||||
if (SelectionMode == CollectionViewSelectionMode.None)
|
||||
return;
|
||||
|
||||
|
||||
if (_lastSelectedViewHolder != null)
|
||||
{
|
||||
_lastSelectedViewHolder.State = ViewHolderState.Normal;
|
||||
}
|
||||
|
||||
_lastSelectedViewHolder = sender as ViewHolder;
|
||||
if (_lastSelectedViewHolder != null)
|
||||
{
|
||||
_lastSelectedViewHolder.State = ViewHolderState.Selected;
|
||||
if (_viewHolderIndexTable.TryGetValue(_lastSelectedViewHolder, out int index))
|
||||
{
|
||||
_selectedItemIndex = index;
|
||||
Adaptor?.SendItemSelected(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICollectionViewController.UnrealizeView(ViewHolder view)
|
||||
{
|
||||
_viewHolderIndexTable.Remove(view);
|
||||
view.ResetState();
|
||||
view.Hide();
|
||||
_pool.AddRecyclerView(view);
|
||||
}
|
||||
|
||||
protected virtual EScroller CreateScroller(EvasObject parent)
|
||||
{
|
||||
return new EScroller(parent);
|
||||
}
|
||||
|
||||
void UpdateSelectedItemIndex()
|
||||
{
|
||||
if (SelectionMode == CollectionViewSelectionMode.None)
|
||||
return;
|
||||
|
||||
ViewHolder holder = null;
|
||||
foreach (var item in _viewHolderIndexTable)
|
||||
{
|
||||
if (item.Value == SelectedItemIndex)
|
||||
{
|
||||
holder = item.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
OnRequestItemSelection(holder, EventArgs.Empty);
|
||||
}
|
||||
|
||||
void UpdateSelectionMode()
|
||||
{
|
||||
if (SelectionMode == CollectionViewSelectionMode.None)
|
||||
{
|
||||
if (_lastSelectedViewHolder != null)
|
||||
{
|
||||
_lastSelectedViewHolder.State = ViewHolderState.Normal;
|
||||
_lastSelectedViewHolder = null;
|
||||
}
|
||||
_selectedItemIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnLayoutManagerChanging()
|
||||
{
|
||||
_layoutManager?.Reset();
|
||||
}
|
||||
|
||||
void OnLayoutManagerChanged()
|
||||
{
|
||||
if (_layoutManager == null)
|
||||
return;
|
||||
|
||||
_layoutManager.CollectionView = this;
|
||||
_layoutManager.SizeAllocated(AllocatedSize);
|
||||
RequestLayoutItems();
|
||||
}
|
||||
|
||||
void OnAdaptorChanging()
|
||||
{
|
||||
_layoutManager?.Reset();
|
||||
if (Adaptor != null)
|
||||
{
|
||||
_pool.Clear(Adaptor);
|
||||
(Adaptor as INotifyCollectionChanged).CollectionChanged -= OnCollectionChanged;
|
||||
Adaptor.CollectionView = null;
|
||||
}
|
||||
}
|
||||
void OnAdaptorChanged()
|
||||
{
|
||||
if (_adaptor == null)
|
||||
return;
|
||||
|
||||
_itemSize = new ESize(-1, -1);
|
||||
(Adaptor as INotifyCollectionChanged).CollectionChanged += OnCollectionChanged;
|
||||
|
||||
RequestLayoutItems();
|
||||
|
||||
if (LayoutManager != null)
|
||||
{
|
||||
var itemSize = (this as ICollectionViewController).GetItemSize();
|
||||
}
|
||||
}
|
||||
|
||||
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
int idx = e.NewStartingIndex;
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
LayoutManager.ItemInserted(idx++);
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
int idx = e.OldStartingIndex;
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
LayoutManager.ItemRemoved(idx);
|
||||
}
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Move)
|
||||
{
|
||||
LayoutManager.ItemRemoved(e.OldStartingIndex);
|
||||
LayoutManager.ItemInserted(e.NewStartingIndex);
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
LayoutManager.ItemUpdated(e.NewStartingIndex);
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
LayoutManager.Reset();
|
||||
}
|
||||
RequestLayoutItems();
|
||||
}
|
||||
|
||||
Rect _lastGeometry;
|
||||
void OnLayout()
|
||||
{
|
||||
if (_lastGeometry == Geometry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastGeometry = Geometry;
|
||||
Scroller.Geometry = Geometry;
|
||||
Scroller.ScrollBlock = ScrollBlock.None;
|
||||
AllocatedSize = Geometry.Size;
|
||||
_itemSize = new ESize(-1, -1);
|
||||
|
||||
if (_adaptor != null && _layoutManager != null)
|
||||
{
|
||||
_layoutManager?.SizeAllocated(Geometry.Size);
|
||||
_layoutManager?.LayoutItems(ViewPort);
|
||||
}
|
||||
}
|
||||
|
||||
void RequestLayoutItems()
|
||||
{
|
||||
if (!_requestLayoutItems)
|
||||
{
|
||||
_requestLayoutItems = true;
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
_requestLayoutItems = false;
|
||||
if (_adaptor != null && _layoutManager != null)
|
||||
{
|
||||
OnInnerLayout();
|
||||
_layoutManager?.LayoutItems(ViewPort, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnInnerLayout()
|
||||
{
|
||||
var size = _layoutManager.GetScrollCanvasSize();
|
||||
_innerLayout.MinimumWidth = size.Width;
|
||||
_innerLayout.MinimumHeight = size.Height;
|
||||
}
|
||||
|
||||
void OnScrolled(object sender, EventArgs e)
|
||||
{
|
||||
_layoutManager.LayoutItems(Scroller.CurrentRegion);
|
||||
}
|
||||
|
||||
void UpdateSnapPointsType(SnapPointsType snapPoints)
|
||||
{
|
||||
var itemSize = new ESize(0, 0);
|
||||
switch (snapPoints)
|
||||
{
|
||||
case SnapPointsType.None:
|
||||
Scroller.HorizontalPageScrollLimit = 0;
|
||||
Scroller.VerticalPageScrollLimit = 0;
|
||||
break;
|
||||
case SnapPointsType.MandatorySingle:
|
||||
Scroller.HorizontalPageScrollLimit = 1;
|
||||
Scroller.VerticalPageScrollLimit = 1;
|
||||
itemSize = (this as ICollectionViewController).GetItemSize();
|
||||
break;
|
||||
case SnapPointsType.Mandatory:
|
||||
Scroller.HorizontalPageScrollLimit = 0;
|
||||
Scroller.VerticalPageScrollLimit = 0;
|
||||
itemSize = (this as ICollectionViewController).GetItemSize();
|
||||
break;
|
||||
}
|
||||
Scroller.SetPageSize(itemSize.Width, itemSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICollectionViewController
|
||||
{
|
||||
EPoint ParentPosition { get; }
|
||||
|
||||
ViewHolder RealizeView(int index);
|
||||
|
||||
void UnrealizeView(ViewHolder view);
|
||||
|
||||
void RequestLayoutItems();
|
||||
|
||||
int Count { get; }
|
||||
|
||||
ESize GetItemSize();
|
||||
}
|
||||
|
||||
public enum CollectionViewSelectionMode
|
||||
{
|
||||
None,
|
||||
Single,
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
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 = null;
|
||||
if (itemsView.EmptyView is View emptyView)
|
||||
{
|
||||
template = new DataTemplate(() =>
|
||||
{
|
||||
return emptyView;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using ElmSharp;
|
||||
using ESize = ElmSharp.Size;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen.Native
|
||||
{
|
||||
|
||||
public abstract class ItemAdaptor : INotifyCollectionChanged
|
||||
{
|
||||
IList _itemsSource;
|
||||
|
||||
public CollectionView CollectionView { get; set; }
|
||||
|
||||
protected ItemAdaptor(IEnumerable items)
|
||||
{
|
||||
SetItemsSource(items);
|
||||
}
|
||||
|
||||
public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
|
||||
|
||||
public virtual void SendItemSelected(int index)
|
||||
{
|
||||
ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs(this[index], index));
|
||||
}
|
||||
|
||||
public void RequestItemSelected(object item)
|
||||
{
|
||||
if (CollectionView != null)
|
||||
{
|
||||
CollectionView.SelectedItemIndex = _itemsSource.IndexOf(item);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetItemsSource(IEnumerable items)
|
||||
{
|
||||
switch (items)
|
||||
{
|
||||
case IList list:
|
||||
_itemsSource = list;
|
||||
_observableCollection = list as INotifyCollectionChanged;
|
||||
break;
|
||||
case IEnumerable<object> generic:
|
||||
_itemsSource = new List<object>(generic);
|
||||
break;
|
||||
case IEnumerable _:
|
||||
_itemsSource = new List<object>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
_itemsSource.Add(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _itemsSource[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => _itemsSource.Count;
|
||||
|
||||
INotifyCollectionChanged _observableCollection;
|
||||
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
if (_observableCollection != null)
|
||||
{
|
||||
_observableCollection.CollectionChanged += value;
|
||||
}
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (_observableCollection != null)
|
||||
{
|
||||
_observableCollection.CollectionChanged -= value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetItemIndex(object item)
|
||||
{
|
||||
return _itemsSource.IndexOf(item);
|
||||
}
|
||||
|
||||
public abstract EvasObject CreateNativeView(EvasObject parent);
|
||||
|
||||
public abstract void RemoveNativeView(EvasObject native);
|
||||
|
||||
public abstract void SetBinding(EvasObject view, int index);
|
||||
|
||||
public abstract ESize MeasureItem(int widthConstraint, int heightConstraint);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ElmSharp;
|
||||
using ESize = ElmSharp.Size;
|
||||
using XLabel = Xamarin.Forms.Label;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen.Native
|
||||
{
|
||||
public class ItemDefaultTemplateAdaptor : ItemTemplateAdaptor
|
||||
{
|
||||
public ItemDefaultTemplateAdaptor(ItemsView itemsView) : base(itemsView)
|
||||
{
|
||||
ItemTemplate = new DataTemplate(() =>
|
||||
{
|
||||
return new StackLayout
|
||||
{
|
||||
BackgroundColor = Color.White,
|
||||
Padding = 30,
|
||||
Children =
|
||||
{
|
||||
new XLabel()
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
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>();
|
||||
ItemsView _itemsView;
|
||||
|
||||
public ItemTemplateAdaptor(ItemsView itemsView) : base(itemsView.ItemsSource)
|
||||
{
|
||||
ItemTemplate = itemsView.ItemTemplate;
|
||||
_itemsView = itemsView;
|
||||
}
|
||||
|
||||
protected ItemTemplateAdaptor(ItemsView itemsView, IEnumerable items, DataTemplate template) : base(items)
|
||||
{
|
||||
ItemTemplate = template;
|
||||
_itemsView = itemsView;
|
||||
}
|
||||
|
||||
protected DataTemplate ItemTemplate { get; set; }
|
||||
|
||||
protected View GetTemplatedView(EvasObject evasObject)
|
||||
{
|
||||
return _nativeFormsTable[evasObject];
|
||||
}
|
||||
|
||||
public override EvasObject CreateNativeView(EvasObject parent)
|
||||
{
|
||||
var view = ItemTemplate.CreateContent() as View;
|
||||
var renderer = Platform.GetOrCreateRenderer(view);
|
||||
var native = Platform.GetOrCreateRenderer(view).NativeView;
|
||||
view.Parent = _itemsView;
|
||||
(renderer as LayoutRenderer)?.RegisterOnLayoutUpdated();
|
||||
|
||||
_nativeFormsTable[native] = view;
|
||||
return native;
|
||||
}
|
||||
|
||||
public override void RemoveNativeView(EvasObject native)
|
||||
{
|
||||
if (_nativeFormsTable.TryGetValue(native, out View view))
|
||||
{
|
||||
Platform.GetRenderer(view)?.Dispose();
|
||||
_nativeFormsTable.Remove(native);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetBinding(EvasObject native, int index)
|
||||
{
|
||||
if (_nativeFormsTable.TryGetValue(native, out View view))
|
||||
{
|
||||
view.BindingContext = this[index];
|
||||
}
|
||||
}
|
||||
|
||||
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 request.ToPixel();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ElmSharp;
|
||||
using ESize = ElmSharp.Size;
|
||||
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen.Native
|
||||
{
|
||||
public class LinearLayoutManager : ICollectionViewLayoutManager
|
||||
{
|
||||
ESize _allocatedSize;
|
||||
bool _isLayouting;
|
||||
Rect _last;
|
||||
Dictionary<int, RealizedItem> _realizedItem = new Dictionary<int, RealizedItem>();
|
||||
|
||||
public LinearLayoutManager(bool isHorizontal)
|
||||
{
|
||||
IsHorizontal = isHorizontal;
|
||||
}
|
||||
|
||||
public bool IsHorizontal { get; }
|
||||
|
||||
public ICollectionViewController CollectionView { get; set; }
|
||||
|
||||
public void SizeAllocated(ESize size)
|
||||
{
|
||||
Reset();
|
||||
_allocatedSize = size;
|
||||
_scrollCanvasSize = new ESize(0, 0);
|
||||
}
|
||||
|
||||
ESize _scrollCanvasSize;
|
||||
|
||||
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(itemCount * itemSize.Width, _allocatedSize.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _scrollCanvasSize = new ESize(_allocatedSize.Width, itemCount * 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 startIndex = Math.Max(GetStartIndex(bound, itemSize) - 2, 0);
|
||||
int endIndex = Math.Min(GetEndIndex(bound, itemSize) + 2, 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 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
size.Width = _allocatedSize.Width;
|
||||
}
|
||||
return
|
||||
IsHorizontal ?
|
||||
new Rect(index * size.Width, 0, size.Width, size.Height) :
|
||||
new Rect(0, index * 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;
|
||||
}
|
||||
|
||||
int GetEndIndex(Rect bound, int itemSize)
|
||||
{
|
||||
return (int)Math.Ceiling(ViewPortEndPoint(bound) / (double)itemSize);
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using ElmSharp;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen.Native
|
||||
{
|
||||
class RecyclerPool
|
||||
{
|
||||
LinkedList<ViewHolder> _pool = new LinkedList<ViewHolder>();
|
||||
|
||||
public void Clear(ItemAdaptor adaptor)
|
||||
{
|
||||
foreach (var item in _pool)
|
||||
{
|
||||
adaptor.RemoveNativeView(item);
|
||||
}
|
||||
_pool.Clear();
|
||||
}
|
||||
|
||||
public void AddRecyclerView(ViewHolder view)
|
||||
{
|
||||
_pool.AddLast(view);
|
||||
}
|
||||
|
||||
public ViewHolder GetRecyclerView()
|
||||
{
|
||||
if (_pool.First != null)
|
||||
{
|
||||
var fisrt = _pool.First;
|
||||
_pool.RemoveFirst();
|
||||
return fisrt.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using ElmSharp;
|
||||
using ERectangle = ElmSharp.Rectangle;
|
||||
using EColor = ElmSharp.Color;
|
||||
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen.Native
|
||||
{
|
||||
|
||||
public enum ViewHolderState
|
||||
{
|
||||
Normal,
|
||||
Selected,
|
||||
}
|
||||
|
||||
public class ViewHolder : Box
|
||||
{
|
||||
static readonly EColor s_defaultFocusEffectColor = EColor.FromRgba(244, 244, 244, 200);
|
||||
static readonly EColor s_defaultSelectedColor = EColor.FromRgba(227, 242, 253, 200);
|
||||
|
||||
ERectangle _background;
|
||||
Button _focusArea;
|
||||
EvasObject _content;
|
||||
ViewHolderState _state;
|
||||
|
||||
public ViewHolder(EvasObject parent) : base(parent)
|
||||
{
|
||||
Initialize(parent);
|
||||
}
|
||||
|
||||
public EColor FocusedColor { get; set; }
|
||||
public EColor SelectedColor { get; set; }
|
||||
|
||||
EColor EffectiveFocusedColor => FocusedColor == EColor.Default ? s_defaultFocusEffectColor : FocusedColor;
|
||||
EColor EffectiveSelectedColor => SelectedColor == EColor.Default ? s_defaultSelectedColor : FocusedColor;
|
||||
|
||||
EColor FocusSelectedColor
|
||||
{
|
||||
get
|
||||
{
|
||||
var color1 = EffectiveFocusedColor;
|
||||
var color2 = EffectiveSelectedColor;
|
||||
return new EColor(
|
||||
(color1.R + color2.R) / 2,
|
||||
(color1.G + color2.G) / 2,
|
||||
(color1.B + color2.B) / 2,
|
||||
(color1.A + color2.A) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
public EvasObject Content
|
||||
{
|
||||
get
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_content != null)
|
||||
{
|
||||
UnPack(_content);
|
||||
}
|
||||
_content = value;
|
||||
if (_content != null)
|
||||
{
|
||||
PackAfter(_content, _background);
|
||||
_content.StackBelow(_focusArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ViewHolderState State
|
||||
{
|
||||
get { return _state; }
|
||||
set
|
||||
{
|
||||
_state = value;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler Selected;
|
||||
public event EventHandler RequestSelected;
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
State = ViewHolderState.Normal;
|
||||
_background.Color = EColor.Transparent;
|
||||
}
|
||||
|
||||
protected void SendSelected()
|
||||
{
|
||||
Selected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected void Initialize(EvasObject parent)
|
||||
{
|
||||
SetLayoutCallback(OnLayout);
|
||||
|
||||
_background = new ERectangle(parent)
|
||||
{
|
||||
Color = EColor.Transparent
|
||||
};
|
||||
_background.Show();
|
||||
|
||||
_focusArea = new Button(parent);
|
||||
_focusArea.Color = EColor.Transparent;
|
||||
_focusArea.BackgroundColor = EColor.Transparent;
|
||||
_focusArea.SetPartColor("effect", EColor.Transparent);
|
||||
_focusArea.Clicked += OnClicked;
|
||||
_focusArea.Focused += OnFocused;
|
||||
_focusArea.Unfocused += OnFocused;
|
||||
_focusArea.KeyUp += OnKeyUp;
|
||||
_focusArea.RepeatEvents = true;
|
||||
_focusArea.Show();
|
||||
|
||||
PackEnd(_background);
|
||||
PackEnd(_focusArea);
|
||||
FocusedColor = EColor.Default;
|
||||
Show();
|
||||
}
|
||||
|
||||
protected virtual void OnFocused(object sender, EventArgs e)
|
||||
{
|
||||
if (_focusArea.IsFocused)
|
||||
{
|
||||
_background.Color = State == ViewHolderState.Selected ? FocusSelectedColor : EffectiveFocusedColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_background.Color = State == ViewHolderState.Selected ? EffectiveSelectedColor : EColor.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnClicked(object sender, EventArgs e)
|
||||
{
|
||||
RequestSelected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnLayout()
|
||||
{
|
||||
_background.Geometry = Geometry;
|
||||
_focusArea.Geometry = Geometry;
|
||||
if (_content != null)
|
||||
{
|
||||
_content.Geometry = Geometry;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateState()
|
||||
{
|
||||
if (State == ViewHolderState.Normal)
|
||||
{
|
||||
_background.Color = _focusArea.IsFocused ? EffectiveFocusedColor : EColor.Transparent;
|
||||
} else
|
||||
{
|
||||
_background.Color = _focusArea.IsFocused ? FocusSelectedColor : SelectedColor;
|
||||
SendSelected();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnKeyUp(object sender, EvasKeyEventArgs e)
|
||||
{
|
||||
if (e.KeyName == "Enter" && _focusArea.IsFocused)
|
||||
{
|
||||
RequestSelected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ using System.Reflection;
|
|||
[assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))]
|
||||
[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
|
||||
[assembly: ExportRenderer(typeof(ImageButton), typeof(ImageButtonRenderer))]
|
||||
[assembly: ExportRenderer(typeof(ItemsView), typeof(ItemsViewRenderer))]
|
||||
|
||||
[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
|
||||
[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImageSourceHandler))]
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
using Xamarin.Forms.Platform.Tizen.Native;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen
|
||||
{
|
||||
public class ItemsViewRenderer : ViewRenderer<ItemsView, Native.CollectionView>
|
||||
{
|
||||
INotifyCollectionChanged _observableSource;
|
||||
|
||||
public ItemsViewRenderer()
|
||||
{
|
||||
RegisterPropertyHandler(ItemsView.ItemsSourceProperty, UpdateItemsSource);
|
||||
RegisterPropertyHandler(ItemsView.ItemTemplateProperty, UpdateAdaptor);
|
||||
RegisterPropertyHandler(ItemsView.ItemsLayoutProperty, UpdateItemsLayout);
|
||||
RegisterPropertyHandler(SelectableItemsView.SelectedItemProperty, UpdateSelectedItem);
|
||||
RegisterPropertyHandler(SelectableItemsView.SelectionModeProperty, UpdateSelectionMode);
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<ItemsView> e)
|
||||
{
|
||||
if (Control == null)
|
||||
{
|
||||
SetNativeControl(new Native.CollectionView(Forms.NativeParent));
|
||||
}
|
||||
|
||||
if (e.NewElement != null)
|
||||
{
|
||||
e.NewElement.ScrollToRequested += OnScrollToRequest;
|
||||
}
|
||||
|
||||
base.OnElementChanged(e);
|
||||
UpdateAdaptor(false);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (Element != null)
|
||||
{
|
||||
Element.ScrollToRequested -= OnScrollToRequest;
|
||||
Element.ItemsLayout.PropertyChanged -= OnLayoutPropertyChanged;
|
||||
}
|
||||
if (_observableSource != null)
|
||||
{
|
||||
_observableSource.CollectionChanged -= OnCollectionChanged;
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
void UpdateSelectedItem(bool initialize)
|
||||
{
|
||||
if (initialize)
|
||||
return;
|
||||
|
||||
if (Element is SelectableItemsView selectable)
|
||||
{
|
||||
Control?.Adaptor?.RequestItemSelected(selectable.SelectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSelectionMode()
|
||||
{
|
||||
if (Element is SelectableItemsView selectable)
|
||||
{
|
||||
Control.SelectionMode = selectable.SelectionMode == SelectionMode.None ? CollectionViewSelectionMode.None : CollectionViewSelectionMode.Single;
|
||||
}
|
||||
}
|
||||
|
||||
void OnScrollToRequest(object sender, ScrollToRequestEventArgs e)
|
||||
{
|
||||
if (e.Mode == ScrollToMode.Position)
|
||||
{
|
||||
Control.ScrollTo(e.Index, e.ScrollToPosition, e.IsAnimated);
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.ScrollTo(e.Item, e.ScrollToPosition, e.IsAnimated);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateItemsSource(bool initialize)
|
||||
{
|
||||
if (Element.ItemsSource is INotifyCollectionChanged collectionChanged)
|
||||
{
|
||||
if (_observableSource != null)
|
||||
{
|
||||
_observableSource.CollectionChanged -= OnCollectionChanged;
|
||||
}
|
||||
_observableSource = collectionChanged;
|
||||
_observableSource.CollectionChanged += OnCollectionChanged;
|
||||
}
|
||||
UpdateAdaptor(initialize);
|
||||
}
|
||||
|
||||
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (Element.ItemsSource == null || !Element.ItemsSource.Cast<object>().Any())
|
||||
{
|
||||
Control.Adaptor = EmptyItemAdaptor.Create(Element);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Control.Adaptor is EmptyItemAdaptor)
|
||||
{
|
||||
UpdateAdaptor(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAdaptor(bool initialize)
|
||||
{
|
||||
if (!initialize)
|
||||
{
|
||||
if (Element.ItemsSource == null || !Element.ItemsSource.Cast<object>().Any())
|
||||
{
|
||||
Control.Adaptor = EmptyItemAdaptor.Create(Element);
|
||||
}
|
||||
else if (Element.ItemTemplate == null)
|
||||
{
|
||||
Control.Adaptor = new ItemDefaultTemplateAdaptor(Element);
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.Adaptor = new ItemTemplateAdaptor(Element);
|
||||
Control.Adaptor.ItemSelected += OnItemSelectedFromUI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnItemSelectedFromUI(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
if (Element is SelectableItemsView selectableItemsView)
|
||||
{
|
||||
selectableItemsView.SelectedItem = e.SelectedItem;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateItemsLayout()
|
||||
{
|
||||
if (Element.ItemsLayout != null)
|
||||
{
|
||||
Control.LayoutManager = Element.ItemsLayout.ToLayoutManager();
|
||||
Control.SnapPointsType = (Element.ItemsLayout as ItemsLayout)?.SnapPointsType ?? SnapPointsType.None;
|
||||
Element.ItemsLayout.PropertyChanged += OnLayoutPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
void OnLayoutPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(ItemsLayout.SnapPointsType))
|
||||
{
|
||||
Control.SnapPointsType = (Element.ItemsLayout as ItemsLayout)?.SnapPointsType ?? SnapPointsType.None;
|
||||
}
|
||||
else if (e.PropertyName == nameof(GridItemsLayout.Span))
|
||||
{
|
||||
((GridLayoutManager)(Control.LayoutManager)).UpdateSpan(((GridItemsLayout)Element.ItemsLayout).Span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ItemsLayoutExtension
|
||||
{
|
||||
public static ICollectionViewLayoutManager ToLayoutManager(this IItemsLayout layout)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case ListItemsLayout listItemsLayout:
|
||||
return new LinearLayoutManager(listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal);
|
||||
case GridItemsLayout gridItemsLayout:
|
||||
return new GridLayoutManager(gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal, gridItemsLayout.Span);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new LinearLayoutManager(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -598,12 +598,12 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
|
||||
static double ComputeAbsoluteX(VisualElement e)
|
||||
{
|
||||
return e.X + ((e.RealParent is VisualElement) && !(e.RealParent is ListView) ? Forms.ConvertToScaledDP(Platform.GetRenderer(e.RealParent).GetNativeContentGeometry().X) : 0.0);
|
||||
return e.X + ((e.RealParent is VisualElement) && !(e.RealParent is ListView || e.RealParent is ItemsView) ? Forms.ConvertToScaledDP(Platform.GetRenderer(e.RealParent).GetNativeContentGeometry().X) : 0.0);
|
||||
}
|
||||
|
||||
static double ComputeAbsoluteY(VisualElement e)
|
||||
{
|
||||
return e.Y + ((e.RealParent is VisualElement) && !(e.RealParent is ListView) ? Forms.ConvertToScaledDP(Platform.GetRenderer(e.RealParent).GetNativeContentGeometry().Y) : 0.0);
|
||||
return e.Y + ((e.RealParent is VisualElement) && !(e.RealParent is ListView || e.RealParent is ItemsView) ? Forms.ConvertToScaledDP(Platform.GetRenderer(e.RealParent).GetNativeContentGeometry().Y) : 0.0);
|
||||
}
|
||||
|
||||
static Point ComputeAbsolutePoint(VisualElement e)
|
||||
|
|
Загрузка…
Ссылка в новой задаче