Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / FlexibleView / FlexibleView.cs
1 /*
2  * Copyright(c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19 using System.Collections.Generic;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// FlexibleView ItemClick Event Arguments.
26     /// </summary>
27     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
28     [EditorBrowsable(EditorBrowsableState.Never)]
29     public class FlexibleViewItemClickedEventArgs : EventArgs
30     {
31         /// <summary>
32         /// The clicked FlexibleViewViewHolder.
33         /// </summary>
34         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
35         [EditorBrowsable(EditorBrowsableState.Never)]
36         public FlexibleViewViewHolder ClickedView { get; set; }
37     }
38
39     /// <summary>
40     /// FlexibleView ItemTouch Event Arguments.
41     /// </summary>
42     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
43     [EditorBrowsable(EditorBrowsableState.Never)]
44     public class FlexibleViewItemTouchEventArgs : View.TouchEventArgs
45     {
46         /// <summary>
47         /// The touched FlexibleViewViewHolder.
48         /// </summary>
49         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public FlexibleViewViewHolder TouchedView { get; set; }
52     }
53
54     /// <summary>
55     /// A flexible view for providing a limited window into a large data set.
56     /// </summary>
57     /// <since_tizen> 6 </since_tizen>
58     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
59     [EditorBrowsable(EditorBrowsableState.Never)]
60     public partial class FlexibleView : Control
61     {
62         /// <summary>
63         /// Constant value: -1.
64         /// </summary>
65         /// <since_tizen> 6 </since_tizen>
66         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
67         [EditorBrowsable(EditorBrowsableState.Never)]
68         public static readonly int NO_POSITION = -1;
69         /// <summary>
70         /// Constant value: -1.
71         /// </summary>
72         /// <since_tizen> 6 </since_tizen>
73         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public static readonly int INVALID_TYPE = -1;
76
77         private FlexibleViewAdapter mAdapter;
78         private FlexibleViewLayoutManager mLayout;
79         private FlexibleViewRecycler mRecycler;
80         private RecycledViewPool mRecyclerPool;
81         private ChildHelper mChildHelper;
82
83         private PanGestureDetector mPanGestureDetector;
84
85         private int mFocusedItemIndex = NO_POSITION;
86
87         private AdapterHelper mAdapteHelper;
88
89         private ScrollBar mScrollBar = null;
90         private Timer mScrollBarShowTimer = null;
91
92         private EventHandler<FlexibleViewItemClickedEventArgs> clickEventHandlers;
93         private EventHandler<FlexibleViewItemTouchEventArgs> touchEventHandlers;
94         private EventHandler<NUI.StyleManager.StyleChangedEventArgs> styleChangedEventHandlers;
95
96         /// <summary>
97         /// Creates a FlexibleView instance.
98         /// </summary>
99         /// <since_tizen> 6 </since_tizen>
100         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
101         [EditorBrowsable(EditorBrowsableState.Never)]
102         public FlexibleView()
103         {
104             mRecyclerPool = new RecycledViewPool(this);
105
106             mRecycler = new FlexibleViewRecycler(this);
107             mRecycler.SetRecycledViewPool(mRecyclerPool);
108
109             mChildHelper = new ChildHelper(this);
110
111             mPanGestureDetector = new PanGestureDetector();
112             mPanGestureDetector.Attach(this);
113             mPanGestureDetector.Detected += OnPanGestureDetected;
114
115             mAdapteHelper = new AdapterHelper(this);
116
117             ClippingMode = ClippingModeType.ClipToBoundingBox;
118         }
119
120         /// <summary>
121         /// Item click event.
122         /// </summary>
123         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
124         [EditorBrowsable(EditorBrowsableState.Never)]
125         public event EventHandler<FlexibleViewItemClickedEventArgs> ItemClicked
126         {
127             add
128             {
129                 clickEventHandlers += value;
130             }
131
132             remove
133             {
134                 clickEventHandlers -= value;
135             }
136         }
137
138
139         /// <summary>
140         /// Item touch event.
141         /// </summary>
142         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public event EventHandler<FlexibleViewItemTouchEventArgs> ItemTouch
145         {
146             add
147             {
148                 touchEventHandlers += value;
149             }
150
151             remove
152             {
153                 touchEventHandlers -= value;
154             }
155         }
156
157         /// <summary>
158         /// Style changed, for example default font size.
159         /// </summary>
160         /// <since_tizen> 6 </since_tizen>
161         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
162         [EditorBrowsable(EditorBrowsableState.Never)]
163         public event EventHandler<NUI.StyleManager.StyleChangedEventArgs> StyleChanged
164         {
165             add
166             {
167                 styleChangedEventHandlers += value;
168             }
169
170             remove
171             {
172                 styleChangedEventHandlers -= value;
173             }
174         }
175
176         private new Extents padding;
177         /// <summary>
178         /// overwrite the Padding.
179         /// </summary>
180         /// <since_tizen> 6 </since_tizen>
181         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
182         [EditorBrowsable(EditorBrowsableState.Never)]
183         public new Extents Padding
184         {
185             get
186             {
187                 if (null == padding)
188                 {
189                     padding = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
190                     {
191                         padding.Start = start;
192                         padding.End = end;
193                         padding.Top = top;
194                         padding.Bottom = bottom;
195                     }, 0, 0, 0, 0);
196                 }
197
198                 return padding;
199             }
200             set
201             {
202                 if (null == padding)
203                 {
204                     padding = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
205                     {
206                         padding.Start = start;
207                         padding.End = end;
208                         padding.Top = top;
209                         padding.Bottom = bottom;
210                     }, 0, 0, 0, 0);
211                 }
212
213                 padding.CopyFrom(value);
214             }
215         }
216
217         /// <summary>
218         /// Gets or sets the focused item index(adapter position).
219         /// </summary>
220         /// <since_tizen> 6 </since_tizen>
221         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
222         [EditorBrowsable(EditorBrowsableState.Never)]
223         public int FocusedItemIndex
224         {
225             get
226             {
227                 return mFocusedItemIndex;
228             }
229             set
230             {
231                 if (value == mFocusedItemIndex)
232                 {
233                     return;
234                 }
235
236                 if (mAdapter == null)
237                 {
238                     return;
239                 }
240
241                 if (mLayout == null)
242                 {
243                     return;
244                 }
245
246                 FlexibleViewViewHolder nextFocusView = FindViewHolderForAdapterPosition(value);
247                 if (nextFocusView == null)
248                 {
249                     mLayout.ScrollToPosition(value);
250                 }
251                 else
252                 {
253                     mLayout.RequestChildRectangleOnScreen(this, nextFocusView, mRecycler, true);
254                     DispatchFocusChanged(value);
255                 }
256             }
257         }
258
259         /// <summary>
260         /// Set a new adapter to provide child views on demand.
261         /// </summary>
262         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
263         [EditorBrowsable(EditorBrowsableState.Never)]
264         public void SetAdapter(FlexibleViewAdapter adapter)
265         {
266             if (adapter == null)
267             {
268                 return;
269             }
270             mAdapter = adapter;
271
272             mAdapter.ItemEvent += OnItemEvent;
273         }
274
275         /// <summary>
276         /// Retrieves the previously set adapter or null if no adapter is set.
277         /// </summary>
278         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
279         [EditorBrowsable(EditorBrowsableState.Never)]
280         public FlexibleViewAdapter GetAdapter()
281         {
282             return mAdapter;
283         }
284
285         /// <summary>
286         /// Set the FlexibleViewLayoutManager that this FlexibleView will use.
287         /// </summary>
288         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
289         [EditorBrowsable(EditorBrowsableState.Never)]
290         public void SetLayoutManager(FlexibleViewLayoutManager layoutManager)
291         {
292             if (null == layoutManager) return;
293             mLayout = layoutManager;
294
295             mLayout.SetRecyclerView(this);
296
297             if (mLayout.CanScrollHorizontally())
298             {
299                 mPanGestureDetector.AddDirection(PanGestureDetector.DirectionHorizontal);
300             }
301             else if (mLayout.CanScrollVertically())
302             {
303                 mPanGestureDetector.AddDirection(PanGestureDetector.DirectionVertical);
304             }
305         }
306
307         /// <summary>
308         /// Return the FlexibleViewLayoutManager currently responsible for layout policy for this FlexibleView.
309         /// </summary>
310         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
311         [EditorBrowsable(EditorBrowsableState.Never)]
312         public FlexibleViewLayoutManager GetLayoutManager()
313         {
314             return mLayout;
315         }
316
317
318         /// <summary>
319         /// Convenience method to scroll to a certain position
320         /// </summary>
321         /// <param name="position">Adapter position</param>
322         /// <param name="offset">The distance (in pixels) between the start edge of the item view and start edge of the FlexibleView.</param>
323         /// <since_tizen> 6 </since_tizen>
324         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
325         [EditorBrowsable(EditorBrowsableState.Never)]
326         public void ScrollToPositionWithOffset(int position, int offset)
327         {
328             mLayout.ScrollToPositionWithOffset(position, offset);
329         }
330
331         /// <summary>
332         /// Move focus by direction.
333         /// </summary>
334         /// <param name="direction">Direction. Should be "Left", "Right", "Up" or "Down" </param>
335         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
336         [EditorBrowsable(EditorBrowsableState.Never)]
337         public void MoveFocus(FlexibleViewLayoutManager.Direction direction)
338         {
339             mLayout.MoveFocus(direction, mRecycler);
340         }
341
342         /// <summary>
343         /// Attach a scrollbar to this FlexibleView.
344         /// </summary>
345         /// <param name="scrollBar">ScrollBar</param>
346         /// <since_tizen> 6 </since_tizen>
347         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
348         [EditorBrowsable(EditorBrowsableState.Never)]
349         public void AttachScrollBar(ScrollBar scrollBar)
350         {
351             if (scrollBar == null)
352             {
353                 return;
354             }
355             mScrollBar = scrollBar;
356             Add(mScrollBar);
357         }
358
359         /// <summary>
360         /// Detach the scrollbar from this FlexibleView.
361         /// </summary>
362         /// <since_tizen> 6 </since_tizen>
363         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
364         [EditorBrowsable(EditorBrowsableState.Never)]
365         public void DetachScrollBar()
366         {
367             if (mScrollBar == null)
368             {
369                 return;
370             }
371             Remove(mScrollBar);
372             mScrollBar = null;
373         }
374
375         /// <summary>
376         /// Return the FlexibleViewViewHolder for the item in the given position of the data set as of the latest layout pass.
377         /// This method checks only the children of FlexibleViewRecyclerView. If the item at the given position is not laid out, it will not create a new one.
378         /// </summary>
379         /// <param name="position">The position of the item in the data set of the adapter</param>
380         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
381         [EditorBrowsable(EditorBrowsableState.Never)]
382         public FlexibleViewViewHolder FindViewHolderForLayoutPosition(int position)
383         {
384             int childCount = mChildHelper.GetChildCount();
385             for (int i = 0; i < childCount; i++)
386             {
387                 if (mChildHelper.GetChildAt(i) is FlexibleViewViewHolder holder)
388                 {
389                     if (holder.LayoutPosition == position)
390                     {
391                         return holder;
392                     }
393                 }
394             }
395
396             return null;
397         }
398
399         /// <summary>
400         /// Return the FlexibleViewViewHolder for the item in the given position of the data set.
401         /// This method checks only the children of FlexibleViewRecyclerView. If the item at the given position is not laid out, it will not create a new one.
402         /// </summary>
403         /// <param name="position">The position of the item in the data set of the adapter</param>
404         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
405         [EditorBrowsable(EditorBrowsableState.Never)]
406         public FlexibleViewViewHolder FindViewHolderForAdapterPosition(int position)
407         {
408             int childCount = mChildHelper.GetChildCount();
409             for (int i = 0; i < childCount; i++)
410             {
411                 if (mChildHelper.GetChildAt(i) is FlexibleViewViewHolder holder)
412                 {
413                     if (holder.AdapterPosition == position)
414                     {
415                         return holder;
416                     }
417                 }
418             }
419
420             return null;
421         }
422
423         /// <summary>
424         /// Return the recycler instance.
425         /// </summary>
426         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
427         [EditorBrowsable(EditorBrowsableState.Never)]
428         public FlexibleViewRecycler GetRecycler()
429         {
430             return mRecycler;
431         }
432
433         /// <summary>
434         /// you can override it to clean-up your own resources.
435         /// </summary>
436         /// <param name="type">DisposeTypes</param>
437         /// <since_tizen> 6 </since_tizen>
438         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
439         [EditorBrowsable(EditorBrowsableState.Never)]
440         protected override void Dispose(DisposeTypes type)
441         {
442             if (disposed)
443             {
444                 return;
445             }
446
447             if (type == DisposeTypes.Explicit)
448             {
449                 if (mLayout != null)
450                 {
451                     mLayout.StopScroll(false);
452                     mLayout.ClearRecyclerView();
453                     mLayout = null;
454                 }
455
456                 if (mAdapter != null)
457                 {
458                     mAdapter.ItemEvent -= OnItemEvent;
459                 }
460
461                 if (mPanGestureDetector != null)
462                 {
463                     mPanGestureDetector.Detected -= OnPanGestureDetected;
464                     mPanGestureDetector.Dispose();
465                     mPanGestureDetector = null;
466                 }
467
468                 if (mScrollBarShowTimer != null)
469                 {
470                     mScrollBarShowTimer.Tick -= OnShowTimerTick;
471                     mScrollBarShowTimer.Stop();
472                     mScrollBarShowTimer.Dispose();
473                     mScrollBarShowTimer = null;
474                 }
475
476                 if (mRecyclerPool != null)
477                 {
478                     mRecyclerPool.Clear();
479                     mRecyclerPool = null;
480                 }
481
482                 if (mChildHelper != null)
483                 {
484                     mChildHelper.Clear();
485                     mChildHelper.Dispose();
486                     mChildHelper = null;
487                 }
488             }
489             base.Dispose(type);
490         }
491
492         /// <summary>
493         /// you can override it to create your own default style.
494         /// </summary>
495         /// <since_tizen> 6 </since_tizen>
496         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
497         [EditorBrowsable(EditorBrowsableState.Never)]
498         protected override ViewStyle CreateViewStyle()
499         {
500             return null;
501         }
502
503         /// <summary>
504         /// you can override it to relayout elements.
505         /// </summary>
506         /// <since_tizen> 6 </since_tizen>
507         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
508         [EditorBrowsable(EditorBrowsableState.Never)]
509         public override void OnRelayout(Vector2 size, RelayoutContainer container)
510         {
511             if (mAdapter == null)
512             {
513                 return;
514             }
515
516             if (mLayout == null)
517             {
518                 return;
519             }
520
521             DispatchLayoutStep1();
522
523             mLayout.OnLayoutChildren(mRecycler);
524
525             RemoveAndRecycleScrapInt();
526         }
527
528         /// <summary>
529         /// you can override it to do something for style change.
530         /// </summary>
531         /// <since_tizen> 6 </since_tizen>
532         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
533         [EditorBrowsable(EditorBrowsableState.Never)]
534         public override void OnStyleChange(NUI.StyleManager styleManager, StyleChangeType change)
535         {
536             if (change == StyleChangeType.DefaultFontSizeChange)
537             {
538                 NUI.StyleManager.StyleChangedEventArgs args = new NUI.StyleManager.StyleChangedEventArgs();
539                 args.StyleManager = styleManager;
540                 args.StyleChange = change;
541
542                 styleChangedEventHandlers?.Invoke(this, args);
543
544                 RelayoutRequest();
545             }
546         }
547
548         private void DispatchLayoutStep1()
549         {
550             ProcessAdapterUpdates();
551             SaveOldPositions();
552             ClearOldPositions();
553         }
554
555         private void ProcessAdapterUpdates()
556         {
557             mAdapteHelper.PreProcess();
558         }
559
560         private void OffsetPositionRecordsForInsert(int positionStart, int itemCount)
561         {
562             int childCount = mChildHelper.GetChildCount();
563             for (int i = 0; i < childCount; i++)
564             {
565                 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
566                 if (holder != null && holder.AdapterPosition >= positionStart)
567                 {
568                     holder.OffsetPosition(itemCount, false);
569                 }
570             }
571
572             if (positionStart <= mFocusedItemIndex)
573             {
574                 mFocusedItemIndex += itemCount;
575             }
576         }
577
578         private void OffsetPositionRecordsForRemove(int positionStart, int itemCount, bool applyToPreLayout)
579         {
580             int positionEnd = positionStart + itemCount;
581             int childCount = mChildHelper.GetChildCount();
582             for (int i = 0; i < childCount; i++)
583             {
584                 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
585                 if (holder != null)
586                 {
587                     if (holder.AdapterPosition >= positionEnd)
588                     {
589                         holder.OffsetPosition(-itemCount, applyToPreLayout);
590                     }
591                     else if (holder.AdapterPosition >= positionStart)
592                     {
593                         holder.FlagRemovedAndOffsetPosition(positionStart - 1, -itemCount, applyToPreLayout);
594                     }
595                 }
596             }
597
598             if (positionEnd <= mFocusedItemIndex)
599             {
600                 mFocusedItemIndex -= itemCount;
601             }
602             else if (positionStart <= mFocusedItemIndex)
603             {
604                 mFocusedItemIndex = positionStart;
605                 if (mFocusedItemIndex >= mAdapter.GetItemCount())
606                 {
607                     mFocusedItemIndex = mAdapter.GetItemCount() - 1;
608                 }
609             }
610         }
611
612         private void SaveOldPositions()
613         {
614             int childCount = mChildHelper.GetChildCount();
615             for (int i = 0; i < childCount; i++)
616             {
617                 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
618                 holder.SaveOldPosition();
619             }
620         }
621
622         private void ClearOldPositions()
623         {
624             int childCount = mChildHelper.GetChildCount();
625             for (int i = 0; i < childCount; i++)
626             {
627                 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
628                 holder.ClearOldPosition();
629             }
630         }
631
632         private void RemoveAndRecycleScrapInt()
633         {
634             int scrapCount = mRecycler.GetScrapCount();
635             for (int i = 0; i < scrapCount; i++)
636             {
637                 FlexibleViewViewHolder scrap = mRecycler.GetScrapViewAt(i);
638                 mChildHelper.RemoveView(scrap);
639                 mRecycler.RecycleView(scrap);
640             }
641             mRecycler.Clear();
642         }
643
644         private void ShowScrollBar(uint millisecond = 700, bool flagAni = false)
645         {
646             if (mScrollBar == null || mLayout == null)
647             {
648                 return;
649             }
650
651             float extent = mLayout.ComputeScrollExtent();
652             float range = mLayout.ComputeScrollRange();
653             if (range == 0)
654             {
655                 return;
656             }
657             float offset = mLayout.ComputeScrollOffset();
658
659             float size = mScrollBar.Direction == ScrollBar.DirectionType.Vertical ? mScrollBar.SizeHeight : mScrollBar.SizeWidth;
660             float thickness = mScrollBar.Direction == ScrollBar.DirectionType.Vertical ? mScrollBar.SizeWidth : mScrollBar.SizeHeight;
661             float length = (float)Math.Round(size * extent / range);
662
663             // avoid the tiny thumb
664             float minLength = thickness * 2;
665             if (length < minLength)
666             {
667                 length = minLength;
668             }
669             // avoid the too-big thumb
670             if (offset > range - extent)
671             {
672                 offset = range - extent;
673             }
674             if (offset < 0)
675             {
676                 offset = 0;
677             }
678             if (mScrollBar.Direction == ScrollBar.DirectionType.Vertical)
679             {
680                 mScrollBar.Style.Thumb.Size = new Size(thickness, length);
681             }
682             else
683             {
684                 mScrollBar.Style.Thumb.Size = new Size(length, thickness);
685             }
686             mScrollBar.MinValue = 0;
687             mScrollBar.MaxValue = (int)(range - extent);
688             mScrollBar.SetCurrentValue((int)offset, flagAni);
689             mScrollBar.Show();
690             if (mScrollBarShowTimer == null)
691             {
692                 mScrollBarShowTimer = new Timer(millisecond);
693                 mScrollBarShowTimer.Tick += OnShowTimerTick;
694             }
695             else
696             {
697                 mScrollBarShowTimer.Interval = millisecond;
698             }
699             mScrollBarShowTimer.Start();
700         }
701
702         private bool OnShowTimerTick(object source, EventArgs e)
703         {
704             if (mScrollBar != null)
705             {
706                 mScrollBar.Hide();
707             }
708
709             return false;
710         }
711
712         internal void DispatchFocusChanged(int nextFocusPosition)
713         {
714             mAdapter.OnFocusChange(this, mFocusedItemIndex, nextFocusPosition);
715
716             mFocusedItemIndex = nextFocusPosition;
717
718             ShowScrollBar();
719         }
720
721         private void DispatchChildAttached(FlexibleViewViewHolder holder)
722         {
723             if (mAdapter != null && holder != null)
724             {
725                 mAdapter.OnViewAttachedToWindow(holder);
726             }
727         }
728
729         private void DispatchChildDetached(FlexibleViewViewHolder holder)
730         {
731             if (mAdapter != null && holder != null)
732             {
733                 mAdapter.OnViewDetachedFromWindow(holder);
734             }
735         }
736
737         private void DispatchChildDestroyed(FlexibleViewViewHolder holder)
738         {
739             if (mAdapter != null && holder != null)
740             {
741                 mAdapter.OnDestroyViewHolder(holder);
742             }
743         }
744
745         private void DispatchItemClicked(FlexibleViewViewHolder clickedHolder)
746         {
747             FlexibleViewItemClickedEventArgs args = new FlexibleViewItemClickedEventArgs();
748             args.ClickedView = clickedHolder;
749             OnClickEvent(this, args);
750         }
751
752         private void DispatchItemTouched(FlexibleViewViewHolder touchedHolder, Touch touchEvent)
753         {
754             FlexibleViewItemTouchEventArgs args = new FlexibleViewItemTouchEventArgs();
755             args.TouchedView = touchedHolder;
756             args.Touch = touchEvent;
757             OnTouchEvent(this, args);
758         }
759
760         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
761         {
762             if (e.PanGesture.State == Gesture.StateType.Started)
763             {
764                 mLayout.StopScroll(true);
765             }
766             else if (e.PanGesture.State == Gesture.StateType.Continuing)
767             {
768                 if (mLayout.CanScrollVertically())
769                 {
770                     mLayout.ScrollVerticallyBy(e.PanGesture.Displacement.Y, mRecycler, true);
771                 }
772                 else if (mLayout.CanScrollHorizontally())
773                 {
774                     mLayout.ScrollHorizontallyBy(e.PanGesture.Displacement.X, mRecycler, true);
775                 }
776
777                 ShowScrollBar();
778             }
779             else if (e.PanGesture.State == Gesture.StateType.Finished)
780             {
781                 if (mLayout.CanScrollVertically())
782                 {
783                     mLayout.ScrollVerticallyBy(e.PanGesture.Velocity.Y * 600, mRecycler, false);
784                 }
785                 else if (mLayout.CanScrollHorizontally())
786                 {
787                     mLayout.ScrollHorizontallyBy(e.PanGesture.Velocity.X * 600, mRecycler, false);
788                 }
789                 ShowScrollBar(1200, true);
790             }
791         }
792
793         private void OnItemEvent(object sender, FlexibleViewAdapter.ItemEventArgs e)
794         {
795             switch (e.EventType)
796             {
797                 case FlexibleViewAdapter.ItemEventType.Insert:
798                     mAdapteHelper.OnItemRangeInserted(e.param[0], e.param[1]);
799                     ShowScrollBar();
800                     break;
801                 case FlexibleViewAdapter.ItemEventType.Remove:
802                     mAdapteHelper.OnItemRangeRemoved(e.param[0], e.param[1]);
803                     ShowScrollBar();
804                     break;
805                 case FlexibleViewAdapter.ItemEventType.Move:
806                     break;
807                 case FlexibleViewAdapter.ItemEventType.Change:
808                     break;
809                 default:
810                     return;
811             }
812             RelayoutRequest();
813         }
814
815
816         private void OnClickEvent(object sender, FlexibleViewItemClickedEventArgs e)
817         {
818             clickEventHandlers?.Invoke(sender, e);
819         }
820
821         private void OnTouchEvent(object sender, FlexibleViewItemTouchEventArgs e)
822         {
823             touchEventHandlers?.Invoke(sender, e);
824         }
825
826         internal void LayoutManagerRelayoutRequest()
827         {
828             RelayoutRequest();
829         }
830
831         internal ChildHelper GetChildHelper()
832         {
833             return mChildHelper;
834         }
835     }
836 }