diff --git a/AiForms.CollectionView.sln b/AiForms.CollectionView.sln index fc570f5..08a0197 100644 --- a/AiForms.CollectionView.sln +++ b/AiForms.CollectionView.sln @@ -11,6 +11,7 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|Any CPU = Release|Any CPU Debug|Any CPU = Debug|Any CPU + Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6E8E8AAC-E482-4E9C-9759-27B03E4DC309}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -25,5 +26,7 @@ Global {56F75955-E569-43D2-B6C7-5C2BB4E72FD3}.Release|Any CPU.Build.0 = Release|Any CPU {56F75955-E569-43D2-B6C7-5C2BB4E72FD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56F75955-E569-43D2-B6C7-5C2BB4E72FD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56F75955-E569-43D2-B6C7-5C2BB4E72FD3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {56F75955-E569-43D2-B6C7-5C2BB4E72FD3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/CollectionView.Droid/CollectionView.Droid.csproj b/CollectionView.Droid/CollectionView.Droid.csproj index 0b5a0c5..d24092c 100644 --- a/CollectionView.Droid/CollectionView.Droid.csproj +++ b/CollectionView.Droid/CollectionView.Droid.csproj @@ -142,7 +142,7 @@ - {6E8E8AAC-E482-4E9C-9759-27B03E4DC309} + {D5C93D88-A5D6-4843-935B-CC72F073B54E} CollectionView diff --git a/CollectionView.Droid/CollectionViewAdapter.cs b/CollectionView.Droid/CollectionViewAdapter.cs index 8dd830e..baede21 100644 --- a/CollectionView.Droid/CollectionViewAdapter.cs +++ b/CollectionView.Droid/CollectionViewAdapter.cs @@ -399,8 +399,10 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi var group = 0; var templatedItems = TemplatedItemsView.TemplatedItems; if (_collectionView.IsGroupingEnabled) + { group = templatedItems.GetGroupIndexFromGlobal(realPosition, out row); - + if (group < 0) return; + } var templatedList = templatedItems.GetGroup(group); if (_collectionView.IsGroupingEnabled) @@ -423,6 +425,8 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi return; } + if (cell == null) return; + AView view = GetCell(cell, container, _recyclerView, _context, _collectionView); diff --git a/CollectionView.Droid/GridCollectionViewRenderer.cs b/CollectionView.Droid/GridCollectionViewRenderer.cs index b016a69..30b5cd4 100644 --- a/CollectionView.Droid/GridCollectionViewRenderer.cs +++ b/CollectionView.Droid/GridCollectionViewRenderer.cs @@ -19,6 +19,10 @@ public class GridCollectionViewRenderer : CollectionViewRenderer, SwipeRefreshLa public int RowSpacing { get; set; } public int ColumnSpacing { get; set; } + int _bothSidesMargin; + int _firstSpacing; + int _lastSpacing; + SwipeRefreshLayout _refresh; GridLayoutManager _gridLayoutManager => LayoutManager as GridLayoutManager; CollectionViewSpanSizeLookup _spanSizeLookup; @@ -43,6 +47,7 @@ protected override void Dispose(bool disposing) if (disposing) { + RecyclerView?.StopScroll(); RecyclerView?.SetAdapter(null); RecyclerView?.RemoveItemDecoration(_itemDecoration); _gridLayoutManager?.SetSpanSizeLookup(null); @@ -126,7 +131,10 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE e.PropertyName == GridCollectionView.ColumnHeightProperty.PropertyName || e.PropertyName == GridCollectionView.ColumnWidthProperty.PropertyName || e.PropertyName == GridCollectionView.SpacingTypeProperty.PropertyName || - e.PropertyName == GridCollectionView.AdditionalHeightProperty.PropertyName) + e.PropertyName == GridCollectionView.AdditionalHeightProperty.PropertyName || + e.PropertyName == CollectionView.GroupFirstSpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupLastSpacingProperty.PropertyName || + e.PropertyName == GridCollectionView.BothSidesMarginProperty.PropertyName) { UpdateGridType(); RefreshAll(); @@ -250,6 +258,9 @@ protected virtual void UpdateGridType(int containerWidth = 0) RecyclerView.SetPadding(0, 0, 0, 0); RowSpacing = (int)Context.ToPixels(_gridCollectionView.RowSpacing); + _firstSpacing = (int)Context.ToPixels(_gridCollectionView.GroupFirstSpacing); + _lastSpacing = (int)Context.ToPixels(_gridCollectionView.GroupLastSpacing); + int spanCount = 0; if (_gridCollectionView.GridType == GridType.UniformGrid) { @@ -265,6 +276,7 @@ protected virtual void UpdateGridType(int containerWidth = 0) spanCount = _gridCollectionView.LandscapeColumns; break; } + _bothSidesMargin = (int)Context.ToPixels(_gridCollectionView.BothSidesMargin); ColumnSpacing = (int)(Context.ToPixels(_gridCollectionView.ColumnSpacing)); CellHeight = GetUniformItemHeight(containerWidth, spanCount); System.Diagnostics.Debug.WriteLine($"Decided RowHeight {CellHeight}"); @@ -295,7 +307,8 @@ protected virtual double CalcurateColumnHeight(double itemWidth) protected virtual int GetUniformItemHeight(int containerWidth, int columns) { - float actualWidth = containerWidth - (float)ColumnSpacing * (float)(columns - 1.0f); + RecyclerView.SetPadding(_bothSidesMargin, 0, _bothSidesMargin, 0); + float actualWidth = containerWidth - _bothSidesMargin * 2f - (float)ColumnSpacing * (float)(columns - 1.0f); var itemWidth = (float)(actualWidth / (float)columns); _gridCollectionView.SetValue(GridCollectionView.ComputedWidthProperty, Context.FromPixels(itemWidth)); return (int)CalcurateColumnHeight(itemWidth); @@ -413,13 +426,14 @@ public override void GetItemOffsets(Android.Graphics.Rect outRect, Android.Views var param = view.LayoutParameters as GridLayoutManager.LayoutParams; var spanIndex = param.SpanIndex; var spanSize = param.SpanSize; + var position = parent.GetChildAdapterPosition(view); if (spanSize == _spanCount) { var headparams = view.LayoutParameters as ViewGroup.MarginLayoutParams; var margin = 0; - if (_gridCollectionView.GridType == GridType.AutoSpacingGrid && - _gridCollectionView.SpacingType == SpacingType.Center) + if (_gridCollectionView.GridType == GridType.AutoSpacingGrid && _gridCollectionView.SpacingType == SpacingType.Center || + _gridCollectionView.GridType == GridType.UniformGrid && _gridCollectionView.BothSidesMargin > 0) { margin = _parentRenderer.RecyclerView.PaddingLeft * -1; headparams.SetMargins(margin, headparams.TopMargin, margin, headparams.BottomMargin); @@ -430,6 +444,11 @@ public override void GetItemOffsets(Android.Graphics.Rect outRect, Android.Views headparams.SetMargins(margin, headparams.TopMargin, margin, headparams.BottomMargin); view.LayoutParameters = headparams; } + + outRect.Bottom = _parentRenderer._firstSpacing; + if(position != 0) { + outRect.Top = _parentRenderer._lastSpacing; + } return; } @@ -447,9 +466,13 @@ public override void GetItemOffsets(Android.Graphics.Rect outRect, Android.Views { outRect.Left = spanIndex * _parentRenderer.ColumnSpacing / _spanCount; // column * ((1f / spanCount) * spacing) outRect.Right = _parentRenderer.ColumnSpacing - (spanIndex + 1) * _parentRenderer.ColumnSpacing / _spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) + } + + if(position < _spanCount || _parentRenderer.Adapter.FirstSectionItems.Contains(position - spanIndex)) { + return; } - outRect.Bottom = _parentRenderer.RowSpacing; + outRect.Top = _parentRenderer.RowSpacing; } } } diff --git a/CollectionView.Droid/HCollectionViewAdapter.cs b/CollectionView.Droid/HCollectionViewAdapter.cs index 0d91fad..f6fcfec 100644 --- a/CollectionView.Droid/HCollectionViewAdapter.cs +++ b/CollectionView.Droid/HCollectionViewAdapter.cs @@ -54,6 +54,10 @@ protected override void OnCollectionChanged(object sender, NotifyCollectionChang public override int GetRealPosition(int position) { + if (_listCount == 0) + { + return position; + } return _hCollectionView.IsInfinite ? position % _listCount : position; } diff --git a/CollectionView.Droid/HCollectionViewRenderer.cs b/CollectionView.Droid/HCollectionViewRenderer.cs index 78dd72d..8bf48e6 100644 --- a/CollectionView.Droid/HCollectionViewRenderer.cs +++ b/CollectionView.Droid/HCollectionViewRenderer.cs @@ -18,6 +18,8 @@ public class HCollectionViewRenderer : CollectionViewRenderer, ICollectionViewRe HCollectionViewAdapter _hAdapter => Adapter as HCollectionViewAdapter; int _spacing; bool _disposed; + int _firstSpacing; + int _lastSpacing; public HCollectionViewRenderer(Context context) : base(context) { @@ -31,6 +33,7 @@ protected override void Dispose(bool disposing) } if (disposing) { + RecyclerView?.StopScroll(); RecyclerView?.SetAdapter(null); RecyclerView?.RemoveItemDecoration(_itemDecoration); @@ -89,7 +92,9 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE UpdateGroupHeaderWidth(); RefreshAll(); } - else if (e.PropertyName == HCollectionView.SpacingProperty.PropertyName) + else if (e.PropertyName == HCollectionView.SpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupFirstSpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupLastSpacingProperty.PropertyName) { UpdateSpacing(); RefreshAll(); @@ -164,6 +169,8 @@ protected virtual void UpdateCellSize() protected virtual void UpdateSpacing() { _spacing = (int)Context.ToPixels(_hCollectionView.Spacing); + _firstSpacing = (int)Context.ToPixels(_hCollectionView.GroupFirstSpacing); + _lastSpacing = (int)Context.ToPixels(_hCollectionView.GroupLastSpacing); } protected virtual void UpdateGroupHeaderWidth() @@ -202,7 +209,19 @@ public override void GetItemOffsets(Rect outRect, Android.Views.View view, Recyc { realPosition = _renderer.Adapter.GetRealPosition(position); } - if (position == 0 || holder.IsHeader || _renderer.Adapter.FirstSectionItems.Contains(realPosition)) + + if(holder.IsHeader) + { + outRect.Right = _renderer._firstSpacing; + if (position != 0) + { + outRect.Left = _renderer._lastSpacing; + } + + return; + } + + if (position == 0 || _renderer.Adapter.FirstSectionItems.Contains(realPosition)) { return; } diff --git a/CollectionView.iOS/CollectionView.iOS.csproj b/CollectionView.iOS/CollectionView.iOS.csproj index e972443..7c26e5b 100644 --- a/CollectionView.iOS/CollectionView.iOS.csproj +++ b/CollectionView.iOS/CollectionView.iOS.csproj @@ -9,7 +9,6 @@ AiForms.Renderers.iOS CollectionView.iOS Resources - 1.1.2 true @@ -79,10 +78,17 @@ + + + + {D5C93D88-A5D6-4843-935B-CC72F073B54E} + CollectionView + + \ No newline at end of file diff --git a/CollectionView.iOS/CollectionViewRenderer.cs b/CollectionView.iOS/CollectionViewRenderer.cs index bae2a17..9d6ab95 100644 --- a/CollectionView.iOS/CollectionViewRenderer.cs +++ b/CollectionView.iOS/CollectionViewRenderer.cs @@ -247,10 +247,10 @@ protected virtual void UpdateItems(NotifyCollectionChangedEventArgs e, int secti // HACK: When an item is added for the first time, UICollectionView is sometimes crashed for some reason. // So, in that case, ReloadData is called. - if (!Control.IndexPathsForVisibleItems.Any()) - { - groupReset = true; - } + //if (!Control.IndexPathsForVisibleItems.Any()) + //{ + // groupReset = true; + //} // We can't do this check on grouped lists because the index doesn't match the number of rows in a section. // Likewise, we can't do this check on lists using RecycleElement because the number of rows in a section will remain constant because they are reused. diff --git a/CollectionView.iOS/CollectionViewSource.cs b/CollectionView.iOS/CollectionViewSource.cs index c5206d6..3eabfa4 100644 --- a/CollectionView.iOS/CollectionViewSource.cs +++ b/CollectionView.iOS/CollectionViewSource.cs @@ -59,6 +59,11 @@ protected override void Dispose(bool disposing) public override nint NumberOfSections(UICollectionView collectionView) { + if (TemplatedItemsView.TemplatedItems.Count == 0) + { + return 0; + } + if (CollectionView.IsGroupingEnabled) { return TemplatedItemsView.TemplatedItems.Count; @@ -168,6 +173,7 @@ public override UICollectionReusableView GetViewForSupplementaryElement(UICollec Performance.Start(out string reference); + var cachingStrategy = CollectionView.CachingStrategy; if (cachingStrategy == ListViewCachingStrategy.RetainElement) { @@ -175,8 +181,6 @@ public override UICollectionReusableView GetViewForSupplementaryElement(UICollec } else if ((cachingStrategy & ListViewCachingStrategy.RecycleElement) != 0) { - var id = TemplateIdForPath(realIndexPath); - // Here is used the argument indexPath as it is because header cell will be got not to displayed when IsInfinite. nativeCell = collectionView.DequeueReusableSupplementaryView( UICollectionElementKindSection.Header, @@ -278,6 +282,17 @@ protected virtual ContentCellContainer GetNativeHeaderCell(NSIndexPath indexPath var nativeCell = renderer.GetCell(cell, reusableCell, _uiCollectionView) as ContentCellContainer; + var cellWithContent = nativeCell; + + // Sometimes iOS for returns a dequeued cell whose Layer is hidden. + // This prevents it from showing up, so lets turn it back on! + if (cellWithContent.Layer.Hidden) + cellWithContent.Layer.Hidden = false; + + // Because the layer was hidden we need to layout the cell by hand + if (cellWithContent != null) + cellWithContent.LayoutSubviews(); + return nativeCell; } diff --git a/CollectionView.iOS/GridCollectionViewRenderer.cs b/CollectionView.iOS/GridCollectionViewRenderer.cs index 663522f..0d6a457 100644 --- a/CollectionView.iOS/GridCollectionViewRenderer.cs +++ b/CollectionView.iOS/GridCollectionViewRenderer.cs @@ -8,6 +8,9 @@ using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; using RectangleF = CoreGraphics.CGRect; +using Foundation; +using System.Collections.Generic; +using CoreFoundation; [assembly: ExportRenderer(typeof(GridCollectionView), typeof(GridCollectionViewRenderer))] namespace AiForms.Renderers.iOS @@ -22,20 +25,20 @@ public class GridCollectionViewRenderer : CollectionViewRenderer bool _disposed; GridCollectionView _gridCollectionView => (GridCollectionView)Element; GridCollectionViewSource _gridSource => DataSource as GridCollectionViewSource; + float _firstSpacing => _gridCollectionView.IsGroupingEnabled ? (float)_gridCollectionView.GroupFirstSpacing : 0; + float _lastSpacing => _gridCollectionView.IsGroupingEnabled ? (float)_gridCollectionView.GroupLastSpacing : 0; bool _isRatioHeight => _gridCollectionView.ColumnHeight <= 5.0; protected override void OnElementChanged(ElementChangedEventArgs e) { if (e.NewElement != null) { - ViewLayout = new UICollectionViewFlowLayout(); + ViewLayout = new GridViewLayout(); ViewLayout.ScrollDirection = UICollectionViewScrollDirection.Vertical; ViewLayout.SectionInset = new UIEdgeInsets(0, 0, 0, 0); ViewLayout.MinimumLineSpacing = 0.0f; ViewLayout.MinimumInteritemSpacing = 0.0f; ViewLayout.EstimatedItemSize = UICollectionViewFlowLayout.AutomaticSize; - ViewLayout.SectionHeadersPinToVisibleBounds = true; // fix header cell - _refreshControl = new UIRefreshControl(); _refreshControl.ValueChanged += RefreshControl_ValueChanged; @@ -59,6 +62,7 @@ protected override void OnElementChanged(ElementChangedEventArgs DataSource = new GridCollectionViewSource(e.NewElement, _collectionView); _collectionView.Source = DataSource; + UpdateIsSticky(); UpdateRowSpacing(); UpdatePullToRefreshEnabled(); UpdatePullToRefreshColor(); @@ -129,10 +133,13 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE e.PropertyName == GridCollectionView.ColumnSpacingProperty.PropertyName || e.PropertyName == GridCollectionView.ColumnHeightProperty.PropertyName || e.PropertyName == GridCollectionView.SpacingTypeProperty.PropertyName || - e.PropertyName == GridCollectionView.AdditionalHeightProperty.PropertyName) + e.PropertyName == GridCollectionView.AdditionalHeightProperty.PropertyName || + e.PropertyName == CollectionView.GroupFirstSpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupLastSpacingProperty.PropertyName || + e.PropertyName == GridCollectionView.BothSidesMarginProperty.PropertyName) { UpdateGridType(); - ViewLayout.InvalidateLayout(); + InvalidateLayout(); } else if (e.PropertyName == GridCollectionView.RowSpacingProperty.PropertyName) { @@ -144,7 +151,7 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE if (_gridCollectionView.GridType != GridType.UniformGrid) { UpdateGridType(); - ViewLayout.InvalidateLayout(); + InvalidateLayout(); } } else if (e.PropertyName == ListView.IsPullToRefreshEnabledProperty.PropertyName) @@ -159,6 +166,11 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE { UpdateIsRefreshing(); } + else if (e.PropertyName == GridCollectionView.IsGroupHeaderStickyProperty.PropertyName) + { + UpdateIsSticky(); + InvalidateLayout(); + } } protected override UICollectionViewScrollPosition GetScrollPosition(ScrollToPosition position) @@ -241,9 +253,15 @@ protected virtual void UpdateGroupHeaderHeight() } } + protected virtual void UpdateIsSticky() + { + ViewLayout.SectionHeadersPinToVisibleBounds = _gridCollectionView.IsGroupHeaderSticky; + } + protected virtual void UpdateGridType() { - ViewLayout.SectionInset = new UIEdgeInsets(0, 0, 0, 0); // Reset insets + // Reset insets + ViewLayout.SectionInset = new UIEdgeInsets(_firstSpacing, 0,_lastSpacing, 0); ViewLayout.MinimumInteritemSpacing = 0; CGSize itemSize = CGSize.Empty; @@ -273,6 +291,7 @@ protected virtual void UpdateGridType() _gridCollectionView.SetValue(GridCollectionView.ComputedHeightProperty, itemSize.Height); DataSource.CellSize = itemSize; + ViewLayout.EstimatedItemSize = itemSize; } protected virtual double CalcurateColumnHeight(double itemWidth) @@ -287,7 +306,10 @@ protected virtual double CalcurateColumnHeight(double itemWidth) protected virtual CGSize GetUniformItemSize(int columns) { - float width = (float)Frame.Width - (float)_gridCollectionView.ColumnSpacing * (float)(columns - 1.0f); + var margin = (float)_gridCollectionView.BothSidesMargin; + ViewLayout.SectionInset = new UIEdgeInsets(_firstSpacing,margin, _lastSpacing, margin); + + float width = (float)Frame.Width - margin * 2f - (float)_gridCollectionView.ColumnSpacing * (float)(columns - 1.0f); _gridSource.SurplusPixel = (int)width % columns; @@ -330,9 +352,28 @@ protected virtual CGSize GetAutoSpacingItemSize() var insetSurplus = (int)insetSum % 2; var inset = (float)Math.Floor(insetSum / 2.0f); - ViewLayout.SectionInset = new UIEdgeInsets(0, inset + (float)insetSurplus, 0, inset); + ViewLayout.SectionInset = new UIEdgeInsets(_firstSpacing, inset + (float)insetSurplus, _lastSpacing, inset); return new CGSize(itemWidth, itemHeight); } + + void InvalidateLayout() + { + if (!_gridCollectionView.IsGroupingEnabled) + { + ViewLayout.InvalidateLayout(); + return; + } + + // HACK: When IsGroupingEnabled is true and changing such layout size as the item size and the spacing size, + // a header cell content is sometimes not reflected or broken layout. + // By reloading with a bit delay after scrolling to Top, this issue can be avoided. + Control.SetContentOffset(CGPoint.Empty, false); + DispatchQueue.MainQueue.DispatchAfter(new DispatchTime(DispatchTime.Now, TimeSpan.FromMilliseconds(150)), () => + { + Control.ReloadData(); + ViewLayout.InvalidateLayout(); + }); + } } } diff --git a/CollectionView.iOS/GridViewLayout.cs b/CollectionView.iOS/GridViewLayout.cs new file mode 100644 index 0000000..cb517b5 --- /dev/null +++ b/CollectionView.iOS/GridViewLayout.cs @@ -0,0 +1,38 @@ +using System; +using Foundation; +using UIKit; +using CoreGraphics; +using System.Linq; +namespace AiForms.Renderers.iOS +{ + public class GridViewLayout:UICollectionViewFlowLayout + { + public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect) + { + var attributes = base.LayoutAttributesForElementsInRect(rect); + attributes.Where(x => x.RepresentedElementCategory == UICollectionElementCategory.Cell); + for (var i = 0; i < attributes.Length;i++) + { + if(attributes[i].RepresentedElementCategory == UICollectionElementCategory.Cell && attributes[i].IndexPath.Row == 0) + { + attributes[i] = LayoutAttributesForItem(attributes[i].IndexPath); + } + } + + return attributes; + } + + public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath indexPath) + { + var curAttributes = base.LayoutAttributesForItem(indexPath); + if(indexPath.Row == 0) + { + var rect = curAttributes.Frame; + curAttributes.Frame = new CGRect(SectionInset.Left, rect.Y, rect.Width, rect.Height); + return curAttributes; + } + + return curAttributes; + } + } +} diff --git a/CollectionView.iOS/HCollectionViewRenderer.cs b/CollectionView.iOS/HCollectionViewRenderer.cs index 51490ab..59bc22c 100644 --- a/CollectionView.iOS/HCollectionViewRenderer.cs +++ b/CollectionView.iOS/HCollectionViewRenderer.cs @@ -19,6 +19,8 @@ public class HCollectionViewRenderer : CollectionViewRenderer CGRect _previousFrame = CGRect.Empty; bool _disposed; HCollectionView _hCollectionView => Element as HCollectionView; + float _firstSpacing => _hCollectionView.IsGroupingEnabled ? (float)_hCollectionView.GroupFirstSpacing : 0; + float _lastSpacing => _hCollectionView.IsGroupingEnabled ? (float)_hCollectionView.GroupLastSpacing : 0; protected override void OnElementChanged(ElementChangedEventArgs e) { @@ -89,14 +91,15 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE { UpdateCellSize(); ViewLayout.InvalidateLayout(); - } else if (e.PropertyName == HCollectionView.GroupHeaderWidthProperty.PropertyName) { UpdateGroupHeaderWidth(); ViewLayout.InvalidateLayout(); } - else if (e.PropertyName == HCollectionView.SpacingProperty.PropertyName) + else if (e.PropertyName == HCollectionView.SpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupFirstSpacingProperty.PropertyName || + e.PropertyName == CollectionView.GroupLastSpacingProperty.PropertyName) { UpdateSpacing(); ViewLayout.InvalidateLayout(); @@ -166,6 +169,7 @@ protected virtual void UpdateCellSize() protected virtual void UpdateSpacing() { ViewLayout.MinimumLineSpacing = (System.nfloat)_hCollectionView.Spacing; + ViewLayout.SectionInset = new UIEdgeInsets(0, _firstSpacing, 0, _lastSpacing); } protected virtual void UpdateGroupHeaderWidth() diff --git a/CollectionView/CollectionView.cs b/CollectionView/CollectionView.cs index ef56b0d..5c1c126 100644 --- a/CollectionView/CollectionView.cs +++ b/CollectionView/CollectionView.cs @@ -85,6 +85,50 @@ public Color TouchFeedbackColor { set { SetValue(TouchFeedbackColorProperty, value); } } + /// + /// The group first spacing property. + /// + public static BindableProperty GroupFirstSpacingProperty = + BindableProperty.Create( + nameof(GroupFirstSpacing), + typeof(double), + typeof(CollectionView), + default(double), + defaultBindingMode: BindingMode.OneWay + ); + + /// + /// Gets or sets the group first spacing. + /// + /// The group first spacing. + public double GroupFirstSpacing + { + get { return (double)GetValue(GroupFirstSpacingProperty); } + set { SetValue(GroupFirstSpacingProperty, value); } + } + + /// + /// The group last spacing property. + /// + public static BindableProperty GroupLastSpacingProperty = + BindableProperty.Create( + nameof(GroupLastSpacing), + typeof(double), + typeof(CollectionView), + default(double), + defaultBindingMode: BindingMode.OneWay + ); + + /// + /// Gets or sets the group last spacing. + /// + /// The group last spacing. + public double GroupLastSpacing + { + get { return (double)GetValue(GroupLastSpacingProperty); } + set { SetValue(GroupLastSpacingProperty, value); } + } + /// /// The scroll controller property. /// diff --git a/CollectionView/GridCollectionView.cs b/CollectionView/GridCollectionView.cs index c4c5f93..8369612 100644 --- a/CollectionView/GridCollectionView.cs +++ b/CollectionView/GridCollectionView.cs @@ -332,5 +332,51 @@ public Color PullToRefreshColor set { SetValue(PullToRefreshColorProperty, value); } } + /// + /// The both sides margin property. + /// + public static BindableProperty BothSidesMarginProperty = + BindableProperty.Create( + nameof(BothSidesMargin), + typeof(double), + typeof(GridCollectionView), + default(double), + defaultBindingMode: BindingMode.OneWay + ); + + /// + /// Gets or sets the both sides margin. + /// + /// The both sides margin. + public double BothSidesMargin + { + get { return (double)GetValue(BothSidesMarginProperty); } + set { SetValue(BothSidesMarginProperty, value); } + } + + + /// + /// The is group header sticky property. + /// + public static BindableProperty IsGroupHeaderStickyProperty = + BindableProperty.Create( + nameof(IsGroupHeaderSticky), + typeof(bool), + typeof(GridCollectionView), + true, + defaultBindingMode: BindingMode.OneWay + ); + + /// + /// Gets or sets a value indicating whether this is group + /// header sticky. + /// + /// true if is group header sticky; otherwise, false. + public bool IsGroupHeaderSticky + { + get { return (bool)GetValue(IsGroupHeaderStickyProperty); } + set { SetValue(IsGroupHeaderStickyProperty, value); } + } + } } diff --git a/README-ja.md b/README-ja.md index 4ef339e..c66e089 100644 --- a/README-ja.md +++ b/README-ja.md @@ -2,6 +2,8 @@ CollectionViewは、Xamarin.Formsで使用できるGridや水平方向レイアウトに対応した柔軟なListViewです。AndroidとiOSに対応しています。 +[![Build status](https://kamusoft.visualstudio.com/NugetCI/_apis/build/status/AiForms.CollectionView)](https://kamusoft.visualstudio.com/NugetCI/_build/latest?definitionId=4) + ## コントロール * [GridCollectionView](#gridcollectionview) @@ -246,6 +248,12 @@ Grid状に各要素を配置するListViewです。これは [WrapLayout](https: * グループヘッダーのセルの高さ。 * [SpacingType](#spacingtype-enumeration) * 列間の間隔の決め方を、Between と Center から選択します。GridType が AutoSpacingGrid のときのみ有効です。(Default: Between) +* GroupFirstSpacing + * グループ内の最初のアイテムの上の間隔。 +* GroupLastSpacing + * グループ内の最後のアイテムの下の間隔。 +* BothSidesMargin + * グループヘッダーセル以外のコンテンツ領域の左右の余白。GridType が UniformGrid のときのみ有効です。 (Default: 0) * PullToRefreshColor * PullToRefreshのインジケータに使用する色。 * ItemTapCommand @@ -256,6 +264,8 @@ Grid状に各要素を配置するListViewです。これは [WrapLayout](https: * アイテムをタッチした時に表示するエフェクト色。 * [ScrollController](#scrollcontroller) * ViewModelなどでCollectionViewのスクロールを制御する場合に使用するオブジェクト。 +* IsGroupHeaderSticky + * グループヘッダーを上に固定するかどうか (iOS のみ) (default: true) ### Special Properties @@ -307,6 +317,10 @@ Grid状に各要素を配置するListViewです。これは [WrapLayout](https: * アイテムをタッチした時に表示するエフェクト色。 * [ScrollController](#scrollcontroller) * ViewModelなどでCollectionViewのスクロールを制御する場合に使用するオブジェクト。 +* GroupFirstSpacing + * グループ内の最初のアイテムの左の間隔。 +* GroupLastSpacing + * グループ内の最後のアイテムの右の間隔。 ### 行の高さについて diff --git a/README.md b/README.md index 7796347..883ccfc 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ This is a flexible ListView that has a grid and horizontal layout with reusable [Japanese](./README-ja.md) +[![Build status](https://kamusoft.visualstudio.com/NugetCI/_apis/build/status/AiForms.CollectionView)](https://kamusoft.visualstudio.com/NugetCI/_build/latest?definitionId=4) + ## Available controls * [GridCollectionView](#gridcollectionview) @@ -248,6 +250,12 @@ This is the ListView that lays out each item in a grid pattern. Though this is s * The height of a group header cell. * [SpacingType](#spacingtype-enumeration) * Select the spacing type using an enumeration value either Between or Center. This is used only when GridType is AutoSpacingGrid. (Default: Between) +* GroupFirstSpacing + * The spacing of the first item's top in a group. +* GroupLastSpacing + * The spacing of the last item's bottom in a group. +* BothSidesMargin + * The margin of the right and left sides in the content area except for a group header cell. This is used only when GridType is UniformGrid. (Default: 0) * PullToRefreshColor * The color of the PullToRefresh indicator icon. * ItemTapCommand @@ -258,6 +266,8 @@ This is the ListView that lays out each item in a grid pattern. Though this is s * The color rendered when an item is touched. * [ScrollController](#scrollcontroller) * The object for manipulating the scroll from such as ViewModel. +* IsGroupHeaderSticky + * Whether a group header is fixed at the top. (iOS only) (default: true) ### Special Properties @@ -309,6 +319,10 @@ This is the ListView that lays out each item horizontally. This can make the scr * The color rendered when an item is touched. * [ScrollController](#scrollcontroller) * The object for manipulating the scroll from such as ViewModel. +* GroupFirstSpacing + * The spacing of the first item's left in a group. +* GroupLastSpacing + * The spacing of the last item's right in a group. ### About Row Height diff --git a/Sample/Sample.Droid/MainActivity.cs b/Sample/Sample.Droid/MainActivity.cs index 4e42ed6..526a16e 100644 --- a/Sample/Sample.Droid/MainActivity.cs +++ b/Sample/Sample.Droid/MainActivity.cs @@ -1,5 +1,4 @@ -using AiForms.Extras; -using Android.App; +using Android.App; using Android.Content.PM; using Android.OS; using CarouselView.FormsPlugin.Android; @@ -23,7 +22,7 @@ protected override void OnCreate(Bundle bundle) Xamarin.Forms.Svg.Droid.SvgImage.Init(); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true); CarouselViewRenderer.Init(); - AiForms.Extras.Extras.Init(this); + AiForms.Dialogs.Dialogs.Init(this); LoadApplication(new App(new AndroidInitializer())); @@ -34,7 +33,7 @@ public class AndroidInitializer : IPlatformInitializer { public void RegisterTypes(IContainerRegistry containerRegistry) { - containerRegistry.RegisterInstance(Toast.Instance); + containerRegistry.RegisterInstance(AiForms.Dialogs.Toast.Instance); } } } diff --git a/Sample/Sample.Droid/Sample.Droid.csproj b/Sample/Sample.Droid/Sample.Droid.csproj index 717ef02..4104bb3 100644 --- a/Sample/Sample.Droid/Sample.Droid.csproj +++ b/Sample/Sample.Droid/Sample.Droid.csproj @@ -208,11 +208,17 @@ ..\packages\Xamarin.Forms.3.2.0.839982\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll - - ..\packages\AiForms.Extras.0.1.0-pre1\lib\MonoAndroid\AiForms.Extras.Abstractions.dll + + ..\packages\AiForms.SettingsView.0.3.2\lib\MonoAndroid\SettingsView.dll - - ..\packages\AiForms.Extras.0.1.0-pre1\lib\MonoAndroid\AiForms.Extras.dll + + ..\packages\AiForms.SettingsView.0.3.2\lib\MonoAndroid\SettingsView.Droid.dll + + + ..\packages\AiForms.Dialogs.0.3.2-pre\lib\MonoAndroid\AiForms.Dialogs.Abstractions.dll + + + ..\packages\AiForms.Dialogs.0.3.2-pre\lib\MonoAndroid\AiForms.Dialogs.dll diff --git a/Sample/Sample.Droid/packages.config b/Sample/Sample.Droid/packages.config index 4090463..0f58cd3 100644 --- a/Sample/Sample.Droid/packages.config +++ b/Sample/Sample.Droid/packages.config @@ -1,8 +1,9 @@  + - + diff --git a/Sample/Sample.iOS/AppDelegate.cs b/Sample/Sample.iOS/AppDelegate.cs index a475f65..d9eb375 100644 --- a/Sample/Sample.iOS/AppDelegate.cs +++ b/Sample/Sample.iOS/AppDelegate.cs @@ -5,8 +5,7 @@ using FFImageLoading.Forms; using FFImageLoading.Forms.Touch; using CarouselView.FormsPlugin.iOS; -using AiForms.Extras.Abstractions; -using AiForms.Extras; +using AiForms.Dialogs; namespace Sample.iOS { @@ -29,9 +28,11 @@ public override bool FinishedLaunching(UIApplication app, NSDictionary options) AiForms.Effects.iOS.Effects.Init(); AiForms.Renderers.iOS.CollectionViewInit.Init(); + AiForms.Renderers.iOS.SettingsViewInit.Init(); Xamarin.Forms.Svg.iOS.SvgImage.Init(); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); CarouselViewRenderer.Init(); + AiForms.Dialogs.Dialogs.Init(); LoadApplication(new App(new iOSInitializer())); diff --git a/Sample/Sample.iOS/Sample.iOS.csproj b/Sample/Sample.iOS/Sample.iOS.csproj index dc952de..d9f6f33 100644 --- a/Sample/Sample.iOS/Sample.iOS.csproj +++ b/Sample/Sample.iOS/Sample.iOS.csproj @@ -181,11 +181,17 @@ ..\packages\Xamarin.Forms.3.2.0.839982\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll - - ..\packages\AiForms.Extras.0.1.0-pre1\lib\Xamarin.iOS10\AiForms.Extras.Abstractions.dll + + ..\packages\AiForms.SettingsView.0.3.2\lib\Xamarin.iOS10\SettingsView.dll - - ..\packages\AiForms.Extras.0.1.0-pre1\lib\Xamarin.iOS10\AiForms.Extras.dll + + ..\packages\AiForms.SettingsView.0.3.2\lib\Xamarin.iOS10\SettingsView.iOS.dll + + + ..\packages\AiForms.Dialogs.0.3.2-pre\lib\Xamarin.iOS10\AiForms.Dialogs.Abstractions.dll + + + ..\packages\AiForms.Dialogs.0.3.2-pre\lib\Xamarin.iOS10\AiForms.Dialogs.dll diff --git a/Sample/Sample.iOS/packages.config b/Sample/Sample.iOS/packages.config index 950b220..1e1521f 100644 --- a/Sample/Sample.iOS/packages.config +++ b/Sample/Sample.iOS/packages.config @@ -1,8 +1,9 @@  + - + diff --git a/Sample/Sample/Sample.csproj b/Sample/Sample/Sample.csproj index df12cf4..3e5461a 100644 --- a/Sample/Sample/Sample.csproj +++ b/Sample/Sample/Sample.csproj @@ -14,7 +14,8 @@ - + + @@ -22,4 +23,7 @@ + + + \ No newline at end of file diff --git a/Sample/Sample/ViewModels/CollectionViewGroupTestViewModel.cs b/Sample/Sample/ViewModels/CollectionViewGroupTestViewModel.cs index 0eee5e0..8b9a50f 100644 --- a/Sample/Sample/ViewModels/CollectionViewGroupTestViewModel.cs +++ b/Sample/Sample/ViewModels/CollectionViewGroupTestViewModel.cs @@ -13,13 +13,10 @@ namespace Sample.ViewModels { public class CollectionViewGroupTestViewModel:CollectionViewTestViewModel { - public ObservableCollection ItemsGroupSource { get; set; } + public ReactiveCommand AddSecCommand { get; set; } = new ReactiveCommand(); public ReactiveCommand DelSecCommand { get; set; } = new ReactiveCommand(); - public ReactivePropertySlim HeaderHeight { get; } = new ReactivePropertySlim(36); - - PhotoGroup _additionalGroup; IPageDialogService _pageDlg; public CollectionViewGroupTestViewModel(IPageDialogService pageDialog):base(pageDialog) @@ -29,153 +26,6 @@ public CollectionViewGroupTestViewModel(IPageDialogService pageDialog):base(page } - public override void IndividualTest() - { - TestList.Add("Test Start").Add( - "Manipulation Test1. Has SectionB been scrolled to Top to Bottom To Center?", - async () => - { - ScrollController.ScrollToStart(); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Start, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.End, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Center, false); - } - ).Add( - "Manipulation Test2. Has SectionB been scrolled to Top to Bottom To Center with animation?", - async () => - { - - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Start, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.End, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Center, true); - - } - ).Add( - "Manipulation Test3. Has SectionC - Title14 been scrolled to Top to Bottom To Center?", - async () => - { - ScrollController.ScrollToStart(false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Start, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.End, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Center, false); - } - ).Add( - "Manipulation Test4. Has SectionC - Title14 been scrolled to Top to Bottom To Center with animation?", - async () => - { - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Start, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.End, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Center, true); - } - ).Add( - "Manipulation Test5. Has new cell been inserted before Title1?", - async () => - { - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ItemsGroupSource[0].Insert(0, GetAdditionalItem()); - } - ).Add( - "Manipulation Test6. Has new cell been inserted after Title20?", - async () => - { - ScrollController.ScrollTo(ItemsGroupSource[0][20],ItemsGroupSource[0], ScrollToPosition.End, true); - await Task.Delay(2000); - ItemsGroupSource[0].Add(GetAdditionalItem()); - } - ).Add( - "Manipulation Test7. Has new cell been inserted between Title8 and Title9?", - async () => - { - ScrollController.ScrollTo(ItemsGroupSource[0][8],ItemsGroupSource[0], ScrollToPosition.Center, true); - await Task.Delay(2000); - ItemsGroupSource[0].Insert(9, GetAdditionalItem()); - } - ).Add( - "Manipulation Test8. Has the first cell been deleted?", - async () => - { - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ItemsGroupSource[0].RemoveAt(0); - } - ).Add( - "Manipulation Test9. Has the last cell in SectionA been deleted?", - async () => - { - ScrollController.ScrollTo(ItemsGroupSource[0][21], ItemsGroupSource[0], ScrollToPosition.End, true); - await Task.Delay(2000); - ItemsGroupSource[0].RemoveAt(ItemsGroupSource[0].Count - 1); - } - ).Add( - "Manipulation Test10. Has the cell between Title8 and Title9 in SectionA been deleted?", - async () => - { - ScrollController.ScrollTo(ItemsGroupSource[0][8], ItemsGroupSource[0], ScrollToPosition.Center, true); - await Task.Delay(2000); - ItemsGroupSource[0].RemoveAt(8); - } - ).Add( - "Manipulation Test11. Has the first cell been changed to AddItem?", - async () => - { - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ItemsGroupSource[0][0] = GetAdditionalItem(); - } - ).Add( - "Manipulation Test12. Has the first cell been moved to after Title5?", - async () => - { - ScrollController.ScrollToStart(true); - await Task.Delay(2000); - ItemsGroupSource[0].Move(0, 4); - } - ).Add( - "Manipulation Test13. Has new section been inserted at last?", - async () => - { - await Task.Delay(2000); - ItemsGroupSource.Add(_additionalGroup); - await Task.Delay(2000); - ScrollController.ScrollToEnd(true); - } - ).Add( - "Manipulation Test14. Has the last section been deleted?", - async () => - { - await Task.Delay(2000); - ItemsGroupSource.Remove(_additionalGroup); - await Task.Delay(2000); - ScrollController.ScrollToEnd(true); - } - ).Add( - "Has GroupHeaderHeight been twice as large as the privious?", - async () => - { - await Task.Delay(2000); - HeaderHeight.Value *= 2; - }, - () => { - HeaderHeight.Value /= 2; - } - ); - } - void InitializeProperties() { ItemsGroupSource = new ObservableCollection(); @@ -209,101 +59,14 @@ void InitializeProperties() { Category = "CCC", }); } - var list4 = new List(); - for (var i = 5; i < 15; i++) - { - list4.Add(new PhotoItem - { - PhotoUrl = $"https://kamusoft.jp/openimage/nativecell/{i + 1}.jpg", - Title = $"Title {i + 1}", - Category = "DDD", - }); - } var group1 = new PhotoGroup(list1) { Head = "SectionA" }; var group2 = new PhotoGroup(list2) { Head = "SectionB" }; var group3 = new PhotoGroup(list3) { Head = "SectionC" }; - _additionalGroup = new PhotoGroup(list4) { Head = "SectionD" }; + ItemsGroupSource.Add(group1); ItemsGroupSource.Add(group2); ItemsGroupSource.Add(group3); - - } - - public override void InitializeCommand() - { - var addItem = new PhotoItem - { - PhotoUrl = $"https://kamusoft.jp/openimage/nativecell/1.jpg", - Title = $"AddItem", - Category = "AAA" - }; - - var addPtn = 0; - AddCommand.Subscribe(_ => - { - switch (addPtn) - { - case 0: - ItemsGroupSource[0].Insert(0, addItem); - break; - case 1: - ItemsGroupSource[0].Add(addItem); - break; - case 2: - ItemsGroupSource[0].Insert(ItemsGroupSource[0].Count / 2, addItem); - break; - } - - addPtn++; - if (addPtn > 2) - { - addPtn = 0; - } - }); - - var delPtn = 0; - DelCommand.Subscribe(_ => - { - switch (delPtn) - { - case 0: - ItemsGroupSource[0].RemoveAt(0); - break; - case 1: - ItemsGroupSource[0].RemoveAt(ItemsGroupSource[0].Count - 1); - break; - case 2: - ItemsGroupSource[0].RemoveAt(ItemsGroupSource[0].Count / 2); - break; - } - delPtn++; - if (delPtn > 2) - { - delPtn = 0; - } - }); - - RepCommand.Subscribe(_ => - { - ItemsGroupSource[0][0] = addItem; - }); - - MoveCommand.Subscribe(_ => - { - ItemsGroupSource[0].Move(0, 4); - }); - - - AddSecCommand.Subscribe(_ => - { - ItemsGroupSource.Add(_additionalGroup); - }); - - DelSecCommand.Subscribe(_ => - { - ItemsGroupSource.RemoveAt(ItemsSource.Count - 1); - }); } } } diff --git a/Sample/Sample/ViewModels/CollectionViewTestViewModel.cs b/Sample/Sample/ViewModels/CollectionViewTestViewModel.cs index a11f7f1..f62db08 100644 --- a/Sample/Sample/ViewModels/CollectionViewTestViewModel.cs +++ b/Sample/Sample/ViewModels/CollectionViewTestViewModel.cs @@ -8,12 +8,14 @@ using AiForms.Renderers; using Prism.Navigation; using System.Linq; +using Prism.Mvvm; namespace Sample.ViewModels { - public class CollectionViewTestViewModel:INavigatingAware + public class CollectionViewTestViewModel:BindableBase, INavigatingAware { public PhotoGroup ItemsSource { get; set; } + public ObservableCollection ItemsGroupSource { get; set; } public ReactiveCommand TapCommand { get; } = new ReactiveCommand(); public ReactiveCommand LongTapCommand { get; } = new ReactiveCommand(); public ReactiveProperty IsRefreshing { get; } = new ReactiveProperty(false); @@ -25,7 +27,10 @@ public class CollectionViewTestViewModel:INavigatingAware public ReactiveCommand DelCommand { get; set; } = new ReactiveCommand(); public ReactiveCommand RepCommand { get; set; } = new ReactiveCommand(); public ReactiveCommand MoveCommand { get; set; } = new ReactiveCommand(); + public ReactiveCommand NextCommand { get; set; } = new ReactiveCommand(); + public ReactiveCommand RepeatCommand { get; set; } = new ReactiveCommand(); + public ReactivePropertySlim HeaderHeight { get; } = new ReactivePropertySlim(36); public ReactivePropertySlim Background { get; } = new ReactivePropertySlim(Color.Transparent); public ReactivePropertySlim FeedbackColor { get; } = new ReactivePropertySlim(Color.Yellow); public ReactivePropertySlim GridType { get; } = new ReactivePropertySlim(AiForms.Renderers.GridType.UniformGrid); @@ -40,6 +45,13 @@ public class CollectionViewTestViewModel:INavigatingAware public ReactivePropertySlim RefreshIconColor { get; } = new ReactivePropertySlim(Color.DimGray); public ReactivePropertySlim AdditionalHeight { get; } = new ReactivePropertySlim(0); public IScrollController ScrollController { get; set; } + public ReactivePropertySlim GroupFirstSpacing { get; } = new ReactivePropertySlim(0); + public ReactivePropertySlim GroupLastSpacing { get; } = new ReactivePropertySlim(0); + public ReactivePropertySlim IsGroupHeaderSticky { get; } = new ReactivePropertySlim(true); + public ReactivePropertySlim BothSidesMargin { get; } = new ReactivePropertySlim(0); + + public List TestSections { get; set; } + IEnumerator _testEnumerator; IPageDialogService _pageDlg; @@ -48,223 +60,33 @@ public CollectionViewTestViewModel(IPageDialogService pageDialog) _pageDlg = pageDialog; InitializeProperties(); - TestSource = new TestFormViewModel + Action currentAction = null; + NextCommand.Subscribe(async _ => { - ItemsSource = TestList, - PageDialog = pageDialog - }; - + if (!_testEnumerator.MoveNext()) + { + await pageDialog.DisplayAlertAsync("", "Finished", "OK"); + return; + } + currentAction = _testEnumerator.Current.Run; + currentAction?.Invoke(); + }); + RepeatCommand.Subscribe(_ => + { + currentAction?.Invoke(); + }); } - void CommontTest() { - TestList.Add( - "PullToRefresh can't be work." - ).Add( - "Can PullToRefresh be work?", - () => { - EnabledPullToRefresh.Value = true; - } - ).Add( - "Has Refresh icon turned red?", - () => { - RefreshIconColor.Value = Color.Red; - }, - () => { - RefreshIconColor.Value = Color.DimGray; - } - ).Add( - "Has RowSpacing become much larger?", - () => { - RowSpacing.Value = 25; - }, - () => { - RowSpacing.Value = 4; - } - ).Add( - "Has ColumnHeight been changed to absolute value(250px)?", - () => { - ColumnHeight.Value = 250; - } - ).Add( - "Has ColumnHeight been changed to relative value(0.5)?", - () => { - ColumnHeight.Value = 0.5; - }, - () => { - ColumnHeight.Value = 1.0; - } - ).Add( - "Has ColumnHeight been changed to 1.0 + 50px by AdditionalHeight?", - () => { - AdditionalHeight.Value = 50; - }, - () => { - AdditionalHeight.Value = 0; - } - ).Add( - "Has Background turned from Yellow to White to Green to transparent?", - async () => - { - Background.Value = Color.Yellow; - await Task.Delay(1000); - Background.Value = Color.White; - await Task.Delay(1000); - Background.Value = Color.Green; - await Task.Delay(1000); - Background.Value = Color.Transparent; - } - ).Add( - "Tap some cells. Has FeedBack color turned Red?", - () => - { - FeedbackColor.Value = Color.Red; - }, - () => - { - FeedbackColor.Value = Color.Yellow; - } - ).Add( - "UniformGrid Test1. GridType is UniformGrid. PortraitColumns are 2.", - () => { - GridType.Value = AiForms.Renderers.GridType.UniformGrid; - PortraitColumns.Value = 2; - } - ).Add( - "UniformGrid Test2. Has Colums number been changed 2 to 3 to 4 to 5 to 6 on Portrait?", - async () => { - PortraitColumns.Value = 3; - await Task.Delay(1000); - PortraitColumns.Value = 4; - await Task.Delay(1000); - PortraitColumns.Value = 5; - await Task.Delay(1000); - PortraitColumns.Value = 6; - } - ).Add( - "UniformGrid Test3. Has ColumnSpacing been changed 0?", - () => { - ColumnSpacing.Value = 0; - } - ).Add( - "UniformGrid Test4. Has Colums number been changed 6 to 5 to 4 to 3 to 2 on Portrait?", - async () => { - PortraitColumns.Value = 5; - await Task.Delay(1000); - PortraitColumns.Value = 4; - await Task.Delay(1000); - PortraitColumns.Value = 3; - await Task.Delay(1000); - PortraitColumns.Value = 2; - } - ).Add( - "AutoSpacingGrid Test1. Column width is 150px. SpacingType is Between.", - () => { - GridType.Value = AiForms.Renderers.GridType.AutoSpacingGrid; - ColumnWidth.Value = 150; - SpacingType.Value = AiForms.Renderers.SpacingType.Between; - ColumnSpacing.Value = 4; - } - ).Add( - "AutoSpacingGrid Test2. Has Colum width been changed 150 to 120 to 90 to 60 with keeping Between?", - async () => { - ColumnWidth.Value = 120; - await Task.Delay(1000); - ColumnWidth.Value = 90; - await Task.Delay(1000); - ColumnWidth.Value = 60; - } - ).Add( - "AutoSpacingGrid Test3. Has SpacingType been changed to Center?", - () => { - SpacingType.Value = AiForms.Renderers.SpacingType.Center; - } - ).Add( - "AutoSpacingGrid Test4. Has Colum width been changed 60 to 90 to 120 to 150 with keeping Center?", - async () => { - ColumnWidth.Value = 90; - await Task.Delay(1000); - ColumnWidth.Value = 120; - await Task.Delay(1000); - ColumnWidth.Value = 150; - } - ).Add( - "AutoSpacingGrid Test5. Has ColumnSpacing been changed 0?", - () => { - ColumnSpacing.Value = 0; - } - ).Add( - "AutoSpacingGrid Test6. Has Colum width been changed 150 to 120 to 90 to 60 with keeping Center?", - async () => { - ColumnWidth.Value = 120; - await Task.Delay(1000); - ColumnWidth.Value = 90; - await Task.Delay(1000); - ColumnWidth.Value = 60; - } - ).Add( - "UniformGrid Test5. Change Landscape", - () => { - GridType.Value = AiForms.Renderers.GridType.UniformGrid; - ColumnSpacing.Value = 4; - } - ).Add( - "UniformGrid Test6. Has Colums number been changed 5 to 6 to 7 to 8 on Landscape?", - async () => { - LandscapeColumns.Value = 6; - await Task.Delay(1000); - LandscapeColumns.Value = 7; - await Task.Delay(1000); - LandscapeColumns.Value = 8; - await Task.Delay(1000); - } - ).Add( - "UniformGrid Test7. Has ColumnSpacing been changed 0?", - () => { - ColumnSpacing.Value = 0; - } - ).Add( - "UniformGrid Test8. Has Colums number been changed 8 to 7 to 6 to 5 to 4 on Landscape?", - async () => { - LandscapeColumns.Value = 7; - await Task.Delay(1000); - LandscapeColumns.Value = 6; - await Task.Delay(1000); - LandscapeColumns.Value = 5; - await Task.Delay(1000); - LandscapeColumns.Value = 4; - } - ).Add( - "AutoSpacingGrid Test7. Landscape Test. SpacingType is Center and ColumnSpacing is 4.", - () => { - GridType.Value = AiForms.Renderers.GridType.AutoSpacingGrid; - SpacingType.Value = AiForms.Renderers.SpacingType.Center; - ColumnSpacing.Value = 4; - } - ).Add( - "AutoSpacingGrid Test8. Has Colum width been changed 60 to 90 to 120 to 150 with keeping Center?", - async () => { - ColumnWidth.Value = 90; - await Task.Delay(1000); - ColumnWidth.Value = 120; - await Task.Delay(1000); - ColumnWidth.Value = 150; - } - ).Add( - "AutoSpacingGrid Test9. Has SpacingType been changed to Between?", - () => { - SpacingType.Value = AiForms.Renderers.SpacingType.Between; - } - ).Add( - "AutoSpacingGrid Test10. Has Colum width been changed 150 to 120 to 90 to 60 with keeping Between?", - async () => { - ColumnWidth.Value = 120; - await Task.Delay(1000); - ColumnWidth.Value = 90; - await Task.Delay(1000); - ColumnWidth.Value = 60; - } - ); + public virtual void OnNavigatingTo(NavigationParameters parameters) + { + TestSections = parameters.GetValue>("tests"); + + _testEnumerator = TestSections.SelectMany(x => x).Where(y => y.Check.Value).SelectMany(z => z).GetEnumerator(); + + TestSections.ForEach(x => x.SetViewModel(this)); + + RaisePropertyChanged(nameof(TestSections)); } public PhotoItem GetAdditionalItem() @@ -277,100 +99,6 @@ public PhotoItem GetAdditionalItem() }; } - public virtual void IndividualTest() - { - - - TestList.Add("Test Start").Add( - "Manipulation Test1. Has Title10 been scrolled to Top to Bottom To Center?", - async () => { - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Start, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.End, false); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Center, false); - } - ).Add( - "Manipulation Test2. Has Title10 been scrolled to Top to Bottom To Center with animation?", - async () => { - - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Start, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.End, true); - await Task.Delay(2000); - ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Center, true); - } - ).Add( - "Manipulation Test3. Has new cell been inserted before Title1?", - async () => { - - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); - await Task.Delay(2000); - ItemsSource.Insert(0, GetAdditionalItem()); - } - ).Add( - "Manipulation Test4. Click + again. Has new cell been inserted after Title20?", - async () => { - - ScrollController.ScrollTo(ItemsSource.Last(), ScrollToPosition.End, true); - await Task.Delay(2000); - ItemsSource.Add(GetAdditionalItem()); - } - ).Add( - "Manipulation Test5. Click + again. Has new cell been inserted between Title8 and Title9?", - async () => { - - ScrollController.ScrollTo(ItemsSource[8], ScrollToPosition.Center, true); - await Task.Delay(2000); - ItemsSource.Insert(9, GetAdditionalItem()); - } - ).Add( - "Manipulation Test4. Click -. Has the first cell been deleted?", - async () => { - - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); - await Task.Delay(2000); - ItemsSource.RemoveAt(0); - } - ).Add( - "Manipulation Test6. Click - again. Has the last cell been deleted?", - async () => { - - ScrollController.ScrollTo(ItemsSource.Last(), ScrollToPosition.End, true); - await Task.Delay(2000); - ItemsSource.RemoveAt(ItemsSource.Count - 1); - } - ).Add( - "Manipulation Test7. Click - again. Has the cell between Title8 and Title9 been deleted?", - async () => { - - ScrollController.ScrollTo(ItemsSource[8], ScrollToPosition.Center, true); - await Task.Delay(2000); - ItemsSource.RemoveAt(8); - } - ).Add( - "Manipulation Test8. Click R. Has the first cell been changed to AddItem?", - async () => { - - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); - await Task.Delay(2000); - ItemsSource[0] = GetAdditionalItem(); - } - ).Add( - "Manipulation Test9. Click M. Has the first cell been moved to after Title5?", - async () => { - - ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); - await Task.Delay(2000); - ItemsSource.Move(0, 4); - } - ); - } - void InitializeProperties() { @@ -404,80 +132,5 @@ void InitializeProperties() IsRefreshing.Value = false; }); } - - public virtual void InitializeCommand() - { - var addItem = new PhotoItem - { - PhotoUrl = $"https://kamusoft.jp/openimage/nativecell/1.jpg", - Title = $"AddItem", - Category = "AAA" - }; - - var addPtn = 0; - AddCommand.Subscribe(_ => - { - switch (addPtn) - { - case 0: - ItemsSource.Insert(0, addItem); - break; - case 1: - ItemsSource.Add(addItem); - break; - case 2: - ItemsSource.Insert(9, addItem); - break; - - } - - addPtn++; - if (addPtn > 2) - { - addPtn = 0; - } - }); - - var delPtn = 0; - DelCommand.Subscribe(_ => - { - switch (delPtn) - { - case 0: - ItemsSource.RemoveAt(0); - break; - case 1: - ItemsSource.RemoveAt(ItemsSource.Count - 1); - break; - case 2: - ItemsSource.RemoveAt(8); - break; - } - delPtn++; - if (delPtn > 2) - { - delPtn = 0; - } - }); - - RepCommand.Subscribe(_ => - { - ItemsSource[0] = addItem; - }); - - MoveCommand.Subscribe(_ => - { - ItemsSource.Move(0, 4); - }); - } - - - - public virtual void OnNavigatingTo(NavigationParameters parameters) - { - InitializeCommand(); - IndividualTest(); - CommontTest(); - } } } diff --git a/Sample/Sample/ViewModels/DemoPageViewModel.cs b/Sample/Sample/ViewModels/DemoPageViewModel.cs index 98ae1ff..4473112 100644 --- a/Sample/Sample/ViewModels/DemoPageViewModel.cs +++ b/Sample/Sample/ViewModels/DemoPageViewModel.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; -using AiForms.Extras.Abstractions; using Reactive.Bindings; using Prism.Services; using Sample.Views; using Xamarin.Forms; using AiForms.Renderers; +using AiForms.Dialogs.Abstractions; namespace Sample.ViewModels { diff --git a/Sample/Sample/ViewModels/GridGroupTestIndexViewModel.cs b/Sample/Sample/ViewModels/GridGroupTestIndexViewModel.cs new file mode 100644 index 0000000..e329827 --- /dev/null +++ b/Sample/Sample/ViewModels/GridGroupTestIndexViewModel.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Prism.Navigation; +using Reactive.Bindings; +using Sample.ViewModels.Tests; +using Xamarin.Forms; + +namespace Sample.ViewModels +{ + public class GridGroupTestIndexViewModel + { + public ReactiveCommand RunCommand { get; } = new ReactiveCommand(); + public ReactiveCommand AllCheckCommand { get; } = new ReactiveCommand(); + public ReactiveCommand NoneCheckCommand { get; } = new ReactiveCommand(); + public ReactiveCommand SaveCommand { get; } = new ReactiveCommand(); + + public List TestSections { get; } = new List(); + + public GridGroupTestIndexViewModel(INavigationService navigationService) + { + var Section = new TestSection { SectionTitle = "Fundamental" }; + Section.Add(new PullToRefreshTest()); + Section.Add(new RowSpacingAndHeightTest()); + Section.Add(new ColorTest()); + + TestSections.Add(Section); + + Section = new TestSection { SectionTitle = "GridType" }; + Section.Add(new UniformGridTest()); + Section.Add(new AutoSpacingTest()); + + TestSections.Add(Section); + + Section = new TestSection { SectionTitle = "Individual" }; + Section.Add(new GridGroupManipulationTest()); + + TestSections.Add(Section); + + var groups = TestSections.SelectMany(x => x).ToList(); + + + void CheckChange(bool turned) + { + foreach (var grp in groups) + { + grp.Check.Value = turned; + } + } + + AllCheckCommand.Subscribe(_ => CheckChange(true)); + NoneCheckCommand.Subscribe(_ => CheckChange(false)); + + SaveCommand.Subscribe(_ => + { + for (var i = 0; i < groups.Count; i++) + { + Application.Current.Properties[$"grid_group_check{i}"] = groups[i].Check.Value; + } + Application.Current.SavePropertiesAsync(); + }); + + for (var i = 0; i < groups.Count; i++) + { + if (Application.Current.Properties.TryGetValue($"grid_group_check{i}", out var check)) + { + groups[i].Check.Value = (bool)check; + } + } + + RunCommand.Subscribe(async _ => + { + var param = new NavigationParameters(); + param.Add("tests", TestSections); + await navigationService.NavigateAsync("CollectionViewGroupTest", param); + }); + } + } +} diff --git a/Sample/Sample/ViewModels/GridTestIndexViewModel.cs b/Sample/Sample/ViewModels/GridTestIndexViewModel.cs new file mode 100644 index 0000000..90d8c95 --- /dev/null +++ b/Sample/Sample/ViewModels/GridTestIndexViewModel.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using Sample.ViewModels.Tests; +using System.Linq; +using Reactive.Bindings; +using Xamarin.Forms; +using Prism.Navigation; + +namespace Sample.ViewModels +{ + public class GridTestIndexViewModel + { + public ReactiveCommand RunCommand { get; } = new ReactiveCommand(); + public ReactiveCommand AllCheckCommand { get; } = new ReactiveCommand(); + public ReactiveCommand NoneCheckCommand { get; } = new ReactiveCommand(); + public ReactiveCommand SaveCommand { get; } = new ReactiveCommand(); + + public List TestSections { get; } = new List(); + public GridTestIndexViewModel(INavigationService navigationService) + { + var Section = new TestSection { SectionTitle = "Fundamental" }; + Section.Add(new PullToRefreshTest()); + Section.Add(new RowSpacingAndHeightTest()); + Section.Add(new ColorTest()); + + TestSections.Add(Section); + + Section = new TestSection { SectionTitle = "GridType" }; + Section.Add(new UniformGridTest()); + Section.Add(new AutoSpacingTest()); + + TestSections.Add(Section); + + Section = new TestSection { SectionTitle = "Individual" }; + Section.Add(new GridManipulationTest()); + + TestSections.Add(Section); + + var groups = TestSections.SelectMany(x => x).ToList(); + + + void CheckChange(bool turned) + { + foreach (var grp in groups) + { + grp.Check.Value = turned; + } + } + + AllCheckCommand.Subscribe(_ => CheckChange(true)); + NoneCheckCommand.Subscribe(_ => CheckChange(false)); + + SaveCommand.Subscribe(_ => + { + for (var i = 0; i < groups.Count; i++) + { + Application.Current.Properties[$"check{i}"] = groups[i].Check.Value; + } + Application.Current.SavePropertiesAsync(); + }); + + for (var i = 0; i < groups.Count; i++) + { + if (Application.Current.Properties.TryGetValue($"check{i}", out var check)) + { + groups[i].Check.Value = (bool)check; + } + } + + RunCommand.Subscribe(async _ => + { + var param = new NavigationParameters(); + param.Add("tests", TestSections); + await navigationService.NavigateAsync("CollectionViewTest", param); + }); + } + } +} diff --git a/Sample/Sample/ViewModels/HCollectionViewTestViewModel.cs b/Sample/Sample/ViewModels/HCollectionViewTestViewModel.cs index 07055d5..89d9c1f 100644 --- a/Sample/Sample/ViewModels/HCollectionViewTestViewModel.cs +++ b/Sample/Sample/ViewModels/HCollectionViewTestViewModel.cs @@ -34,6 +34,8 @@ public class HCollectionViewTestViewModel public ReactivePropertySlim Spacing { get; } = new ReactivePropertySlim(5); public ReactivePropertySlim GroupWidth { get; } = new ReactivePropertySlim(50); public ReactivePropertySlim IsInfinite { get; } = new ReactivePropertySlim(false); + public ReactivePropertySlim GroupFirstSpacing { get; } = new ReactivePropertySlim(0); + public ReactivePropertySlim GroupLastSpacing { get; } = new ReactivePropertySlim(0); public IScrollController ScrollController {get;set;} public IScrollController ScrollController2 { get; set; } @@ -237,6 +239,18 @@ public HCollectionViewTestViewModel(IPageDialogService pageDialog) { Spacing.Value /= 2; } + ).Add( + "Has Group First Spacing been 30px in Grouped?", + () => + { + GroupFirstSpacing.Value = 30; + } + ).Add( + "Has Group Last Spacing been 30px in Grouped?", + () => + { + GroupLastSpacing.Value = 30; + } ).Add( "Has Column width been twice as large as previous?", () => diff --git a/Sample/Sample/ViewModels/TestFormViewModel.cs b/Sample/Sample/ViewModels/TestFormViewModel.cs index 026595d..5692fce 100644 --- a/Sample/Sample/ViewModels/TestFormViewModel.cs +++ b/Sample/Sample/ViewModels/TestFormViewModel.cs @@ -4,6 +4,12 @@ using Unity.Attributes; using Prism.Services; using System.Linq; +using Xamarin.Forms; +using System.Reflection; +using System.Threading.Tasks; +using Sample.Views; +using AiForms.Dialogs; + namespace Sample.ViewModels { public class TestFormViewModel @@ -71,5 +77,78 @@ public class TestItem public bool Result { get; set; } public Action Action { get; set; } public Action CancelAction { get; set; } + + public bool IsFirstItem { get; set; } + public bool IsLastItem { get; set; } + public LayoutAlignment VAlign { get; set; } = LayoutAlignment.Center; + public TestGroup Parent { get; set; } + + public async void Run() + { + if(IsFirstItem) + { + Toast.Instance.Show(new {Message=Parent.GroupTitle,VAlign=LayoutAlignment.Start}); + Parent?.Initialize(); + await Task.Delay(500); + } + Toast.Instance.Show(this); + Parent.SetUp(); + await Task.Delay(500); + Action?.Invoke(); + + if(IsLastItem) + { + Parent?.Destroy(); + } + } + } + + public class TestAttribute:Attribute{ + public string Message { get; set; } + } + + public class TestGroup:List + { + public CollectionViewTestViewModel VM { get; set; } + public string GroupTitle { get; set; } + public ReactivePropertySlim Check { get; } = new ReactivePropertySlim(); + + + public TestGroup(string groupTitle) + { + GroupTitle = groupTitle; + var methods = this.GetType().GetMethods().Where(x => x.GetCustomAttribute() != null); + + foreach (var method in methods) + { + var methodAction = (Action)method.CreateDelegate(typeof(Action), this); + var testAttr = method.GetCustomAttribute(); + + Add(new TestItem { Parent = this,Action = methodAction, Message = testAttr.Message }); + } + + this[0].IsFirstItem = true; + this[methods.Count() - 1].IsLastItem = true; + } + + public virtual void Initialize(){} + + public virtual void Destroy(){} + + public virtual void SetUp(){} + + } + + public class TestSection:List + { + public string SectionTitle { get; set; } + + public void SetViewModel(CollectionViewTestViewModel vm) + { + foreach(var group in this) + { + group.VM = vm; + } + } } } diff --git a/Sample/Sample/ViewModels/Tests/AutoSpacingTest.cs b/Sample/Sample/ViewModels/Tests/AutoSpacingTest.cs new file mode 100644 index 0000000..c9f3991 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/AutoSpacingTest.cs @@ -0,0 +1,104 @@ +using System; +using System.Threading.Tasks; + +namespace Sample.ViewModels.Tests +{ + public class AutoSpacingTest:TestGroup + { + public AutoSpacingTest():base("AutoSpacing") + { + } + + public override void Initialize() + { + base.Initialize(); + VM.GridType.Value = AiForms.Renderers.GridType.AutoSpacingGrid; + VM.ColumnWidth.Value = 150; + VM.SpacingType.Value = AiForms.Renderers.SpacingType.Between; + VM.ColumnSpacing.Value = 4; + } + + public override void Destroy() + { + base.Destroy(); + Initialize(); + } + + [Test(Message = "Has Colum width been changed 150 to 120 to 90 to 60 with keeping Between?")] + public async void ColumnNumberDecrement() + { + VM.ColumnWidth.Value = 120; + await Task.Delay(1000); + VM.ColumnWidth.Value = 90; + await Task.Delay(1000); + VM.ColumnWidth.Value = 60; + } + + [Test(Message = "Has SpacingType been changed to Center?")] + public void ChangeTypeCenter() + { + VM.SpacingType.Value = AiForms.Renderers.SpacingType.Center; + } + + [Test(Message = "Has Colum width been changed 60 to 90 to 120 to 150 with keeping Center?")] + public async void ColumnNumberIncrement() + { + VM.ColumnWidth.Value = 90; + await Task.Delay(1000); + VM.ColumnWidth.Value = 120; + await Task.Delay(1000); + VM.ColumnWidth.Value = 150; + } + + [Test(Message = "Has ColumnSpacing been changed 0?")] + public void ChangeSpacing0() + { + VM.ColumnSpacing.Value = 0; + } + + [Test(Message = "Has Colum width been changed 150 to 120 to 90 to 60 with keeping Center?")] + public async void ColumnNumberDecrement2() + { + VM.ColumnWidth.Value = 120; + await Task.Delay(1000); + VM.ColumnWidth.Value = 90; + await Task.Delay(1000); + VM.ColumnWidth.Value = 60; + } + + [Test(Message = "Change Landscape.")] + public void ChangeLandscape() + { + VM.ColumnSpacing.Value = 4; + } + + [Test(Message = "Has Colum width been changed 60 to 90 to 120 to 150 with keeping Center?")] + public async void ColumnNumberIncrement2() + { + VM.ColumnWidth.Value = 90; + await Task.Delay(1000); + VM.ColumnWidth.Value = 120; + await Task.Delay(1000); + VM.ColumnWidth.Value = 150; + } + + [Test(Message = "Has SpacingType been changed to Between?")] + public void ChnageBetweeen() + { + VM.SpacingType.Value = AiForms.Renderers.SpacingType.Between; + } + + [Test(Message = "Has Colum width been changed 150 to 120 to 90 to 60 with keeping Between?")] + public async void ColumnNumberDecrement3() + { + VM.ColumnWidth.Value = 120; + await Task.Delay(1000); + VM.ColumnWidth.Value = 90; + await Task.Delay(1000); + VM.ColumnWidth.Value = 60; + } + + [Test(Message = "Change Portrait")] + public void ChangePortrait(){} + } +} diff --git a/Sample/Sample/ViewModels/Tests/ColorTest.cs b/Sample/Sample/ViewModels/Tests/ColorTest.cs new file mode 100644 index 0000000..951d500 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/ColorTest.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Sample.ViewModels.Tests +{ + public class ColorTest:TestGroup + { + public ColorTest():base("Color") + { + } + + public override void SetUp() + { + base.SetUp(); + VM.Background.Value = Color.White; + VM.FeedbackColor.Value = Color.Yellow; + } + + [Test(Message = "Has Background turned from Yellow to White to Green to transparent?")] + public async void BackgroundColor() + { + VM.Background.Value = Color.Yellow; + await Task.Delay(1000); + VM.Background.Value = Color.White; + await Task.Delay(1000); + VM.Background.Value = Color.Green; + await Task.Delay(1000); + VM.Background.Value = Color.Transparent; + } + + [Test(Message = "Tap some cells. Has FeedBack color turned Red?")] + public void FeedbackColor() + { + VM.FeedbackColor.Value = Color.Red; + } + } +} diff --git a/Sample/Sample/ViewModels/Tests/GridGroupManipulationTest.cs b/Sample/Sample/ViewModels/Tests/GridGroupManipulationTest.cs new file mode 100644 index 0000000..6f5efe8 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/GridGroupManipulationTest.cs @@ -0,0 +1,171 @@ +using System; +using System.Threading.Tasks; +using AiForms.Renderers; +using System.Collections.ObjectModel; +using Xamarin.Forms; +using System.Collections.Generic; + +namespace Sample.ViewModels.Tests +{ + public class GridGroupManipulationTest:TestGroup + { + + IScrollController ScrollController => VM.ScrollController; + PhotoGroup ItemsSource => VM.ItemsSource; + ObservableCollection ItemsGroupSource => VM.ItemsGroupSource; + PhotoGroup _additionalGroup; + + public GridGroupManipulationTest():base("GridGroupManipulation") + { + } + + public override void Initialize() + { + base.Initialize(); + var list = new List(); + for (var i = 5; i < 15; i++) + { + list.Add(new PhotoItem + { + PhotoUrl = $"https://kamusoft.jp/openimage/nativecell/{i + 1}.jpg", + Title = $"Title {i + 1}", + Category = "DDD", + }); + } + _additionalGroup = new PhotoGroup(list) { Head = "SectionD" }; + + } + + [Test(Message = "Has SectionB been scrolled to Top to Bottom To Center?")] + public async void ScrollTest() + { + ScrollController.ScrollToStart(); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Start, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.End, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Center, false); + } + + [Test(Message = "Has SectionB been scrolled to Top to Bottom To Center with animation?")] + public async void ScrollTest2() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Start, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.End, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[1][0], ItemsGroupSource[1], ScrollToPosition.Center, true); + } + + [Test(Message = "Has SectionC - Title14 been scrolled to Top to Bottom To Center?")] + public async void ScrollTest3() + { + ScrollController.ScrollToStart(false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Start, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.End, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Center, false); + } + + [Test(Message = "Has SectionC - Title14 been scrolled to Top to Bottom To Center with animation?")] + public async void ScrollTest4() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Start, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.End, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsGroupSource[2][3], ItemsGroupSource[2], ScrollToPosition.Center, true); + } + + [Test(Message = "Has new cell been inserted before Title1?")] + public async void InsertTest() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ItemsGroupSource[0].Insert(0, VM.GetAdditionalItem()); + } + + [Test(Message = "Has new cell been inserted after Title20?")] + public async void InsertTest2() + { + ScrollController.ScrollTo(ItemsGroupSource[0][20], ItemsGroupSource[0], ScrollToPosition.End, true); + await Task.Delay(2000); + ItemsGroupSource[0].Add(VM.GetAdditionalItem()); + } + + [Test(Message = "Has new cell been inserted between Title8 and Title9?")] + public async void InsertTest3() + { + ScrollController.ScrollTo(ItemsGroupSource[0][8], ItemsGroupSource[0], ScrollToPosition.Center, true); + await Task.Delay(2000); + ItemsGroupSource[0].Insert(9, VM.GetAdditionalItem()); + } + + [Test(Message = "Has the first cell been deleted?")] + public async void DeleteTest() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ItemsGroupSource[0].RemoveAt(0); + } + + [Test(Message = "Has the last cell in SectionA been deleted?")] + public async void DeleteTest2() + { + ScrollController.ScrollTo(ItemsGroupSource[0][21], ItemsGroupSource[0], ScrollToPosition.End, true); + await Task.Delay(2000); + ItemsGroupSource[0].RemoveAt(ItemsGroupSource[0].Count - 1); + } + + [Test(Message = "Has the cell between Title8 and Title9 in SectionA been deleted?")] + public async void DeleteTest3() + { + ScrollController.ScrollTo(ItemsGroupSource[0][8], ItemsGroupSource[0], ScrollToPosition.Center, true); + await Task.Delay(2000); + ItemsGroupSource[0].RemoveAt(8); + } + + [Test(Message = "Has the first cell been changed to AddItem?")] + public async void ReplaceTest() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ItemsGroupSource[0][0] = VM.GetAdditionalItem(); + } + + [Test(Message = "Has the first cell been moved to after Title5?")] + public async void MoveTest() + { + ScrollController.ScrollToStart(true); + await Task.Delay(2000); + ItemsGroupSource[0].Move(0, 4); + } + + [Test(Message = "Has new section been inserted at last?")] + public async void SectionInsertTest() + { + await Task.Delay(2000); + ItemsGroupSource.Add(_additionalGroup); + await Task.Delay(2000); + ScrollController.ScrollToEnd(true); + } + + [Test(Message = "Has the last section been deleted?")] + public async void SectionDeleteTest() + { + await Task.Delay(2000); + ItemsGroupSource.Remove(_additionalGroup); + await Task.Delay(2000); + ScrollController.ScrollToEnd(true); + } + + + } +} diff --git a/Sample/Sample/ViewModels/Tests/GridManipulationTest.cs b/Sample/Sample/ViewModels/Tests/GridManipulationTest.cs new file mode 100644 index 0000000..bc4ccb5 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/GridManipulationTest.cs @@ -0,0 +1,106 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AiForms.Renderers; +using Xamarin.Forms; + +namespace Sample.ViewModels.Tests +{ + public class GridManipulationTest:TestGroup + { + IScrollController ScrollController => VM.ScrollController; + PhotoGroup ItemsSource => VM.ItemsSource; + + public GridManipulationTest():base("GridManipulation") + { + } + + [Test(Message = "Has Title10 been scrolled to Top to Bottom To Center?")] + public async void ScrollTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Start, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.End, false); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Center, false); + } + + [Test(Message = "Has Title10 been scrolled to Top to Bottom To Center with animation?")] + public async void ScrollAnimeTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Start, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.End, true); + await Task.Delay(2000); + ScrollController.ScrollTo(ItemsSource[9], ScrollToPosition.Center, true); + } + + [Test(Message = "Has new cell been inserted before Title1?")] + public async void InsertTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); + await Task.Delay(2000); + ItemsSource.Insert(0, VM.GetAdditionalItem()); + } + + [Test(Message = "Has new cell been inserted after Title20?")] + public async void InsertTest2() + { + ScrollController.ScrollTo(ItemsSource.Last(), ScrollToPosition.End, true); + await Task.Delay(2000); + ItemsSource.Add(VM.GetAdditionalItem()); + } + + [Test(Message = "Has new cell been inserted between Title8 and Title9?")] + public async void InsertTest3() + { + ScrollController.ScrollTo(ItemsSource[8], ScrollToPosition.Center, true); + await Task.Delay(2000); + ItemsSource.Insert(9, VM.GetAdditionalItem()); + } + + [Test(Message = "Has the first cell been deleted?")] + public async void DeleteTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); + await Task.Delay(2000); + ItemsSource.RemoveAt(0); + } + + [Test(Message = "Has the last cell been deleted?")] + public async void DeleteTest2() + { + ScrollController.ScrollTo(ItemsSource.Last(), ScrollToPosition.End, true); + await Task.Delay(2000); + ItemsSource.RemoveAt(ItemsSource.Count - 1); + } + + [Test(Message = "Has the cell between Title8 and Title9 been deleted?")] + public async void DeleteTest3() + { + ScrollController.ScrollTo(ItemsSource[8], ScrollToPosition.Center, true); + await Task.Delay(2000); + ItemsSource.RemoveAt(8); + } + + [Test(Message = "Has the first cell been changed to AddItem?")] + public async void ReplaceTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); + await Task.Delay(2000); + ItemsSource[0] = VM.GetAdditionalItem(); + } + + [Test(Message = "Has the first cell been moved to after Title5?")] + public async void MoveTest() + { + ScrollController.ScrollTo(ItemsSource.First(), ScrollToPosition.Start, true); + await Task.Delay(2000); + ItemsSource.Move(0, 4); + } + } +} diff --git a/Sample/Sample/ViewModels/Tests/PullToRefreshTest.cs b/Sample/Sample/ViewModels/Tests/PullToRefreshTest.cs new file mode 100644 index 0000000..7fdf179 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/PullToRefreshTest.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Sample.ViewModels.Tests +{ + public class PullToRefreshTest:TestGroup + { + public PullToRefreshTest():base("PullToRefresh"){} + + public override void SetUp() + { + base.SetUp(); + VM.EnabledPullToRefresh.Value = true; + VM.RefreshIconColor.Value = Color.DimGray; + } + + [Test(Message = "PullToRefresh can't be work.")] + public void Disabled() + { + VM.EnabledPullToRefresh.Value = false; + } + + [Test(Message = "Can PullToRefresh be work?")] + public void Enabled() + { + VM.EnabledPullToRefresh.Value = true; + } + + [Test(Message = "Has Refresh icon turned red?")] + public void TurnIconColor() + { + VM.RefreshIconColor.Value = Color.Red; + } + } +} diff --git a/Sample/Sample/ViewModels/Tests/RowSpacingAndHeightTest.cs b/Sample/Sample/ViewModels/Tests/RowSpacingAndHeightTest.cs new file mode 100644 index 0000000..8d6c3da --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/RowSpacingAndHeightTest.cs @@ -0,0 +1,72 @@ +using System; +namespace Sample.ViewModels.Tests +{ + public class RowSpacingAndHeightTest:TestGroup + { + public RowSpacingAndHeightTest():base("RowSpacingAndHeight") + { + } + + public override void SetUp() + { + base.SetUp(); + VM.RowSpacing.Value = 4.0; + VM.GroupFirstSpacing.Value = 0; + VM.GroupLastSpacing.Value = 0; + VM.ColumnHeight.Value = 1.0; + VM.IsGroupHeaderSticky.Value = true; + VM.AdditionalHeight.Value = 0; + VM.HeaderHeight.Value = 36; + } + + [Test(Message = "Has RowSpacing become much larger?")] + public void RowSpacing() + { + VM.RowSpacing.Value = 25; + } + + [Test(Message = "Has Group First Spacing been 30px in Grouped?")] + public void FirstSpacing() + { + VM.GroupFirstSpacing.Value = 30; + } + + [Test(Message = "Has Group Last Spacing been 30px in Grouped?")] + public void LastSpacing() + { + VM.GroupLastSpacing.Value = 30; + } + + [Test(Message = "Has ColumnHeight been changed to absolute value(250px)?")] + public void ColumnHeightAbsolute() + { + VM.ColumnHeight.Value = 250; + } + + [Test(Message = "Has ColumnHeight been changed to relative value(0.5)?")] + public void ColumnHeightProportional() + { + VM.ColumnHeight.Value = 0.5; + } + + [Test(Message = "Has ColumnHeight been changed to 1.0 + 50px by AdditionalHeight?")] + public void AdditionalHeight() + { + VM.AdditionalHeight.Value = 50; + } + + [Test(Message = "Has GroupHeaderHeight been twice as large as the privious?")] + public void GroupHeaderHeight() + { + VM.HeaderHeight.Value *= 2; + } + + + [Test(Message = "Has HeaderCell position been released from sticky? (iOS)")] + public void Sticky() + { + VM.IsGroupHeaderSticky.Value = false; + } + + } +} diff --git a/Sample/Sample/ViewModels/Tests/UniformGridTest.cs b/Sample/Sample/ViewModels/Tests/UniformGridTest.cs new file mode 100644 index 0000000..1326772 --- /dev/null +++ b/Sample/Sample/ViewModels/Tests/UniformGridTest.cs @@ -0,0 +1,102 @@ +using System; +using System.Threading.Tasks; + +namespace Sample.ViewModels.Tests +{ + public class UniformGridTest : TestGroup + { + public UniformGridTest() : base("UniformGrid") + { + } + + public override void Initialize() + { + base.Initialize(); + VM.GridType.Value = AiForms.Renderers.GridType.UniformGrid; + VM.PortraitColumns.Value = 2; + VM.LandscapeColumns.Value = 4; + VM.BothSidesMargin.Value = 10; + VM.ColumnSpacing.Value = 4; + } + + public override void Destroy() + { + base.Destroy(); + Initialize(); + } + + public override void SetUp() + { + base.SetUp(); + } + + [Test(Message = "Has Colums number been changed 2 to 3 to 4 to 5 to 6 on Portrait?")] + public async void ColumnNumberIncrement() + { + VM.PortraitColumns.Value = 3; + await Task.Delay(1000); + VM.PortraitColumns.Value = 4; + await Task.Delay(1000); + VM.PortraitColumns.Value = 5; + await Task.Delay(1000); + VM.PortraitColumns.Value = 6; + } + + [Test(Message = "Has ColumnSpacing / BothSidesMargin been changed 0?")] + public void SpacingAndMargin() + { + VM.ColumnSpacing.Value = 0; + VM.BothSidesMargin.Value = 0; + } + + [Test(Message = "Has Colums number been changed 6 to 5 to 4 to 3 to 2 on Portrait?")] + public async void ColumnNumberDecrement() + { + VM.PortraitColumns.Value = 5; + await Task.Delay(1000); + VM.PortraitColumns.Value = 4; + await Task.Delay(1000); + VM.PortraitColumns.Value = 3; + await Task.Delay(1000); + VM.PortraitColumns.Value = 2; + } + + [Test(Message = "Change Landscape. Is Columns number 4?")] + public void ChangeLandscape() { } + + [Test(Message = "Has Colums number been changed 5 to 6 to 7 to 8 on Landscape?")] + public async void LandscapeColumnNumber() + { + VM.LandscapeColumns.Value = 5; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 6; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 7; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 8; + await Task.Delay(1000); + } + + [Test(Message = "Have ColumnSpacing and BothSidesMargin been changed 4 and 10?")] + public void SpacingAndMargin2() + { + VM.ColumnSpacing.Value = 4; + VM.BothSidesMargin.Value = 10; + } + + [Test(Message = "Has Colums number been changed 8 to 7 to 6 to 5 to 4 on Landscape?")] + public async void LandscapeColumnNumber2() + { + VM.LandscapeColumns.Value = 7; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 6; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 5; + await Task.Delay(1000); + VM.LandscapeColumns.Value = 4; + } + + [Test(Message = "Change Portrait")] + public void ChangePortrait() { } + } +} diff --git a/Sample/Sample/Views/CollectionVIewTest.xaml b/Sample/Sample/Views/CollectionVIewTest.xaml index 2b56a27..9f036d5 100644 --- a/Sample/Sample/Views/CollectionVIewTest.xaml +++ b/Sample/Sample/Views/CollectionVIewTest.xaml @@ -8,74 +8,67 @@ x:Class="Sample.Views.CollectionViewTest"> - + + + - - - - RecycleElement - - - - - - - - - - - - + + + RecycleElement + - - - - - - - - - - + + + + + + + + + + diff --git a/Sample/Sample/Views/CollectionViewGroupTest.xaml b/Sample/Sample/Views/CollectionViewGroupTest.xaml index 8e8cb16..ed2ec7c 100644 --- a/Sample/Sample/Views/CollectionViewGroupTest.xaml +++ b/Sample/Sample/Views/CollectionViewGroupTest.xaml @@ -7,84 +7,74 @@ xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms" x:Class="Sample.Views.CollectionViewGroupTest"> - - - - - - + + + - - - - RecycleElement - - - - - - - - - - - - - - - - - + + + + RecycleElement + + + + + + + + + + + + + + + + + - - - - - - - - - - diff --git a/Sample/Sample/Views/GridGroupTestIndex.xaml b/Sample/Sample/Views/GridGroupTestIndex.xaml new file mode 100644 index 0000000..f12fc87 --- /dev/null +++ b/Sample/Sample/Views/GridGroupTestIndex.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/Views/GridGroupTestIndex.xaml.cs b/Sample/Sample/Views/GridGroupTestIndex.xaml.cs new file mode 100644 index 0000000..044498a --- /dev/null +++ b/Sample/Sample/Views/GridGroupTestIndex.xaml.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +using Xamarin.Forms; + +namespace Sample.Views +{ + public partial class GridGroupTestIndex : ContentPage + { + public GridGroupTestIndex() + { + InitializeComponent(); + } + } +} diff --git a/Sample/Sample/Views/GridTestIndex.xaml b/Sample/Sample/Views/GridTestIndex.xaml new file mode 100644 index 0000000..48ea251 --- /dev/null +++ b/Sample/Sample/Views/GridTestIndex.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/Views/GridTestIndex.xaml.cs b/Sample/Sample/Views/GridTestIndex.xaml.cs new file mode 100644 index 0000000..3772218 --- /dev/null +++ b/Sample/Sample/Views/GridTestIndex.xaml.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +using Xamarin.Forms; + +namespace Sample.Views +{ + public partial class GridTestIndex : ContentPage + { + public GridTestIndex() + { + InitializeComponent(); + } + } +} diff --git a/Sample/Sample/Views/HCollectionViewTest.xaml b/Sample/Sample/Views/HCollectionViewTest.xaml index c075cfc..e9e70ab 100644 --- a/Sample/Sample/Views/HCollectionViewTest.xaml +++ b/Sample/Sample/Views/HCollectionViewTest.xaml @@ -17,6 +17,8 @@