[NUI] fix layout issue when flexibleview animation is canceled (#1111)
authorhuayongxu <49056704+huayongxu@users.noreply.github.com>
Wed, 6 Nov 2019 11:40:40 +0000 (19:40 +0800)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Wed, 6 Nov 2019 11:40:40 +0000 (20:40 +0900)
* fix layout issue when flexibleview animation is canceled

* add some comments for protected functions

src/Tizen.NUI.Components/Controls/FlexibleView/FlexibleView.cs
src/Tizen.NUI.Components/Controls/FlexibleView/GridLayoutManager.cs
src/Tizen.NUI.Components/Controls/FlexibleView/LinearLayoutManager.cs

index 4fb7a9f..6cb1b5b 100755 (executable)
@@ -398,7 +398,7 @@ namespace Tizen.NUI.Components
             {
                 if (mLayout != null)
                 {
-                    mLayout.StopScroll();
+                    mLayout.StopScroll(false);
                 }
 
                 if (mAdapter != null)
@@ -618,6 +618,10 @@ namespace Tizen.NUI.Components
             {
                 offset = range - extent;
             }
+            if (offset < 0)
+            {
+                offset = 0;
+            }
             if (mScrollBar.Direction == ScrollBar.DirectionType.Vertical)
             {
                 mScrollBar.ThumbSize = new Size(thickness, length);
@@ -704,7 +708,7 @@ namespace Tizen.NUI.Components
         {
             if (e.PanGesture.State == Gesture.StateType.Started)
             {
-                mLayout.StopScroll();
+                mLayout.StopScroll(true);
             }
             else if (e.PanGesture.State == Gesture.StateType.Continuing)
             {
@@ -1127,6 +1131,8 @@ namespace Tizen.NUI.Components
                 Down
             }
 
+            private readonly int SCROLL_ANIMATION_DURATION = 500;
+
             private FlexibleView mFlexibleView;
             private ChildHelper mChildHelper;
 
@@ -1450,20 +1456,10 @@ namespace Tizen.NUI.Components
                     return;
                 }
 
-                if (mScrollAni == null)
-                {
-                    mScrollAni = new Animation();
-                    mScrollAni.Finished += OnScrollAnimationFinished;
-                }
-                else if (mScrollAni.State == Animation.States.Playing)
+                if (dx == 0)
                 {
-                    //StopScroll();
-                    mScrollAni.Stop(Animation.EndActions.StopFinal);
+                    return;
                 }
-                mScrollAni.Duration = 500;
-                mScrollAni.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
-
-                mScrollAni.Clear();
 
                 int childCount = mChildHelper.GetChildCount();
                 if (immediate == true)
@@ -1476,9 +1472,52 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
+                    if (mScrollAni == null)
+                    {
+                        mScrollAni = new Animation();
+                        mScrollAni.Duration = SCROLL_ANIMATION_DURATION;
+                        mScrollAni.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
+                    }
+
+                    // avoid out of boundary of flexibleview. delta value might be used for shadow.
+                    // this must be done before animation clear.
+                    if (childCount > 0)
+                    {
+                        ViewHolder vh = mChildHelper.GetChildAt(0);
+                        if (vh.LayoutPosition == 0)
+                        {
+                            if ((int)(vh.Left + dx) > 0)
+                            {
+                                dx = 0 - vh.Left;
+                            }
+                        }
+
+                        vh = mChildHelper.GetChildAt(childCount - 1);
+                        if (vh.LayoutPosition == ItemCount - 1)
+                        {
+                            if ((int)(vh.Right + dx) < (int)Width + PaddingRight)
+                            {
+                                dx = Width + PaddingRight - vh.Right;
+                            }
+                        }
+                    }
+
+                    // save position before animation clear.
+                    float[] childrenPositon = new float[childCount];
                     for (int i = childCount - 1; i >= 0; i--)
                     {
                         ViewHolder v = mChildHelper.GetChildAt(i);
+                        childrenPositon[i] = v.ItemView.PositionX;
+                    }
+
+                    mScrollAni.Clear();
+                    mScrollAni.Finished += OnScrollAnimationFinished;
+
+                    for (int i = childCount - 1; i >= 0; i--)
+                    {
+                        ViewHolder v = mChildHelper.GetChildAt(i);
+                        // set position again because position might be changed after animation clear.
+                        v.ItemView.PositionX = childrenPositon[i];
                         mScrollAni.AnimateTo(v.ItemView, "PositionX", v.ItemView.PositionX + dx);
                     }
                     mScrollAni.Play();
@@ -1500,20 +1539,10 @@ namespace Tizen.NUI.Components
                     return;
                 }
 
-                if (mScrollAni == null)
-                {
-                    mScrollAni = new Animation();
-                    mScrollAni.Finished += OnScrollAnimationFinished;
-                }
-                else if (mScrollAni.State == Animation.States.Playing)
+                if (dy == 0)
                 {
-                    //StopScroll();
-                    mScrollAni.Stop(Animation.EndActions.StopFinal);
+                    return;
                 }
-                mScrollAni.Duration = 500;
-                mScrollAni.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
-
-                mScrollAni.Clear();
 
                 int childCount = mChildHelper.GetChildCount();
                 if (immediate == true)
@@ -1526,9 +1555,52 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
+                    if (mScrollAni == null)
+                    {
+                        mScrollAni = new Animation();
+                        mScrollAni.Duration = SCROLL_ANIMATION_DURATION;
+                        mScrollAni.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
+                    }
+
+                    // avoid out of boundary of flexibleview. delta value might be used for shadow.
+                    // this must be done before animation clear.
+                    if (childCount > 0)
+                    {
+                        ViewHolder vh = mChildHelper.GetChildAt(0);
+                        if (vh.LayoutPosition == 0)
+                        {
+                            if ((int)(vh.Top + dy) > 0)
+                            {
+                                dy = 0 - vh.Top;
+                            }
+                        }
+
+                        vh = mChildHelper.GetChildAt(childCount - 1);
+                        if (vh.LayoutPosition == ItemCount - 1)
+                        {
+                            if ((int)(vh.Bottom + dy) < (int)Height + PaddingBottom)
+                            {
+                                dy = Height + PaddingBottom - vh.Bottom;
+                            }
+                        }
+                    }
+
+                    // save position before animation clear.
+                    float[] childPositon = new float[childCount];
                     for (int i = childCount - 1; i >= 0; i--)
                     {
                         ViewHolder v = mChildHelper.GetChildAt(i);
+                        childPositon[i] = v.ItemView.PositionY;
+                    }
+
+                    mScrollAni.Clear();
+                    mScrollAni.Finished += OnScrollAnimationFinished;
+
+                    for (int i = childCount - 1; i >= 0; i--)
+                    {
+                        ViewHolder v = mChildHelper.GetChildAt(i);
+                        // set position again because position might be changed after animation clear.
+                        v.ItemView.PositionY = childPositon[i];
                         mScrollAni.AnimateTo(v.ItemView, "PositionY", v.ItemView.PositionY + dy);
                     }
                     mScrollAni.Play();
@@ -1738,6 +1810,28 @@ namespace Tizen.NUI.Components
             [EditorBrowsable(EditorBrowsableState.Never)]
             protected abstract int GetNextPosition(int position, FlexibleView.LayoutManager.Direction direction);
 
+            /// <summary>
+            /// Retrieves the first visible item view.
+            /// </summary>
+            /// <since_tizen> 6 </since_tizen>
+            /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            protected virtual ViewHolder FindFirstVisibleItemView()
+            {
+                return null;
+            }
+
+            /// <summary>
+            /// Retrieves the last visible item view.
+            /// </summary>
+            /// <since_tizen> 6 </since_tizen>
+            /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            protected virtual ViewHolder FindLastVisibleItemView()
+            {
+                return null;
+            }
+
             internal virtual ViewHolder OnFocusSearchFailed(FlexibleView.ViewHolder focused, LayoutManager.Direction direction, Recycler recycler)
             {
                 return null;
@@ -1749,13 +1843,17 @@ namespace Tizen.NUI.Components
                 mChildHelper = recyclerView.mChildHelper;
             }
 
-            internal void StopScroll()
+            internal void StopScroll(bool doSomethingAfterAnimationStopped)
             {
                 if (mScrollAni != null && mScrollAni.State == Animation.States.Playing)
                 {
-                    mScrollAni.Stop(Animation.EndActions.StopFinal);
-                    mScrollAni.Clear();
-                    OnScrollAnimationFinished(mScrollAni, null);
+                    mScrollAni.Finished -= OnScrollAnimationFinished;
+                    mScrollAni.Stop();
+
+                    if (doSomethingAfterAnimationStopped)
+                    {
+                        OnScrollAnimationFinished(mScrollAni, null);
+                    }
                 }
             }
 
@@ -1807,7 +1905,49 @@ namespace Tizen.NUI.Components
 
             private void OnScrollAnimationFinished(object sender, EventArgs e)
             {
-                RecycleChildrenInt(mFlexibleView.mRecycler);
+                foreach (ViewHolder holder in mPendingRecycleViews)
+                {
+                    holder.PendingRecycle = false;
+                }
+                mPendingRecycleViews.Clear();
+
+                int start = NO_POSITION;
+                ViewHolder firstItemView = FindFirstVisibleItemView();
+                if (firstItemView != null)
+                    start = firstItemView.LayoutPosition;
+                else
+                    start = 0;
+
+                int itemCount = ChildCount;
+
+                int end = NO_POSITION;
+                ViewHolder lastItemView = FindLastVisibleItemView();
+                if (lastItemView != null)
+                    end = lastItemView.LayoutPosition;
+                else
+                    end = itemCount - 1;
+
+                List<ViewHolder> removedViewList = new List<ViewHolder>();
+                for (int i = 0; i < itemCount; i++)
+                {
+                    ViewHolder v = GetChildAt(i);
+
+                    //if item view of holder is visible, it should not be recycled.
+                    if (v.LayoutPosition >= start && v.LayoutPosition <= end)
+                        continue;
+
+                    removedViewList.Add(v);
+                }
+
+                for (int i = 0; i < removedViewList.Count; i++)
+                {
+                    ViewHolder v = removedViewList[i];
+                    v.PendingRecycle = false;
+                    mFlexibleView.mRecycler.RecycleView(v);
+                    mChildHelper.RemoveView(v);
+                }
+
+                // relayout
             }
 
             private void AddViewInternal(ViewHolder holder, int index, bool disappearing)
index 3100256..e746be5 100755 (executable)
@@ -46,7 +46,7 @@ namespace Tizen.NUI.Components
         internal override void EnsureAnchorReady(FlexibleView.Recycler recycler, AnchorInfo anchorInfo, int itemDirection)
         {
             bool layingOutInPrimaryDirection = (itemDirection == LayoutState.ITEM_DIRECTION_TAIL);
-            int span = anchorInfo.Position;
+            int span = anchorInfo.Position % mSpanCount;
             if (layingOutInPrimaryDirection)
             {
                 // choose span 0
@@ -64,7 +64,7 @@ namespace Tizen.NUI.Components
                 int bestSpan = span;
                 while (pos < indexLimit)
                 {
-                    int next = (pos + 1);
+                    int next = (pos + 1) % mSpanCount;
                     if (next > bestSpan)
                     {
                         pos += 1;
index 9315aeb..0323ad9 100755 (executable)
@@ -451,6 +451,78 @@ namespace Tizen.NUI.Components
             return NO_POSITION;
         }
 
+        /// <summary>
+        /// Retrieves the first visible item view.
+        /// </summary>
+        /// <since_tizen> 6 </since_tizen>
+        /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override FlexibleView.ViewHolder FindFirstVisibleItemView()
+        {
+            int childCount = ChildCount;
+            if (mShouldReverseLayout == false)
+            {
+                for (int i = 0; i < childCount; i++)
+                {
+                    FlexibleView.ViewHolder child = GetChildAt(i);
+                    int end = (int)mOrientationHelper.GetViewHolderEnd(child);
+                    if (end >= 0 && end < (int)mOrientationHelper.GetEnd())
+                    {
+                        return child;
+                    }
+                }
+            }
+            else
+            {
+                for (int i = childCount - 1; i >= 0; i--)
+                {
+                    FlexibleView.ViewHolder child = GetChildAt(i);
+                    int end = (int)mOrientationHelper.GetViewHolderEnd(child);
+                    if (end >= 0 && end < (int)mOrientationHelper.GetEnd())
+                    {
+                        return child;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Retrieves the last visible item view.
+        /// </summary>
+        /// <since_tizen> 6 </since_tizen>
+        /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override FlexibleView.ViewHolder FindLastVisibleItemView()
+        {
+            int childCount = ChildCount;
+            if (mShouldReverseLayout == false)
+            {
+                for (int i = childCount - 1; i >= 0; i--)
+                {
+                    FlexibleView.ViewHolder child = GetChildAt(i);
+                    int start = (int)mOrientationHelper.GetViewHolderStart(child);
+                    if (start > 0 && start < (int)mOrientationHelper.GetEnd())
+                    {
+                        return child;
+                    }
+                }
+            }
+            else
+            {
+                for (int i = 0; i < childCount; i++)
+                {
+                    FlexibleView.ViewHolder child = GetChildAt(i);
+                    int start = (int)mOrientationHelper.GetViewHolderStart(child);
+                    if (start > 0 && start < (int)mOrientationHelper.GetEnd())
+                    {
+                        return child;
+                    }
+                }
+            }
+            return null;
+        }
+
         internal virtual void LayoutChunk(FlexibleView.Recycler recycler,
             LayoutState layoutState, LayoutChunkResult result)
         {
@@ -601,10 +673,11 @@ namespace Tizen.NUI.Components
                 return false;
             }
 
-            FlexibleView.ViewHolder anchorChild = FindFirstCompleteVisibleItemView();
+            FlexibleView.ViewHolder anchorChild = FindFirstVisibleItemView();
             if (anchorChild == null)
             {
-                return false;
+                Log.Error("flexibleview", $"exception occurs when updating anchor information!");
+                anchorChild = GetChildAt(0);
             }
             anchorInfo.Position = anchorChild.LayoutPosition;
             anchorInfo.Coordinate = mOrientationHelper.GetViewHolderStart(anchorChild);
@@ -946,34 +1019,6 @@ namespace Tizen.NUI.Components
             mLayoutState.Extra = mOrientationHelper.GetStartAfterPadding();
         }
 
-        private FlexibleView.ViewHolder FindFirstVisibleItemView()
-        {
-            int childCount = ChildCount;
-            if (mShouldReverseLayout == false)
-            {
-                for (int i = 0; i < childCount; i++)
-                {
-                    FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderEnd(child) > 0)
-                    {
-                        return child;
-                    }
-                }
-            }
-            else
-            {
-                for (int i = childCount - 1; i >= 0; i--)
-                {
-                    FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderEnd(child) > 0)
-                    {
-                        return child;
-                    }
-                }
-            }
-            return null;
-        }
-
         private FlexibleView.ViewHolder FindFirstCompleteVisibleItemView()
         {
             int childCount = ChildCount;
@@ -982,7 +1027,8 @@ namespace Tizen.NUI.Components
                 for (int i = 0; i < childCount; i++)
                 {
                     FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderStart(child) > 0)
+                    int start = (int)mOrientationHelper.GetViewHolderStart(child);
+                    if (start > 0 && start < (int)mOrientationHelper.GetEnd())
                     {
                         return child;
                     }
@@ -993,35 +1039,8 @@ namespace Tizen.NUI.Components
                 for (int i = childCount - 1; i >= 0; i--)
                 {
                     FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderStart(child) > 0)
-                    {
-                        return child;
-                    }
-                }
-            }
-            return null;
-        }
-
-        private FlexibleView.ViewHolder FindLastVisibleItemView()
-        {
-            int childCount = ChildCount;
-            if (mShouldReverseLayout == false)
-            {
-                for (int i = childCount - 1; i >= 0; i--)
-                {
-                    FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderStart(child) < (int)mOrientationHelper.GetEnd())
-                    {
-                        return child;
-                    }
-                }
-            }
-            else
-            {
-                for (int i = 0; i < childCount; i++)
-                {
-                    FlexibleView.ViewHolder child = GetChildAt(i);
-                    if ((int)mOrientationHelper.GetViewHolderStart(child) < (int)mOrientationHelper.GetEnd())
+                    int start = (int)mOrientationHelper.GetViewHolderStart(child);
+                    if (start > 0 && start < (int)mOrientationHelper.GetEnd())
                     {
                         return child;
                     }