1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Windows.Input;
21 using System.ComponentModel;
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding;
25 namespace Tizen.NUI.Components
28 /// This class provides a View that can layouting items in list and grid with high performance.
30 [EditorBrowsable(EditorBrowsableState.Never)]
31 public class CollectionView : RecyclerView
34 /// Binding Property of selected item in single selection.
36 [EditorBrowsable(EditorBrowsableState.Never)]
37 public static readonly BindableProperty SelectedItemProperty =
38 BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(CollectionView), null,
39 propertyChanged: (bindable, oldValue, newValue)=>
41 var colView = (CollectionView)bindable;
42 oldValue = colView.selectedItem;
43 colView.selectedItem = newValue;
44 var args = new SelectionChangedEventArgs(oldValue, newValue);
46 foreach (RecyclerViewItem item in colView.ContentContainer.Children.Where((item) => item is RecyclerViewItem))
48 if (item.BindingContext == null) continue;
49 if (item.BindingContext == oldValue) item.IsSelected = false;
50 else if (item.BindingContext == newValue) item.IsSelected = true;
53 SelectionPropertyChanged(colView, args);
55 defaultValueCreator: (bindable)=>
57 var colView = (CollectionView)bindable;
58 return colView.selectedItem;
62 /// Binding Property of selected items list in multiple selection.
64 [EditorBrowsable(EditorBrowsableState.Never)]
65 public static readonly BindableProperty SelectedItemsProperty =
66 BindableProperty.Create(nameof(SelectedItems), typeof(IList<object>), typeof(CollectionView), null,
67 propertyChanged: (bindable, oldValue, newValue)=>
69 var colView = (CollectionView)bindable;
70 var oldSelection = colView.selectedItems ?? selectEmpty;
71 //FIXME : CoerceSelectedItems calls only isCreatedByXaml
72 var newSelection = (SelectionList)CoerceSelectedItems(colView, newValue);
73 colView.selectedItems = newSelection;
74 colView.SelectedItemsPropertyChanged(oldSelection, newSelection);
76 defaultValueCreator: (bindable) =>
78 var colView = (CollectionView)bindable;
79 colView.selectedItems = colView.selectedItems ?? new SelectionList(colView);
80 return colView.selectedItems;
84 /// Binding Property of selected items list in multiple selection.
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 public static readonly BindableProperty SelectionModeProperty =
88 BindableProperty.Create(nameof(SelectionMode), typeof(ItemSelectionMode), typeof(CollectionView), ItemSelectionMode.None,
89 propertyChanged: (bindable, oldValue, newValue)=>
91 var colView = (CollectionView)bindable;
92 oldValue = colView.selectionMode;
93 colView.selectionMode = (ItemSelectionMode)newValue;
94 SelectionModePropertyChanged(colView, oldValue, newValue);
96 defaultValueCreator: (bindable) =>
98 var colView = (CollectionView)bindable;
99 return colView.selectionMode;
103 private static readonly IList<object> selectEmpty = new List<object>(0);
104 private DataTemplate itemTemplate = null;
105 private IEnumerable itemsSource = null;
106 private ItemsLayouter itemsLayouter = null;
107 private DataTemplate groupHeaderTemplate;
108 private DataTemplate groupFooterTemplate;
109 private bool isGrouped;
110 private bool wasRelayouted = false;
111 private bool needInitalizeLayouter = false;
112 private object selectedItem;
113 private SelectionList selectedItems;
114 private bool suppressSelectionChangeNotification;
115 private ItemSelectionMode selectionMode = ItemSelectionMode.None;
116 private RecyclerViewItem header;
117 private RecyclerViewItem footer;
118 private View focusedView;
119 private int prevFocusedDataIndex = 0;
120 private List<RecyclerViewItem> recycleGroupHeaderCache { get; } = new List<RecyclerViewItem>();
121 private List<RecyclerViewItem> recycleGroupFooterCache { get; } = new List<RecyclerViewItem>();
124 /// Base constructor.
126 [EditorBrowsable(EditorBrowsableState.Never)]
127 public CollectionView() : base()
130 SetKeyboardNavigationSupport(true);
134 /// Base constructor with ItemsSource
136 /// <param name="itemsSource">item's data source</param>
137 [EditorBrowsable(EditorBrowsableState.Never)]
138 public CollectionView(IEnumerable itemsSource) : this()
140 ItemsSource = itemsSource;
144 /// Base constructor with ItemsSource, ItemsLayouter and ItemTemplate
146 /// <param name="itemsSource">item's data source</param>
147 /// <param name="layouter">item's layout manager</param>
148 /// <param name="template">item's view template with data bindings</param>
149 [EditorBrowsable(EditorBrowsableState.Never)]
150 public CollectionView(IEnumerable itemsSource, ItemsLayouter layouter, DataTemplate template) : this()
152 ItemsSource = itemsSource;
153 ItemTemplate = template;
154 ItemsLayouter = layouter;
158 /// Event of Selection changed.
159 /// old selection list and new selection will be provided.
161 [EditorBrowsable(EditorBrowsableState.Never)]
162 public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
165 /// Align item in the viewport when ScrollTo() calls.
167 [EditorBrowsable(EditorBrowsableState.Never)]
168 public enum ItemScrollTo
171 /// Scroll to show item in nearest viewport on scroll direction.
172 /// item is above the scroll viewport, item will be came into front,
173 /// item is under the scroll viewport, item will be came into end,
174 /// item is in the scroll viewport, no scroll.
178 /// Scroll to show item in start of the viewport.
182 /// Scroll to show item in center of the viewport.
186 /// Scroll to show item in end of the viewport.
192 /// Item's source data.
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public override IEnumerable ItemsSource
207 if (InternalItemSource != null) InternalItemSource.Dispose();
211 if (InternalItemSource != null) InternalItemSource.Dispose();
212 InternalItemSource = ItemsSourceFactory.Create(this);
214 if (itemsLayouter == null) return;
216 needInitalizeLayouter = true;
222 /// DataTemplate for items.
224 [EditorBrowsable(EditorBrowsableState.Never)]
225 public override DataTemplate ItemTemplate
233 itemTemplate = value;
240 needInitalizeLayouter = true;
248 [EditorBrowsable(EditorBrowsableState.Never)]
249 public virtual ItemsLayouter ItemsLayouter
253 return itemsLayouter;
257 itemsLayouter = value;
258 base.InternalItemsLayouter = ItemsLayouter;
261 needInitalizeLayouter = false;
265 needInitalizeLayouter = true;
271 /// Scrolling direction to display items layout.
273 [EditorBrowsable(EditorBrowsableState.Never)]
274 public new Direction ScrollingDirection
278 return base.ScrollingDirection;
282 base.ScrollingDirection = value;
284 if (ScrollingDirection == Direction.Horizontal)
286 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
290 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
296 /// Selected item in single selection.
298 [EditorBrowsable(EditorBrowsableState.Never)]
299 public object SelectedItem
301 get => GetValue(SelectedItemProperty);
302 set => SetValue(SelectedItemProperty, value);
306 /// Selected items list in multiple selection.
308 [EditorBrowsable(EditorBrowsableState.Never)]
309 public IList<object> SelectedItems
311 get => (IList<object>)GetValue(SelectedItemsProperty);
312 // set => SetValue(SelectedItemsProperty, new SelectionList(this, value));
316 /// Selection mode to handle items selection. See ItemSelectionMode for details.
318 [EditorBrowsable(EditorBrowsableState.Never)]
319 public ItemSelectionMode SelectionMode
321 get => (ItemSelectionMode)GetValue(SelectionModeProperty);
322 set => SetValue(SelectionModeProperty, value);
326 /// Command of selection changed.
328 [EditorBrowsable(EditorBrowsableState.Never)]
329 public ICommand SelectionChangedCommand { set; get; }
332 /// Command parameter of selection changed.
334 [EditorBrowsable(EditorBrowsableState.Never)]
335 public object SelectionChangedCommandParameter { set; get; }
338 /// Size strategy of measuring scroll content. see details in ItemSizingStrategy.
340 [EditorBrowsable(EditorBrowsableState.Never)]
341 public ItemSizingStrategy SizingStrategy { get; set; }
344 /// Header item which placed in top-most position.
345 /// note : internal index and count will be increased.
347 [EditorBrowsable(EditorBrowsableState.Never)]
348 public RecyclerViewItem Header
355 //ContentContainer.Remove(header);
356 Utility.Dispose(header);
361 value.ParentItemsView = this;
362 value.IsHeader = true;
363 ContentContainer.Add(value);
366 needInitalizeLayouter = true;
372 /// Footer item which placed in bottom-most position.
373 /// note : internal count will be increased.
375 [EditorBrowsable(EditorBrowsableState.Never)]
376 public RecyclerViewItem Footer
383 //ContentContainer.Remove(footer);
384 Utility.Dispose(footer);
388 value.Index = InternalItemSource?.Count ?? 0;
389 value.ParentItemsView = this;
390 value.IsFooter = true;
391 ContentContainer.Add(value);
394 needInitalizeLayouter = true;
400 /// Boolean flag of group feature existence.
402 [EditorBrowsable(EditorBrowsableState.Never)]
403 public bool IsGrouped
409 needInitalizeLayouter = true;
410 //Need to re-intialize Internal Item Source.
411 if (InternalItemSource != null)
413 InternalItemSource.Dispose();
414 InternalItemSource = null;
416 if (ItemsSource != null)
417 InternalItemSource = ItemsSourceFactory.Create(this);
423 /// DataTemplate of group header. Group feature is not supported yet.
425 [EditorBrowsable(EditorBrowsableState.Never)]
426 public DataTemplate GroupHeaderTemplate
430 return groupHeaderTemplate;
434 groupHeaderTemplate = value;
435 needInitalizeLayouter = true;
441 /// DataTemplate of group footer. Group feature is not supported yet.
443 [EditorBrowsable(EditorBrowsableState.Never)]
444 public DataTemplate GroupFooterTemplate
448 return groupFooterTemplate;
452 groupFooterTemplate = value;
453 needInitalizeLayouter = true;
460 /// Internal encapsulated items data source.
462 internal new IGroupableItemSource InternalItemSource
466 return (base.InternalItemSource as IGroupableItemSource);
470 base.InternalItemSource = value;
474 [EditorBrowsable(EditorBrowsableState.Never)]
475 public override void OnRelayout(Vector2 size, RelayoutContainer container)
477 base.OnRelayout(size, container);
479 wasRelayouted = true;
480 if (needInitalizeLayouter) Init();
484 [EditorBrowsable(EditorBrowsableState.Never)]
485 public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
487 View nextFocusedView = null;
489 if (focusedView == null)
491 // If focusedView is null, find child which has previous data index
492 if (ContentContainer.Children.Count > 0 && InternalItemSource.Count > 0)
494 for (int i = 0; i < ContentContainer.Children.Count; i++)
496 RecyclerViewItem item = Children[i] as RecyclerViewItem;
497 if (item?.Index == prevFocusedDataIndex)
499 nextFocusedView = item;
507 // If this is not first focus, request next focus to Layouter
508 nextFocusedView = ItemsLayouter.RequestNextFocusableView(currentFocusedView, direction, loopEnabled);
511 if (nextFocusedView != null)
513 // Check next focused view is inside of visible area.
514 // If it is not, move scroll position to make it visible.
515 Position scrollPosition = ContentContainer.CurrentPosition;
516 float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
518 float left = nextFocusedView.Position.X;
519 float right = nextFocusedView.Position.X + nextFocusedView.Size.Width;
520 float top = nextFocusedView.Position.Y;
521 float bottom = nextFocusedView.Position.Y + nextFocusedView.Size.Height;
523 float visibleRectangleLeft = -scrollPosition.X;
524 float visibleRectangleRight = -scrollPosition.X + Size.Width;
525 float visibleRectangleTop = -scrollPosition.Y;
526 float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
528 if (ScrollingDirection == Direction.Horizontal)
530 if ((direction == View.FocusDirection.Left || direction == View.FocusDirection.Up) && left < visibleRectangleLeft)
532 targetPosition = left;
534 else if ((direction == View.FocusDirection.Right || direction == View.FocusDirection.Down) && right > visibleRectangleRight)
536 targetPosition = right - Size.Width;
541 if ((direction == View.FocusDirection.Up || direction == View.FocusDirection.Left) && top < visibleRectangleTop)
543 targetPosition = top;
545 else if ((direction == View.FocusDirection.Down || direction == View.FocusDirection.Right) && bottom > visibleRectangleBottom)
547 targetPosition = bottom - Size.Height;
551 focusedView = nextFocusedView;
552 prevFocusedDataIndex = (nextFocusedView as RecyclerViewItem)?.Index ?? -1;
554 ScrollTo(targetPosition, true);
558 // If nextView is null, it means that we should move focus to outside of Control.
559 // Return FocusableView depending on direction.
562 case View.FocusDirection.Left:
564 nextFocusedView = LeftFocusableView;
567 case View.FocusDirection.Right:
569 nextFocusedView = RightFocusableView;
572 case View.FocusDirection.Up:
574 nextFocusedView = UpFocusableView;
577 case View.FocusDirection.Down:
579 nextFocusedView = DownFocusableView;
584 if(nextFocusedView != null)
590 //If FocusableView doesn't exist, not move focus.
591 nextFocusedView = focusedView;
595 return nextFocusedView;
599 /// Update selected items list in multiple selection.
601 /// <param name="newSelection">updated selection list by user</param>
602 [EditorBrowsable(EditorBrowsableState.Never)]
603 public void UpdateSelectedItems(IList<object> newSelection)
605 var oldSelection = new List<object>(SelectedItems);
607 suppressSelectionChangeNotification = true;
609 SelectedItems.Clear();
611 if (newSelection?.Count > 0)
613 for (int n = 0; n < newSelection.Count; n++)
615 SelectedItems.Add(newSelection[n]);
619 suppressSelectionChangeNotification = false;
621 SelectedItemsPropertyChanged(oldSelection, newSelection);
625 /// Scroll to specific position with or without animation.
627 /// <param name="position">Destination.</param>
628 /// <param name="animate">Scroll with or without animation</param>
629 [EditorBrowsable(EditorBrowsableState.Never)]
630 public new void ScrollTo(float position, bool animate) => base.ScrollTo(position, animate);
633 /// Scroll to specific item's aligned position with or without animation.
635 /// <param name="item">Target item of dataset.</param>
636 /// <param name="animate">Boolean flag of animation.</param>
637 /// <param name="align">Align state of item. see details in ItemScrollTo.</param>
638 [EditorBrowsable(EditorBrowsableState.Never)]
639 public virtual void ScrollTo(object item, bool animate = false, ItemScrollTo align = ItemScrollTo.Nearest)
641 if (item == null) throw new ArgumentNullException(nameof(item));
642 if (ItemsLayouter == null) throw new Exception("Item Layouter must exist.");
644 if (InternalItemSource.GetPosition(item) == -1)
646 throw new Exception("ScrollTo parameter item is not a member of ItemsSource");
649 float scrollPos, curPos, curSize, curItemSize;
650 (float x, float y) = ItemsLayouter.GetItemPosition(item);
651 (float width, float height) = ItemsLayouter.GetItemSize(item);
652 if (ScrollingDirection == Direction.Horizontal)
655 curPos = ScrollPosition.X;
656 curSize = Size.Width;
662 curPos = ScrollPosition.Y;
663 curSize = Size.Height;
664 curItemSize = height;
667 //Console.WriteLine("[NUI] ScrollTo [{0}:{1}], curPos{2}, itemPos{3}, curSize{4}, itemSize{5}", InternalItemSource.GetPosition(item), align, curPos, scrollPos, curSize, curItemSize);
670 case ItemScrollTo.Start:
673 case ItemScrollTo.Center:
674 scrollPos = scrollPos - (curSize / 2) + (curItemSize / 2);
676 case ItemScrollTo.End:
677 scrollPos = scrollPos - curSize + curItemSize;
679 case ItemScrollTo.Nearest:
680 if (scrollPos < curPos - curItemSize)
682 // item is placed before the current screen. scrollTo.Top
684 else if (scrollPos >= curPos + curSize + curItemSize)
686 // item is placed after the current screen. scrollTo.End
687 scrollPos = scrollPos - curSize + curItemSize;
691 // item is in the scroller. ScrollTo() is ignored.
697 //Console.WriteLine("[NUI] ScrollTo [{0}]-------------------", scrollPos);
698 base.ScrollTo(scrollPos, animate);
701 // Realize and Decorate the item.
702 internal override RecyclerViewItem RealizeItem(int index)
704 if (index == 0 && Header != null)
710 if (index == InternalItemSource.Count - 1 && Footer != null)
718 var context = InternalItemSource.GetItem(index);
719 if (InternalItemSource.IsGroupHeader(index))
721 DataTemplate templ = (groupHeaderTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupHeaderTemplate;
723 RecyclerViewItem groupHeader = PopRecycleGroupCache(templ, true);
724 if (groupHeader == null)
726 groupHeader = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupHeaderTemplate, context, this);
728 groupHeader.ParentItemsView = this;
729 groupHeader.Template = templ;
730 groupHeader.isGroupHeader = true;
731 groupHeader.isGroupFooter = false;
732 ContentContainer.Add(groupHeader);
734 groupHeader.Index = index;
735 groupHeader.ParentGroup = context;
736 groupHeader.BindingContext = context;
740 else if (InternalItemSource.IsGroupFooter(index))
742 DataTemplate templ = (groupFooterTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupFooterTemplate;
744 RecyclerViewItem groupFooter = PopRecycleGroupCache(templ, false);
745 if (groupFooter == null)
747 groupFooter = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupFooterTemplate, context, this);
749 groupFooter.ParentItemsView = this;
750 groupFooter.Template = templ;
751 groupFooter.isGroupHeader = false;
752 groupFooter.isGroupFooter = true;
753 ContentContainer.Add(groupFooter);
755 groupFooter.Index = index;
756 groupFooter.ParentGroup = context;
757 groupFooter.BindingContext = context;
764 RecyclerViewItem item = base.RealizeItem(index);
765 if (isGrouped) item.ParentGroup = InternalItemSource.GetGroupParent(index);
767 switch (SelectionMode)
769 case ItemSelectionMode.SingleSelection:
770 if (item.BindingContext == SelectedItem) item.IsSelected = true;
773 case ItemSelectionMode.MultipleSelections:
774 if (SelectedItems?.Contains(item.BindingContext) ?? false) item.IsSelected = true;
776 case ItemSelectionMode.None:
777 item.IsSelectable = false;
784 // Unrealize and caching the item.
785 internal override void UnrealizeItem(RecyclerViewItem item, bool recycle = true)
797 if (item.isGroupHeader || item.isGroupFooter)
799 if (!recycle || !PushRecycleGroupCache(item))
800 Utility.Dispose(item);
804 item.IsSelected = false;
805 base.UnrealizeItem(item, recycle);
808 internal void SelectedItemsPropertyChanged(IList<object> oldSelection, IList<object> newSelection)
810 if (suppressSelectionChangeNotification)
815 foreach (RecyclerViewItem item in ContentContainer.Children.Where((item) => item is RecyclerViewItem))
817 if (item.BindingContext == null) continue;
818 if (newSelection.Contains(item.BindingContext))
820 if (!item.IsSelected) item.IsSelected = true;
824 if (item.IsSelected) item.IsSelected = false;
827 SelectionPropertyChanged(this, new SelectionChangedEventArgs(oldSelection, newSelection));
829 OnPropertyChanged(SelectedItemsProperty.PropertyName);
833 /// Internal selection callback.
835 [EditorBrowsable(EditorBrowsableState.Never)]
836 protected virtual void OnSelectionChanged(SelectionChangedEventArgs args)
842 /// Adjust scrolling position by own scrolling rules.
843 /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
845 /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
846 /// <returns>Adjusted scroll destination</returns>
847 [EditorBrowsable(EditorBrowsableState.Never)]
848 protected override float AdjustTargetPositionOfScrollAnimation(float position)
850 // Destination is depending on implementation of layout manager.
851 // Get destination from layout manager.
852 return ItemsLayouter.CalculateCandidateScrollPosition(position);
856 /// OnScroll event callback.
858 /// <param name="source">Scroll source object</param>
859 /// <param name="args">Scroll event argument</param>
860 [EditorBrowsable(EditorBrowsableState.Never)]
861 protected override void OnScrolling(object source, ScrollEventArgs args)
863 if (disposed) return;
865 if (needInitalizeLayouter)
867 ItemsLayouter.Initialize(this);
868 needInitalizeLayouter = false;
870 base.OnScrolling(source, args);
874 /// Dispose ItemsView and all children on it.
876 /// <param name="type">Dispose type.</param>
877 protected override void Dispose(DisposeTypes type)
884 if (type == DisposeTypes.Explicit)
887 if (InternalItemSource != null)
889 InternalItemSource.Dispose();
890 InternalItemSource = null;
894 Utility.Dispose(Header);
899 Utility.Dispose(Footer);
902 groupHeaderTemplate = null;
903 groupFooterTemplate = null;
910 private static void SelectionPropertyChanged(CollectionView colView, SelectionChangedEventArgs args)
912 var command = colView.SelectionChangedCommand;
916 var commandParameter = colView.SelectionChangedCommandParameter;
918 if (command.CanExecute(commandParameter))
920 command.Execute(commandParameter);
923 colView.SelectionChanged?.Invoke(colView, args);
924 colView.OnSelectionChanged(args);
927 private static object CoerceSelectedItems(BindableObject bindable, object value)
931 return new SelectionList((CollectionView)bindable);
934 if (value is SelectionList)
939 return new SelectionList((CollectionView)bindable, value as IList<object>);
942 private static void SelectionModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
944 var colView = (CollectionView)bindable;
946 var oldMode = (ItemSelectionMode)oldValue;
947 var newMode = (ItemSelectionMode)newValue;
949 IList<object> previousSelection = new List<object>();
950 IList<object> newSelection = new List<object>();
954 case ItemSelectionMode.None:
956 case ItemSelectionMode.SingleSelection:
957 if (colView.SelectedItem != null)
959 previousSelection.Add(colView.SelectedItem);
962 case ItemSelectionMode.MultipleSelections:
963 previousSelection = colView.SelectedItems;
969 case ItemSelectionMode.None:
971 case ItemSelectionMode.SingleSelection:
972 if (colView.SelectedItem != null)
974 newSelection.Add(colView.SelectedItem);
977 case ItemSelectionMode.MultipleSelections:
978 newSelection = colView.SelectedItems;
982 if (previousSelection.Count == newSelection.Count)
984 if (previousSelection.Count == 0 || (previousSelection[0] == newSelection[0]))
986 // Both selections are empty or have the same single item; no reason to signal a change
991 var args = new SelectionChangedEventArgs(previousSelection, newSelection);
992 SelectionPropertyChanged(colView, args);
997 if (ItemsSource == null) return;
998 if (ItemsLayouter == null) return;
999 if (ItemTemplate == null) return;
1001 if (disposed) return;
1002 if (needInitalizeLayouter)
1004 if (InternalItemSource == null) return;
1006 InternalItemSource.HasHeader = (header != null);
1007 InternalItemSource.HasFooter = (footer != null);
1010 if (!wasRelayouted) return;
1012 if (needInitalizeLayouter)
1014 ItemsLayouter.Initialize(this);
1015 needInitalizeLayouter = false;
1017 ItemsLayouter.RequestLayout(0.0f, true);
1019 if (ScrollingDirection == Direction.Horizontal)
1021 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
1025 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
1029 private bool PushRecycleGroupCache(RecyclerViewItem item)
1031 if (item == null) throw new ArgumentNullException(nameof(item));
1032 if (RecycleCache.Count >= 20) return false;
1033 if (item.Template == null) return false;
1034 if (item.isGroupHeader)
1036 recycleGroupHeaderCache.Add(item);
1038 else if (item.isGroupFooter)
1040 recycleGroupFooterCache.Add(item);
1048 private RecyclerViewItem PopRecycleGroupCache(DataTemplate Template, bool isHeader)
1050 RecyclerViewItem viewItem = null;
1052 var Cache = (isHeader ? recycleGroupHeaderCache : recycleGroupFooterCache);
1053 for (int i = 0; i < Cache.Count; i++)
1055 viewItem = Cache[i];
1056 if (Template == viewItem.Template) break;
1059 if (viewItem != null)
1061 Cache.Remove(viewItem);