From ed2ad60b2546e335459749ca5d77f40abc844a49 Mon Sep 17 00:00:00 2001 From: "sung-su.kim" Date: Wed, 8 Apr 2020 01:53:28 +0900 Subject: [PATCH] [Tizen] Changed CarouselView scroll logic according to the Core change. (#10235) * [Tizen] Invoke ItemsView Scrolled event * [Tizen] Changed CarouselView scroll logic * [Tizen] Add IndicatorView --- Stubs/Xamarin.Forms.Platform.cs | 2 - .../ControlGallery.Tizen.cs | 2 +- .../Native/CollectionView/CollectionView.cs | 19 +++- .../CollectionView/GridLayoutManager.cs | 23 ++++ .../ICollectionViewLayoutManager.cs | 2 + .../Native/CollectionView/IndicatorView.cs | 80 ++++++++++++++ .../CollectionView/LinearLayoutManager.cs | 16 +++ .../Properties/AssemblyInfo.cs | 2 + .../Renderers/CarouselViewRenderer.cs | 103 +++++++++++++----- .../Renderers/IndicatorViewRenderer.cs | 59 ++++++++++ .../Renderers/ItemsViewRenderer.cs | 10 +- .../StaticRegistrar.cs | 1 + 12 files changed, 287 insertions(+), 32 deletions(-) create mode 100644 Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs create mode 100644 Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs diff --git a/Stubs/Xamarin.Forms.Platform.cs b/Stubs/Xamarin.Forms.Platform.cs index ef9b75db9..49cadd188 100644 --- a/Stubs/Xamarin.Forms.Platform.cs +++ b/Stubs/Xamarin.Forms.Platform.cs @@ -124,9 +124,7 @@ namespace Xamarin.Forms.Platform internal class _CheckBoxRenderer { } #endif -#if !TIZEN4_0 [RenderWith(typeof(IndicatorViewRenderer))] -#endif internal class _IndicatorViewRenderer { } #if __IOS__ diff --git a/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs b/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs index 9ef12bd05..c737c052a 100644 --- a/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs +++ b/Xamarin.Forms.ControlGallery.Tizen/ControlGallery.Tizen.cs @@ -22,7 +22,7 @@ namespace Xamarin.Forms.ControlGallery.Tizen { var app = new MainApplication(); FormsMaps.Init("HERE", "write-your-API-key-here"); - Forms.SetFlags("CollectionView_Experimental", "Shell_Experimental", "MediaElement_Experimental"); + Forms.SetFlags("CollectionView_Experimental", "Shell_Experimental", "MediaElement_Experimental", "IndicatorView_Experimental"); Forms.Init(app); FormsMaterial.Init(); app.Run(args); diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs index b507f5250..f673cf4fc 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs @@ -27,6 +27,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native SnapPointsType _snapPoints; ESize _itemSize = new ESize(-1, -1); + public event EventHandler Scrolled; + public CollectionView(EvasObject parent) : base(parent) { SetLayoutCallback(OnLayout); @@ -522,9 +524,24 @@ namespace Xamarin.Forms.Platform.Tizen.Native _innerLayout.MinimumHeight = size.Height; } + int _previousHorizontalOffset = 0; + int _previousVerticalOffset = 0; void OnScrolled(object sender, EventArgs e) { - _layoutManager.LayoutItems(Scroller.CurrentRegion); + _layoutManager.LayoutItems(ViewPort); + var args = new ItemsViewScrolledEventArgs(); + args.FirstVisibleItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X, ViewPort.Y); + args.CenterItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X + (ViewPort.Width / 2), ViewPort.Y + (ViewPort.Height / 2)); + args.LastVisibleItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X + ViewPort.Width, ViewPort.Y + ViewPort.Height); + args.HorizontalOffset = ViewPort.X; + args.HorizontalDelta = ViewPort.X - _previousHorizontalOffset; + args.VerticalOffset = ViewPort.Y; + args.VerticalDelta = ViewPort.Y - _previousVerticalOffset; + + Scrolled?.Invoke(this, args); + + _previousHorizontalOffset = ViewPort.X; + _previousVerticalOffset = ViewPort.Y; } void UpdateSnapPointsType(SnapPointsType snapPoints) diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs index 10359c9b3..b9fcc0936 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs @@ -338,6 +338,29 @@ namespace Xamarin.Forms.Platform.Tizen.Native } } + public int GetVisibleItemIndex(int x, int y) + { + int index = 0; + if (x < 0 || y < 0) + return index; + if (_scrollCanvasSize.Width < x || _scrollCanvasSize.Height < y) + return CollectionView.Count - 1; + + int first = (IsHorizontal ? x : y) / BaseItemSize; + if (_hasUnevenRows) + first = _accumulatedItemSizes.FindIndex(current => (IsHorizontal ? x : y) <= current); + + int second = (IsHorizontal ? y : x) / ColumnSize; + if (second == Span) + second -= 1; + + index = (first * Span) + second; + + if (index < CollectionView.Count) + return index; + return CollectionView.Count - 1; + } + void InitializeMeasureCache() { _baseItemSize = 0; diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs index 4b4c6266b..891fb17b6 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs @@ -28,5 +28,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native void Reset(); void ItemMeasureInvalidated(int index); + + int GetVisibleItemIndex(int x, int y); } } diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs new file mode 100644 index 000000000..102035095 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using ElmSharp; + +namespace Xamarin.Forms.Platform.Tizen.Native +{ + public class IndicatorView : Index + { + List _list = new List(); + + public IndicatorView(EvasObject parent) : base(parent) + { + AutoHide = false; + IsHorizontal = true; + Style = "pagecontrol"; + if (Device.Idiom == TargetIdiom.Watch) + Style = "circle"; + } + + public event EventHandler SelectedPosition; + + public void UpdateSelectedIndex(int index) + { + if (index > -1 && index < _list.Count) + { + _list[index].Select(true); + } + } + + public void AppendIndex(int count = 1) + { + for (int i = 0; i < count; i++) + { + var item = Append(null); + item.Selected += OnSelected; + _list.Add(item); + } + if (Device.Idiom == TargetIdiom.Watch) + ApplyStyle(); + } + + public void ClearIndex() + { + foreach (var item in _list) + { + item.Selected -= OnSelected; + } + _list.Clear(); + Clear(); + } + + void ApplyStyle() + { + foreach (var item in _list) + { + int center = 10; + int start = center - (_list.Count / 2); + int index = _list.IndexOf(item); + int position = start + index; + if (_list.Count % 2 == 0) + { + string itemStyle = "item/even_" + position; + item.Style = itemStyle; + } + else + { + string itemStyle = "item/odd_" + position; + item.Style = itemStyle; + } + } + } + + void OnSelected(object sender, EventArgs e) + { + var index = _list.IndexOf((IndexItem)sender); + SelectedPosition?.Invoke(this, new SelectedPositionChangedEventArgs(index)); + UpdateSelectedIndex(index); + } + } +} diff --git a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs index 012404df2..aca667a69 100644 --- a/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs +++ b/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs @@ -296,6 +296,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native } } + public int GetVisibleItemIndex(int x, int y) + { + int coordinate = IsHorizontal ? x : y; + int canvasSize = IsHorizontal ? _scrollCanvasSize.Width : _scrollCanvasSize.Height; + + if (coordinate < 0) + return 0; + if (canvasSize < coordinate) + return CollectionView.Count - 1; + + if (!_hasUnevenRows) + return coordinate / BaseItemSize; + else + return _accumulatedItemSizes.FindIndex(current => coordinate <= current); + } + void InitializeMeasureCache() { _baseItemSize = 0; diff --git a/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs index 3202fb749..fc8732833 100644 --- a/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs @@ -30,6 +30,7 @@ using Xamarin.Forms.Platform.Tizen; [assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] [assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))] [assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))] +[assembly: ExportRenderer(typeof(IndicatorView), typeof(IndicatorViewRenderer))] [assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))] [assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))] [assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))] @@ -42,6 +43,7 @@ using Xamarin.Forms.Platform.Tizen; [assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))] [assembly: ExportRenderer(typeof(RefreshView), typeof(RefreshViewRenderer))] [assembly: ExportRenderer(typeof(MediaElement), typeof(MediaElementRenderer))] +[assembly: ExportRenderer(typeof(IndicatorView), typeof(IndicatorViewRenderer))] [assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))] [assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImageSourceHandler))] diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/CarouselViewRenderer.cs index 3dc9b4ff3..6da6f8244 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/CarouselViewRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/CarouselViewRenderer.cs @@ -1,4 +1,6 @@ -namespace Xamarin.Forms.Platform.Tizen +using System; + +namespace Xamarin.Forms.Platform.Tizen { public class CarouselViewRenderer : ItemsViewRenderer { @@ -7,7 +9,8 @@ RegisterPropertyHandler(CarouselView.ItemsLayoutProperty, UpdateItemsLayout); RegisterPropertyHandler(CarouselView.IsBounceEnabledProperty, UpdateIsBounceEnabled); RegisterPropertyHandler(CarouselView.IsSwipeEnabledProperty, UpdateIsSwipeEnabled); - RegisterPropertyHandler(CarouselView.PositionProperty, UpdatePosition); + RegisterPropertyHandler(CarouselView.PositionProperty, UpdatePositionFromElement); + RegisterPropertyHandler(CarouselView.CurrentItemProperty, UpdateCurrentItemFromElement); } protected override Native.CarouselView CreateNativeControl(ElmSharp.EvasObject parent) @@ -20,17 +23,23 @@ return Element.ItemsLayout; } + ElmSharp.SmartEvent _animationStart; + ElmSharp.SmartEvent _animationStop; protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); - Control.Scroll.Scrolled += OnScrollStart; - Control.Scroll.PageScrolled += OnScrollStop; - } - - protected override void OnItemSelectedFromUI(object sender, SelectedItemChangedEventArgs e) - { - Element.Position = e.SelectedItemIndex; - Element.CurrentItem = e.SelectedItem; + if (e.NewElement != null) + { + Control.Scrolled += OnScrolled; + Control.Scroll.DragStart += OnDragStart; + Control.Scroll.DragStop += OnDragStop; + _animationStart = new ElmSharp.SmartEvent(Control.Scroll, Control.Scroll.RealHandle, "scroll,anim,start"); + _animationStart.On += OnScrollStart; + _animationStop = new ElmSharp.SmartEvent(Control.Scroll, Control.Scroll.RealHandle, "scroll,anim,stop"); + _animationStop.On += OnScrollStop; + } + UpdatePositionFromElement(false); + UpdateCurrentItemFromElement(false); } protected override void Dispose(bool disposing) @@ -39,39 +48,79 @@ { if (Element != null) { - Control.Scroll.Scrolled -= OnScrollStart; - Control.Scroll.PageScrolled -= OnScrollStop; + Control.Scrolled -= OnScrolled; + Control.Scroll.DragStart -= OnDragStart; + Control.Scroll.DragStop -= OnDragStop; + _animationStart.On -= OnScrollStart; + _animationStop.On -= OnScrollStop; } } base.Dispose(disposing); } + void OnDragStart(object sender, System.EventArgs e) + { + Element.SetIsDragging(true); + Element.IsScrolling = true; + } + + void OnDragStop(object sender, System.EventArgs e) + { + Element.SetIsDragging(false); + Element.IsScrolling = false; + } + void OnScrollStart(object sender, System.EventArgs e) { - if (!Element.IsDragging) - { - Element.SetIsDragging(true); - } + Element.IsScrolling = true; } void OnScrollStop(object sender, System.EventArgs e) { - if (Element.IsDragging) - { - Element.SetIsDragging(false); - } + var scrollerIndex = Control.LayoutManager.IsHorizontal ? Control.Scroll.HorizontalPageIndex : Control.Scroll.VerticalPageIndex; + Element.SetValueFromRenderer(CarouselView.PositionProperty, scrollerIndex); + Element.SetValueFromRenderer(CarouselView.CurrentItemProperty, Control.Adaptor[scrollerIndex]); + Control.Adaptor.RequestItemSelected(Control.Adaptor[scrollerIndex]); + Element.IsScrolling = false; } - void UpdatePosition(bool initialize) + void OnScrolled(object sender, ItemsViewScrolledEventArgs e) { - if (initialize) - { + if (!Element.IsScrolling) + Element.IsScrolling = true; + + if (Element.IsDragging) + if (Element.Position != e.CenterItemIndex) + Element.SetValueFromRenderer(CarouselView.PositionProperty, e.CenterItemIndex); + } + + void UpdateCurrentItemFromElement(bool isInitializing) + { + if (isInitializing) return; - } - if (Element.Position > -1 && Element.Position < Control.Adaptor.Count) + + if (Element.CurrentItem != null) + ScrollTo(Control.Adaptor.GetItemIndex(Element.CurrentItem)); + } + + void UpdatePositionFromElement(bool isInitializing) + { + if (isInitializing) + return; + + ScrollTo(Element.Position); + } + + void ScrollTo(int position) + { + if (Element.IsScrolling) + return; + + if (position > -1 && position < Control.Adaptor.Count) { - Control.Adaptor.RequestItemSelected(Element.Position); - Element.CurrentItem = Control.Adaptor[Element.Position]; + var scrollerIndex = Control.LayoutManager.IsHorizontal ? Control.Scroll.HorizontalPageIndex : Control.Scroll.VerticalPageIndex; + if (position != scrollerIndex) + Control.ScrollTo(position); } } diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs new file mode 100644 index 000000000..d33969535 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs @@ -0,0 +1,59 @@ +using System.Collections; + +namespace Xamarin.Forms.Platform.Tizen +{ + public class IndicatorViewRenderer : ViewRenderer + { + public IndicatorViewRenderer() + { + RegisterPropertyHandler(IndicatorView.CountProperty, UpdateItemsSource); + RegisterPropertyHandler(IndicatorView.PositionProperty, UpdatePosition); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + if (Control == null) + { + SetNativeControl(new Native.IndicatorView(Forms.NativeParent)); + } + if (e.NewElement != null) + { + Control.SelectedPosition += OnSelectedPosition; + } + if (e.OldElement != null) + { + Control.SelectedPosition -= OnSelectedPosition; + } + base.OnElementChanged(e); + } + + void OnSelectedPosition(object sender, SelectedPositionChangedEventArgs e) + { + Element.SetValueFromRenderer(IndicatorView.PositionProperty, (int)e.SelectedPosition); + } + + void UpdateItemsSource() + { + Control.ClearIndex(); + int count = 0; + if (Element.ItemsSource is ICollection collection) + { + count = collection.Count; + } + else + { + var enumerator = Element.ItemsSource.GetEnumerator(); + while (enumerator?.MoveNext() ?? false) + { + count++; + } + } + Control.AppendIndex(count); + } + + void UpdatePosition() + { + Control.UpdateSelectedIndex(Element.Position); + } + } +} diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs index 276bb159a..9313bb86e 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs @@ -1,4 +1,5 @@ -using System.Collections.Specialized; +using System; +using System.Collections.Specialized; using System.Linq; using Xamarin.Forms.Platform.Tizen.Native; @@ -26,6 +27,7 @@ namespace Xamarin.Forms.Platform.Tizen if (Control == null) { SetNativeControl(CreateNativeControl(Forms.NativeParent)); + Control.Scrolled += OnScrolled; } if (e.NewElement != null) { @@ -44,6 +46,7 @@ namespace Xamarin.Forms.Platform.Tizen { Element.ScrollToRequested -= OnScrollToRequest; ItemsLayout.PropertyChanged -= OnLayoutPropertyChanged; + Control.Scrolled -= OnScrolled; } if (_observableSource != null) { @@ -93,6 +96,11 @@ namespace Xamarin.Forms.Platform.Tizen { } + void OnScrolled(object sender, ItemsViewScrolledEventArgs e) + { + Element.SendScrolled(e); + } + void OnScrollToRequest(object sender, ScrollToRequestEventArgs e) { if (e.Mode == ScrollToMode.Position) diff --git a/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs b/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs index 4f1b0b9f5..30f972e90 100644 --- a/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs +++ b/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs @@ -104,6 +104,7 @@ namespace Xamarin.Forms.Platform.Tizen Registered.Register(typeof(SwipeView), () => new SwipeViewRenderer()); Registered.Register(typeof(RefreshView), () => new RefreshViewRenderer()); Registered.Register(typeof(MediaElement), () => new MediaElementRenderer()); + Registered.Register(typeof(IndicatorView), () => new IndicatorViewRenderer()); //ImageSourceHandlers Registered.Register(typeof(FileImageSource), () => new FileImageSourceHandler());