add shadow effect for scrollablebase.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / ScrollableBase.cs
index 679e676..50f177d 100755 (executable)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2019 Samsung Electronics Co., Ltd.
+/* Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 using System;
 using Tizen.NUI.BaseComponents;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Tizen.NUI.Accessibility;
+
 namespace Tizen.NUI.Components
 {
     /// <summary>
-    /// [Draft] This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
+    /// ScrollEventArgs is a class to record scroll event arguments which will sent to user.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class ScrollEventArgs : EventArgs
+    {
+        private Position position;
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="position">Current scroll position</param>
+        /// <since_tizen> 8 </since_tizen>
+        public ScrollEventArgs(Position position)
+        {
+            this.position = position;
+        }
+
+        /// <summary>
+        /// Current position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position Position
+        {
+            get
+            {
+                return position;
+            }
+        }
+    }
+
+    /// <summary>
+    /// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
     /// </summary>
-    /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 8 </since_tizen>
     public class ScrollableBase : Control
     {
-           static bool LayoutDebugScrollableBase = false; // Debug flag
+        static bool LayoutDebugScrollableBase = false; // Debug flag
         private Direction mScrollingDirection = Direction.Vertical;
         private bool mScrollEnabled = true;
+        private int mScrollDuration = 125;
         private int mPageWidth = 0;
+        private float mPageFlickThreshold = 0.4f;
+        private float mScrollingEventThreshold = 0.00001f;
 
         private class ScrollableBaseCustomLayout : LayoutGroup
         {
@@ -46,26 +83,26 @@ namespace Tizen.NUI.Components
                 ScrollableBase scrollableBase = this.Owner as ScrollableBase;
                 if (scrollableBase)
                 {
-                   scrollingDirection = scrollableBase.ScrollingDirection;
+                    scrollingDirection = scrollableBase.ScrollingDirection;
                 }
 
                 // measure child, should be a single scrolling child
-                foreach( LayoutItem childLayout in LayoutChildren )
+                foreach (LayoutItem childLayout in LayoutChildren)
                 {
                     if (childLayout != null)
                     {
                         // Get size of child
                         // Use an Unspecified MeasureSpecification mode so scrolling child is not restricted to it's parents size in Height (for vertical scrolling)
                         // or Width for horizontal scrolling
-                        MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification( heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+                        MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
 
                         if (scrollingDirection == Direction.Vertical)
                         {
-                            MeasureChild( childLayout, widthMeasureSpec, unrestrictedMeasureSpec );  // Height unrestricted by parent
+                            MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0));  // Height unrestricted by parent
                         }
                         else
                         {
-                            MeasureChild( childLayout, unrestrictedMeasureSpec, heightMeasureSpec );  // Width unrestricted by parent
+                            MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));  // Width unrestricted by parent
                         }
 
                         float childWidth = childLayout.MeasuredWidth.Size.AsDecimal();
@@ -86,7 +123,7 @@ namespace Tizen.NUI.Components
                         {
                             childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
                         }
-                        if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
+                        if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
                         {
                             childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
                         }
@@ -94,30 +131,31 @@ namespace Tizen.NUI.Components
                 }
 
 
-                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
-                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
                 totalWidth = widthSizeAndState.Size.AsDecimal();
                 totalHeight = heightSizeAndState.Size.AsDecimal();
 
                 // Ensure layout respects it's given minimum size
-                totalWidth = Math.Max( totalWidth, SuggestedMinimumWidth.AsDecimal() );
-                totalHeight = Math.Max( totalHeight, SuggestedMinimumHeight.AsDecimal() );
+                totalWidth = Math.Max(totalWidth, SuggestedMinimumWidth.AsDecimal());
+                totalHeight = Math.Max(totalHeight, SuggestedMinimumHeight.AsDecimal());
 
                 widthSizeAndState.State = childWidthState;
                 heightSizeAndState.State = childHeightState;
 
-                SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(totalWidth), widthMeasureSpec, childWidthState ),
-                                       ResolveSizeAndState( new LayoutLength(totalHeight), heightMeasureSpec, childHeightState ) );
+                SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, childWidthState),
+                                       ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childHeightState));
 
                 // Size of ScrollableBase is changed. Change Page width too.
                 scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
+                scrollableBase.OnScrollingChildRelayout(null , null);
             }
 
             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
             {
-                foreach( LayoutItem childLayout in LayoutChildren )
+                foreach (LayoutItem childLayout in LayoutChildren)
                 {
-                    if( childLayout != null )
+                    if (childLayout != null)
                     {
                         LayoutLength childWidth = childLayout.MeasuredWidth.Size;
                         LayoutLength childHeight = childLayout.MeasuredHeight.Size;
@@ -129,7 +167,7 @@ namespace Tizen.NUI.Components
                         LayoutLength childLeft = new LayoutLength(childPosition.X + childMargin.Start + padding.Start);
                         LayoutLength childTop = new LayoutLength(childPosition.Y + childMargin.Top + padding.Top);
 
-                        childLayout.Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
+                        childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
                     }
                 }
             }
@@ -138,57 +176,27 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// The direction axis to scroll.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public enum Direction
         {
             /// <summary>
             /// Horizontal axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Horizontal,
 
             /// <summary>
             /// Vertical axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Vertical
         }
 
         /// <summary>
-        /// [Draft] Configurable speed threshold that register the gestures as a flick.
-        /// If the flick speed less than the threshold then will not be considered a flick.
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public float FlickThreshold { get; set; } = 0.2f;
-
-        /// <summary>
-        /// [Draft] Configurable duration modifer for the flick animation.
-        /// Determines the speed of the scroll, large value results in a longer flick animation. Range (0.1 - 1.0)
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public float FlickAnimationSpeed { get; set; } = 0.4f;
-
-        /// <summary>
-        /// [Draft] Configurable modifer for the distance to be scrolled when flicked detected.
-        /// It a ratio of the ScrollableBase's length. (not child's length).
-        /// First value is the ratio of the distance to scroll with the weakest flick.
-        /// Second value is the ratio of the distance to scroll with the strongest flick.
-        /// Second > First.
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Vector2 FlickDistanceMultiplierRange { get; set; } = new Vector2(0.6f, 1.8f);
-
-        /// <summary>
-        /// [Draft] Scrolling direction mode.
+        /// Scrolling direction mode.
         /// Default is Vertical scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Direction ScrollingDirection
         {
             get
@@ -197,20 +205,26 @@ namespace Tizen.NUI.Components
             }
             set
             {
-                if(value != mScrollingDirection)
+                if (value != mScrollingDirection)
                 {
                     mScrollingDirection = value;
-                    mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ? PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
-                    mPanGestureDetector.AddDirection(value == Direction.Horizontal ? PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
+                    mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ?
+                        PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
+                    mPanGestureDetector.AddDirection(value == Direction.Horizontal ?
+                        PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
+
+                    ContentContainer.WidthSpecification = mScrollingDirection == Direction.Vertical ?
+                        LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent;
+                    ContentContainer.HeightSpecification = mScrollingDirection == Direction.Vertical ?
+                        LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent;
                 }
             }
         }
 
         /// <summary>
-        /// [Draft] Enable or disable scrolling.
+        /// Enable or disable scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool ScrollEnabled
         {
             get
@@ -222,202 +236,528 @@ namespace Tizen.NUI.Components
                 if (value != mScrollEnabled)
                 {
                     mScrollEnabled = value;
-                    if(mScrollEnabled)
+                    if (mScrollEnabled)
                     {
                         mPanGestureDetector.Detected += OnPanGestureDetected;
-                        mTapGestureDetector.Detected += OnTapGestureDetected;
                     }
                     else
                     {
                         mPanGestureDetector.Detected -= OnPanGestureDetected;
-                        mTapGestureDetector.Detected -= OnTapGestureDetected;
                     }
                 }
             }
         }
 
         /// <summary>
-        /// [Draft] Pages mode, enables moving to the next or return to current page depending on pan displacement.
+        /// Pages mode, enables moving to the next or return to current page depending on pan displacement.
         /// Default is false.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool SnapToPage { set; get; } = false;
 
         /// <summary>
-        /// [Draft] Get current page.
-        /// Working propery with SnapToPage property.
+        /// Get current page.
+        /// Working property with SnapToPage property.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public int CurrentPage { get; private set; } = 0;
 
         /// <summary>
-        /// [Draft] Duration of scroll animation.
+        /// Duration of scroll animation.
+        /// Default value is 125ms.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public int ScrollDuration { set; get; } = 125;
+        /// <since_tizen> 8 </since_tizen>
+        public int ScrollDuration
+        {
+            set
+            {
+                mScrollDuration = value >= 0 ? value : mScrollDuration;
+            }
+            get
+            {
+                return mScrollDuration;
+            }
+        }
 
         /// <summary>
-        /// ScrollEventArgs is a class to record scroll event arguments which will sent to user.
+        /// Scroll Available area.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public class ScrollEventArgs : EventArgs
-        {
-        }
+        /// <since_tizen> 8 </since_tizen>
+        public Vector2 ScrollAvailableArea { set; get; }
 
         /// <summary>
         /// An event emitted when user starts dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public event EventHandler<ScrollEventArgs> ScrollDragStartEvent;
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<ScrollEventArgs> ScrollDragStarted;
 
         /// <summary>
         /// An event emitted when user stops dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public event EventHandler<ScrollEventArgs> ScrollDragEndEvent;
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<ScrollEventArgs> ScrollDragEnded;
 
 
         /// <summary>
         /// An event emitted when the scrolling slide animation starts, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public event EventHandler<ScrollEventArgs> ScrollAnimationStartEvent;
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<ScrollEventArgs> ScrollAnimationStarted;
 
         /// <summary>
         /// An event emitted when the scrolling slide animation ends, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<ScrollEventArgs> ScrollAnimationEnded;
+
+
+        /// <summary>
+        /// An event emitted when scrolling, user can subscribe or unsubscribe to this event handler.<br />
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<ScrollEventArgs> Scrolling;
+
+
+        /// <summary>
+        /// Scrollbar for ScrollableBase.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public ScrollbarBase Scrollbar
+        {
+            get
+            {
+                return scrollBar;
+            }
+            set
+            {
+                if (scrollBar)
+                {
+                    scrollBar.Unparent();
+                }
+                scrollBar = value;
+
+                if (scrollBar != null)
+                {
+                    scrollBar.Name = "ScrollBar";
+                    base.Add(scrollBar);
+
+                    if (hideScrollbar)
+                    {
+                        scrollBar.Hide();
+                    }
+                    else
+                    {
+                        scrollBar.Show();
+                    }
+
+                    SetScrollbar();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Always hide Scrollbar.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public bool HideScrollbar
+        {
+            get
+            {
+                return hideScrollbar;
+            }
+            set
+            {
+                hideScrollbar = value;
+
+                if (scrollBar)
+                {
+                    if (value)
+                    {
+                        scrollBar.Hide();
+                    }
+                    else
+                    {
+                        scrollBar.Show();
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Container which has content of ScrollableBase.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public View ContentContainer { get; private set; }
+
+        /// <summary>
+        /// Set the layout on this View. Replaces any existing Layout.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public new LayoutItem Layout
+        {
+            get
+            {
+                return ContentContainer.Layout;
+            }
+            set
+            {
+                ContentContainer.Layout = value;
+                if (ContentContainer.Layout != null)
+                {
+                    ContentContainer.Layout.SetPositionByLayout = false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// List of children of Container.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public new List<View> Children
+        {
+            get
+            {
+                return ContentContainer.Children;
+            }
+        }
+
+        /// <summary>
+        /// Deceleration rate of scrolling by finger.
+        /// Rate should be bigger than 0 and smaller than 1.
+        /// Default value is 0.998f;
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public float DecelerationRate
+        {
+            get
+            {
+                return decelerationRate;
+            }
+            set
+            {
+                decelerationRate = (value < 1 && value > 0) ? value : decelerationRate;
+                logValueOfDeceleration = (float)Math.Log(value);
+            }
+        }
+
+        /// <summary>
+        /// Threashold not to go infinit at the end of scrolling animation.
+        /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public event EventHandler<ScrollEventArgs> ScrollAnimationEndEvent;
+        public float DecelerationThreshold { get; set; } = 0.1f;
 
+        /// <summary>
+        /// Scrolling event will be thrown when this amount of scroll positino is changed.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float ScrollingEventThreshold
+        {
+            get
+            {
+                return mScrollingEventThreshold;
+            }
+            set
+            {
+                if (mScrollingEventThreshold != value && value > 0)
+                {
+                    ContentContainer.RemovePropertyNotification(propertyNotification);
+                    propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(value));
+                    propertyNotification.Notified += OnPropertyChanged;
+                    mScrollingEventThreshold = value;
+                }
+            }
+        }
 
 
-        private Animation scrollAnimation;
+        /// <summary>
+        /// Page will be changed when velocity of panning is over threshold.
+        /// The unit of threshold is pixel per milisec.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public float PageFlickThreshold
+        {
+            get
+            {
+                return mPageFlickThreshold;
+            }
+            set
+            {
+                mPageFlickThreshold = value >= 0f ? value : mPageFlickThreshold;
+            }
+        }
+
+        /// <summary>
+        /// Alphafunction for scroll animation.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AlphaFunction ScrollAlphaFunction { get; set; } = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+
+        private bool hideScrollbar = true;
         private float maxScrollDistance;
         private float childTargetPosition = 0.0f;
         private PanGestureDetector mPanGestureDetector;
-        private TapGestureDetector mTapGestureDetector;
-        private View mScrollingChild;
-        private float multiplier =1.0f;
+        private View mInterruptTouchingChild;
+        private ScrollbarBase scrollBar;
         private bool scrolling = false;
         private float ratioOfScreenWidthToCompleteScroll = 0.5f;
         private float totalDisplacementForPan = 0.0f;
-
-        // If false then can only flick pages when the current animation/scroll as ended.
-        private bool flickWhenAnimating = false;
+        private Size previousContainerSize = new Size();
+        private Size previousSize = new Size();
+        private PropertyNotification propertyNotification;
+        private float noticeAnimationEndBeforePosition = 0.0f;
+        private bool readyToNotice = false;
 
         /// <summary>
-        /// [Draft] Constructor
+        /// Notice before animation is finished.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
         [EditorBrowsable(EditorBrowsableState.Never)]
+        // Let's consider more whether this needs to be set as protected.
+        public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
+
+        // Let's consider more whether this needs to be set as protected.
+        private float finalTargetPosition;
+
+        private Animation scrollAnimation;
+        // Declare user alpha function delegate
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate float UserAlphaFunctionDelegate(float progress);
+        private UserAlphaFunctionDelegate customScrollAlphaFunction;
+        private float velocityOfLastPan = 0.0f;
+        private float panAnimationDuration = 0.0f;
+        private float panAnimationDelta = 0.0f;
+        private float logValueOfDeceleration = 0.0f;
+        private float decelerationRate = 0.0f;
+
+        private View mVerticalTopShadowView;
+        private View mVerticalBottomShadowView;
+        private const int mVerticalShadowScaleHeightLimit = 64 * 3;
+        private const int mVerticalShadowAnimationDuration = 300;
+        private Animation mVerticalShadowAnimation;
+        private bool isVerticalShadowShown = false;
+        private float mStartShowShadowDisplacement;
+
+        /// <summary>
+        /// Default Constructor
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
         public ScrollableBase() : base()
         {
+            DecelerationRate = 0.998f;
+
+            base.Layout = new ScrollableBaseCustomLayout();
             mPanGestureDetector = new PanGestureDetector();
             mPanGestureDetector.Attach(this);
             mPanGestureDetector.AddDirection(PanGestureDetector.DirectionVertical);
             mPanGestureDetector.Detected += OnPanGestureDetected;
 
-            mTapGestureDetector = new TapGestureDetector();
-            mTapGestureDetector.Attach(this);
-            mTapGestureDetector.Detected += OnTapGestureDetected;
+            ClippingMode = ClippingModeType.ClipChildren;
 
-            ClippingMode = ClippingModeType.ClipToBoundingBox;
+            //Default Scrolling child
+            ContentContainer = new View()
+            {
+                WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
+                HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
+                Layout = new AbsoluteLayout() { SetPositionByLayout = false },
+            };
+            ContentContainer.Relayout += OnScrollingChildRelayout;
+            propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
+            propertyNotification.Notified += OnPropertyChanged;
+            base.Add(ContentContainer);
+
+            //Interrupt touching when panning is started
+            mInterruptTouchingChild = new View()
+            {
+                Size = new Size(Window.Instance.WindowSize),
+                BackgroundColor = Color.Transparent,
+            };
+            mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
+            Scrollbar = new Scrollbar();
+
+            //Show vertical shadow when panning down (or up) on the scroll top (or end).
+            mVerticalTopShadowView = new View
+            {
+                BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_top.png"),
+                Opacity = 1.0f,
+                SizeHeight = 0.0f,
+                PositionUsesPivotPoint = true,
+                ParentOrigin = NUI.ParentOrigin.TopCenter,
+                PivotPoint = NUI.PivotPoint.TopCenter,
+            };
+            mVerticalBottomShadowView = new View
+            {
+                BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_bottom.png"),
+                Opacity = 1.0f,
+                SizeHeight = 0.0f,
+                PositionUsesPivotPoint = true,
+                ParentOrigin = NUI.ParentOrigin.BottomCenter,
+                PivotPoint = NUI.PivotPoint.BottomCenter,
+            };
+
+            AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "ScrollableBase");
+        }
 
-            mScrollingChild = new View();
+        private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
+        {
+            if (args.Touch.GetState(0) == PointStateType.Down)
+            {
+                if (scrolling && !SnapToPage)
+                {
+                    StopScroll();
+                }
+            }
+            return true;
+        }
 
-            Layout = new ScrollableBaseCustomLayout();
+        private void OnPropertyChanged(object source, PropertyNotification.NotifyEventArgs args)
+        {
+            OnScroll();
         }
 
         /// <summary>
         /// Called after a child has been added to the owning view.
         /// </summary>
         /// <param name="view">The child which has been added.</param>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void OnChildAdd(View view)
+        /// <since_tizen> 8 </since_tizen>
+        public override void Add(View view)
         {
-            mScrollingChild = view;
-            {
-            if (Children.Count > 1)
-                Log.Error("ScrollableBase", $"Only 1 child should be added to ScrollableBase.");
-            }
+            ContentContainer.Add(view);
         }
 
         /// <summary>
         /// Called after a child has been removed from the owning view.
         /// </summary>
         /// <param name="view">The child which has been removed.</param>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void OnChildRemove(View view)
+        /// <since_tizen> 8 </since_tizen>
+        public override void Remove(View view)
         {
-            mScrollingChild = new View();
+            if (SnapToPage && CurrentPage == Children.IndexOf(view) && CurrentPage == Children.Count - 1)
+            {
+                // Target View is current page and also last child.
+                // CurrentPage should be changed to previous page.
+                CurrentPage = Math.Max(0, CurrentPage - 1);
+                ScrollToIndex(CurrentPage);
+            }
+
+            ContentContainer.Remove(view);
+        }
+
+        private void OnScrollingChildRelayout(object source, EventArgs args)
+        {
+            // Size is changed. Calculate maxScrollDistance.
+            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height
+                || previousSize.Width != Size.Width || previousSize.Height != Size.Height;
+
+            if (isSizeChanged)
+            {
+                maxScrollDistance = CalculateMaximumScrollDistance();
+                SetScrollbar();
+            }
+
+            previousContainerSize = ContentContainer.Size;
+            previousSize = Size;
         }
 
+        /// <summary>
+        /// The composition of a Scrollbar can vary depending on how you use ScrollableBase.
+        /// Set the composition that will go into the ScrollableBase according to your ScrollableBase.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void SetScrollbar()
+        {
+            if (Scrollbar)
+            {
+                bool isHorizontal = ScrollingDirection == Direction.Horizontal;
+                float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
+                float viewportLength = isHorizontal ? Size.Width : Size.Height;
+                float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
+                Scrollbar.Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
+            }
+        }
 
         /// <summary>
         /// Scrolls to the item at the specified index.
         /// </summary>
         /// <param name="index">Index of item.</param>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public void ScrollToIndex(int index)
         {
-            if(mScrollingChild.ChildCount-1 < index || index < 0)
+            if (ContentContainer.ChildCount - 1 < index || index < 0)
             {
                 return;
             }
 
-            if(SnapToPage)
+            if (SnapToPage)
             {
                 CurrentPage = index;
             }
 
-            maxScrollDistance = CalculateMaximumScrollDistance();
-
-            float targetPosition = Math.Min(ScrollingDirection == Direction.Vertical ? mScrollingChild.Children[index].Position.Y : mScrollingChild.Children[index].Position.X, maxScrollDistance);
+            float targetPosition = Math.Min(ScrollingDirection == Direction.Vertical ? Children[index].Position.Y : Children[index].Position.X, maxScrollDistance);
             AnimateChildTo(ScrollDuration, -targetPosition);
         }
 
-        private void OnScrollDragStart()
+        private void OnScrollDragStarted()
+        {
+            ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
+            ScrollDragStarted?.Invoke(this, eventArgs);
+        }
+
+        private void OnScrollDragEnded()
         {
-            ScrollEventArgs eventArgs = new ScrollEventArgs();
-            ScrollDragStartEvent?.Invoke(this, eventArgs);
+            ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
+            ScrollDragEnded?.Invoke(this, eventArgs);
         }
 
-        private void OnScrollDragEnd()
+        private void OnScrollAnimationStarted()
         {
-            ScrollEventArgs eventArgs = new ScrollEventArgs();
-            ScrollDragEndEvent?.Invoke(this, eventArgs);
+            ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
+            ScrollAnimationStarted?.Invoke(this, eventArgs);
         }
 
-        private void OnScrollAnimationStart()
+        private void OnScrollAnimationEnded()
         {
-            ScrollEventArgs eventArgs = new ScrollEventArgs();
-            ScrollAnimationStartEvent?.Invoke(this, eventArgs);
+            scrolling = false;
+            base.Remove(mInterruptTouchingChild);
+
+            ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
+            ScrollAnimationEnded?.Invoke(this, eventArgs);
+        }
+
+        private void OnScroll()
+        {
+            ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
+            Scrolling?.Invoke(this, eventArgs);
+
+            bool isHorizontal = ScrollingDirection == Direction.Horizontal;
+            float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
+            float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
+
+            scrollBar?.Update(contentLength, Math.Abs(currentPosition));
+            CheckPreReachedTargetPosition();
         }
 
-        private void OnScrollAnimationEnd()
+        private void CheckPreReachedTargetPosition()
         {
-            ScrollEventArgs eventArgs = new ScrollEventArgs();
-            ScrollAnimationEndEvent?.Invoke(this, eventArgs);
+            // Check whether we reached pre-reached target position
+            if (readyToNotice &&
+                ContentContainer.CurrentPosition.Y <= finalTargetPosition + NoticeAnimationEndBeforePosition &&
+                ContentContainer.CurrentPosition.Y >= finalTargetPosition - NoticeAnimationEndBeforePosition)
+            {
+                //Notice first
+                readyToNotice = false;
+                OnPreReachedTargetPosition(finalTargetPosition);
+            }
+        }
+
+        /// <summary>
+        /// This helps developer who wants to know before scroll is reaching target position.
+        /// </summary>
+        /// <param name="targetPosition">Index of item.</param>
+        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void OnPreReachedTargetPosition(float targetPosition)
+        {
+
         }
 
         private void StopScroll()
@@ -428,28 +768,16 @@ namespace Tizen.NUI.Components
                 {
                     Debug.WriteLineIf(LayoutDebugScrollableBase, "StopScroll Animation Playing");
                     scrollAnimation.Stop(Animation.EndActions.Cancel);
-                    OnScrollAnimationEnd();
+                    OnScrollAnimationEnded();
                 }
                 scrollAnimation.Clear();
             }
         }
 
-        // static constructor registers the control type
-        static ScrollableBase()
-        {
-            // ViewRegistry registers control type with DALi type registry
-            // also uses introspection to find any properties that need to be registered with type registry
-            CustomViewRegistry.Instance.Register(CreateInstance, typeof(ScrollableBase));
-        }
-
-        internal static CustomView CreateInstance()
-        {
-            return new ScrollableBase();
-        }
-
         private void AnimateChildTo(int duration, float axisPosition)
         {
             Debug.WriteLineIf(LayoutDebugScrollableBase, "AnimationTo Animation Duration:" + duration + " Destination:" + axisPosition);
+            finalTargetPosition = axisPosition;
 
             StopScroll(); // Will replace previous animation so will stop existing one.
 
@@ -460,60 +788,88 @@ namespace Tizen.NUI.Components
             }
 
             scrollAnimation.Duration = duration;
-            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSine);
-            scrollAnimation.AnimateTo(mScrollingChild, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition);
+            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
+            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition, ScrollAlphaFunction);
             scrolling = true;
-            OnScrollAnimationStart();
+            OnScrollAnimationStarted();
             scrollAnimation.Play();
         }
 
+        /// <summary>
+        /// Scroll to specific position with or without animation.
+        /// </summary>
+        /// <param name="position">Destination.</param>
+        /// <param name="animate">Scroll with or without animation</param>
+        /// <since_tizen> 8 </since_tizen>
+        public void ScrollTo(float position, bool animate)
+        {
+            float currentPositionX = ContentContainer.CurrentPosition.X != 0 ? ContentContainer.CurrentPosition.X : ContentContainer.Position.X;
+            float currentPositionY = ContentContainer.CurrentPosition.Y != 0 ? ContentContainer.CurrentPosition.Y : ContentContainer.Position.Y;
+            float delta = ScrollingDirection == Direction.Horizontal ? currentPositionX : currentPositionY;
+            // The argument position is the new pan position. So the new position of ScrollableBase becomes (-position).
+            // To move ScrollableBase's position to (-position), it moves by (-position - currentPosition).
+            delta = -position - delta;
+
+            ScrollBy(delta, animate);
+        }
+
+        private float BoundScrollPosition(float targetPosition)
+        {
+            if (ScrollAvailableArea != null)
+            {
+                float minScrollPosition = ScrollAvailableArea.X;
+                float maxScrollPosition = ScrollAvailableArea.Y;
+
+                targetPosition = Math.Min(-minScrollPosition, targetPosition);
+                targetPosition = Math.Max(-maxScrollPosition, targetPosition);
+            }
+            else
+            {
+                targetPosition = Math.Min(0, targetPosition);
+                targetPosition = Math.Max(-maxScrollDistance, targetPosition);
+            }
+
+            return targetPosition;
+        }
+
         private void ScrollBy(float displacement, bool animate)
         {
-            if (GetChildCount() == 0 || displacement == 0 || maxScrollDistance < 0)
+            if (GetChildCount() == 0 || maxScrollDistance < 0)
             {
                 return;
             }
 
-            float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? mScrollingChild.PositionX: mScrollingChild.PositionY;
+            float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? ContentContainer.PositionX : ContentContainer.PositionY;
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy childCurrentPosition:" + childCurrentPosition +
                                                    " displacement:" + displacement,
-                                                   " maxScrollDistance:" + maxScrollDistance );
+                                                   " maxScrollDistance:" + maxScrollDistance);
 
             childTargetPosition = childCurrentPosition + displacement; // child current position + gesture displacement
-            childTargetPosition = Math.Min(0,childTargetPosition);
-            childTargetPosition = Math.Max(-maxScrollDistance,childTargetPosition);
 
-            Debug.WriteLineIf( LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
+
+            Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
 
             if (animate)
             {
                 // Calculate scroll animaton duration
-                float scrollDistance = 0.0f;
-                if (childCurrentPosition < childTargetPosition)
-                {
-                    scrollDistance = Math.Abs(childCurrentPosition + childTargetPosition);
-                }
-                else
-                {
-                    scrollDistance = Math.Abs(childCurrentPosition - childTargetPosition);
-                }
-
-                int duration = (int)((320*FlickAnimationSpeed) + (scrollDistance * FlickAnimationSpeed));
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "Scroll Animation Duration:" + duration + " Distance:" + scrollDistance);
+                float scrollDistance = Math.Abs(displacement);
+                readyToNotice = true;
 
-                AnimateChildTo(duration, childTargetPosition);
+                AnimateChildTo(ScrollDuration, BoundScrollPosition(AdjustTargetPositionOfScrollAnimation(BoundScrollPosition(childTargetPosition))));
             }
             else
             {
+                finalTargetPosition = BoundScrollPosition(childTargetPosition);
+
                 // Set position of scrolling child without an animation
                 if (ScrollingDirection == Direction.Horizontal)
                 {
-                    mScrollingChild.PositionX = childTargetPosition;
+                    ContentContainer.PositionX = finalTargetPosition;
                 }
                 else
                 {
-                    mScrollingChild.PositionY = childTargetPosition;
+                    ContentContainer.PositionY = finalTargetPosition;
                 }
             }
         }
@@ -522,7 +878,6 @@ namespace Tizen.NUI.Components
         /// you can override it to clean-up your own resources.
         /// </summary>
         /// <param name="type">DisposeTypes</param>
-        /// <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 void Dispose(DisposeTypes type)
@@ -534,6 +889,7 @@ namespace Tizen.NUI.Components
 
             if (type == DisposeTypes.Explicit)
             {
+                StopVerticalShadowAnimation();
                 StopScroll();
 
                 if (mPanGestureDetector != null)
@@ -543,83 +899,58 @@ namespace Tizen.NUI.Components
                     mPanGestureDetector = null;
                 }
 
-                if (mTapGestureDetector != null)
-                {
-                    mTapGestureDetector.Detected -= OnTapGestureDetected;
-                    mTapGestureDetector.Dispose();
-                    mTapGestureDetector = null;
-                }
+                propertyNotification.Dispose();
             }
             base.Dispose(type);
         }
 
-        private float CalculateDisplacementFromVelocity(float axisVelocity)
-        {
-            // Map: flick speed of range (2.0 - 6.0) to flick multiplier of range (0.7 - 1.6)
-            float speedMinimum = FlickThreshold;
-            float speedMaximum = FlickThreshold + 6.0f;
-            float multiplierMinimum = FlickDistanceMultiplierRange.X;
-            float multiplierMaximum = FlickDistanceMultiplierRange.Y;
-
-            float flickDisplacement = 0.0f;
-
-            float speed = Math.Min(4.0f,Math.Abs(axisVelocity));
-
-            Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollableBase Candidate Flick speed:" + speed);
-
-            if (speed > FlickThreshold)
-            {
-                // Flick length is the length of the ScrollableBase.
-                float flickLength = (ScrollingDirection == Direction.Horizontal) ?CurrentSize.Width:CurrentSize.Height;
-
-                // Calculate multiplier by mapping speed between the multiplier minimum and maximum.
-                multiplier =( (speed - speedMinimum) / ( (speedMaximum - speedMinimum) * (multiplierMaximum - multiplierMinimum) ) )+ multiplierMinimum;
-
-                // flick displacement is the product of the flick length and multiplier
-                flickDisplacement = ((flickLength * multiplier) * speed) / axisVelocity;  // *speed and /velocity to perserve sign.
-
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "Calculated FlickDisplacement[" + flickDisplacement +"] from speed[" + speed + "] multiplier:"
-                                                        + multiplier);
-            }
-            return flickDisplacement;
-        }
-
         private float CalculateMaximumScrollDistance()
         {
-            int scrollingChildLength = 0;
-            int scrollerLength = 0;
+            float scrollingChildLength = 0;
+            float scrollerLength = 0;
             if (ScrollingDirection == Direction.Horizontal)
             {
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Horizontal");
 
-                scrollingChildLength = (int)mScrollingChild.Layout.MeasuredWidth.Size.AsRoundedValue();
-                scrollerLength = CurrentSize.Width;
+                scrollingChildLength = ContentContainer.Size.Width;
+                scrollerLength = Size.Width;
             }
             else
             {
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Vertical");
-                scrollingChildLength = (int)mScrollingChild.Layout.MeasuredHeight.Size.AsRoundedValue();
-                scrollerLength = CurrentSize.Height;
+                scrollingChildLength = ContentContainer.Size.Height;
+                scrollerLength = Size.Height;
             }
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy maxScrollDistance:" + (scrollingChildLength - scrollerLength) +
                                                    " parent length:" + scrollerLength +
                                                    " scrolling child length:" + scrollingChildLength);
 
-            return Math.Max(scrollingChildLength - scrollerLength,0);
+            return Math.Max(scrollingChildLength - scrollerLength, 0);
         }
 
-        private void PageSnap()
+        private void PageSnap(float velocity)
         {
             Debug.WriteLineIf(LayoutDebugScrollableBase, "PageSnap with pan candidate totalDisplacement:" + totalDisplacementForPan +
-                                                                " currentPage[" + CurrentPage + "]" );
+                                                                " currentPage[" + CurrentPage + "]");
 
             //Increment current page if total displacement enough to warrant a page change.
             if (Math.Abs(totalDisplacementForPan) > (mPageWidth * ratioOfScreenWidthToCompleteScroll))
             {
                 if (totalDisplacementForPan < 0)
                 {
-                    CurrentPage = Math.Min(Math.Max(mScrollingChild.Children.Count - 1,0), ++CurrentPage);
+                    CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), ++CurrentPage);
+                }
+                else
+                {
+                    CurrentPage = Math.Max(0, --CurrentPage);
+                }
+            }
+            else if (Math.Abs(velocity) > PageFlickThreshold)
+            {
+                if (velocity < 0)
+                {
+                    CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), ++CurrentPage);
                 }
                 else
                 {
@@ -628,107 +959,406 @@ namespace Tizen.NUI.Components
             }
 
             // Animate to new page or reposition to current page
-            int destinationX = -(CurrentPage * mPageWidth);
-            Debug.WriteLineIf(LayoutDebugScrollableBase, "Snapping to page[" + CurrentPage + "] to:"+ destinationX + " from:" + mScrollingChild.PositionX);
+            float destinationX = -(Children[CurrentPage].Position.X + Children[CurrentPage].CurrentSize.Width / 2 - CurrentSize.Width / 2); // set to middle of current page
+            Debug.WriteLineIf(LayoutDebugScrollableBase, "Snapping to page[" + CurrentPage + "] to:" + destinationX + " from:" + ContentContainer.PositionX);
             AnimateChildTo(ScrollDuration, destinationX);
         }
 
-        private void Flick(float flickDisplacement)
-        {
-          if (SnapToPage)
-          {
-              if ( ( flickWhenAnimating && scrolling == true) || ( scrolling == false) )
-              {
-                  if(flickDisplacement < 0)
-                  {
-                      CurrentPage = Math.Min(Math.Max(mScrollingChild.Children.Count - 1,0), CurrentPage + 1);
-                      Debug.WriteLineIf(LayoutDebugScrollableBase, "Snap - to page:" + CurrentPage);
-                  }
-                  else
-                  {
-                      CurrentPage = Math.Max(0, CurrentPage - 1);
-                      Debug.WriteLineIf(LayoutDebugScrollableBase, "Snap + to page:" + CurrentPage);
-                  }
-                  float targetPosition = -(CurrentPage* mPageWidth); // page size
-                  Debug.WriteLineIf(LayoutDebugScrollableBase, "Snapping to :" + targetPosition);
-                  AnimateChildTo(ScrollDuration,targetPosition);
-              }
-          }
-          else
-          {
-              ScrollBy(flickDisplacement, true); // Animate flickDisplacement.
-          }
+        private void AttachShadowView()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            base.Add(mVerticalTopShadowView);
+            base.Add(mVerticalBottomShadowView);
+
+            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalTopShadowView.Opacity = 1.0f;
+
+            mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalBottomShadowView.Opacity = 1.0f;
+
+            // at the beginning, height of vertical shadow is 0, so it is invisible.
+            isVerticalShadowShown = false;
+        }
+
+        private void DragVerticalShadow(float displacement)
+        {
+            if ((int)displacement > 0) // downwards
+            {
+                // check if reaching at the top.
+                if ((int)finalTargetPosition != 0)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    mStartShowShadowDisplacement = displacement;
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = displacement < mStartShowShadowDisplacement ? 0 : displacement - mStartShowShadowDisplacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
+                mVerticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                mVerticalTopShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else if ((int)displacement < 0) // upwards
+            {
+                // check if reaching at the bottom.
+                if (-(int)finalTargetPosition != (int)maxScrollDistance)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    mStartShowShadowDisplacement = displacement;
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = mStartShowShadowDisplacement < displacement ? 0 : mStartShowShadowDisplacement - displacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
+                mVerticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                mVerticalBottomShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else
+            {
+                // if total displacement is 0, shadow would become invisible.
+                isVerticalShadowShown = false;
+            }
+        }
+
+        private void PlayVerticalShadowAnimation()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            if (mVerticalShadowAnimation == null)
+            {
+                mVerticalShadowAnimation = new Animation(mVerticalShadowAnimationDuration);
+                mVerticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
+            }
+
+            View targetView = totalDisplacementForPan < 0 ? mVerticalBottomShadowView : mVerticalTopShadowView;
+            mVerticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
+            mVerticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
+            mVerticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
+            mVerticalShadowAnimation.Play();
+        }
+
+        private void StopVerticalShadowAnimation()
+        {
+            if (mVerticalShadowAnimation == null || mVerticalShadowAnimation.State != Animation.States.Playing)
+                return;
+
+            Debug.WriteLineIf(LayoutDebugScrollableBase, "gesture finished. Stop Vertical Shadow Animation Playing.");
+            mVerticalShadowAnimation.Stop(Animation.EndActions.Cancel);
+            OnVerticalShadowAnimationFinished(null, null);
+            mVerticalShadowAnimation.Clear();
+        }
+
+        private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
+        {
+            base.Remove(mVerticalTopShadowView);
+            base.Remove(mVerticalBottomShadowView);
+
+            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+
+            // after animation finished, height & opacity of vertical shadow both are 0, so it is invisible.
+            isVerticalShadowShown = false;
         }
 
         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
         {
-            if (e.PanGesture.State == Gesture.StateType.Started)
+            OnPanGesture(e.PanGesture);
+        }
+
+        private void OnPanGesture(PanGesture panGesture)
+        {
+            if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
+            {
+                return;
+            }
+
+            if (panGesture.State == Gesture.StateType.Started)
             {
+                readyToNotice = false;
+                base.Add(mInterruptTouchingChild);
+                AttachShadowView();
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Gesture Start");
                 if (scrolling && !SnapToPage)
                 {
                     StopScroll();
                 }
-                maxScrollDistance = CalculateMaximumScrollDistance();
                 totalDisplacementForPan = 0.0f;
-                OnScrollDragStart();
+                OnScrollDragStarted();
             }
-            else if (e.PanGesture.State == Gesture.StateType.Continuing)
+            else if (panGesture.State == Gesture.StateType.Continuing)
             {
                 if (ScrollingDirection == Direction.Horizontal)
                 {
-                    ScrollBy(e.PanGesture.Displacement.X, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.X;
+                    ScrollBy(panGesture.Displacement.X, false);
+                    totalDisplacementForPan += panGesture.Displacement.X;
                 }
                 else
                 {
-                    ScrollBy(e.PanGesture.Displacement.Y, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.Y;
+                    // if vertical shadow is shown, does not scroll.
+                    if (!isVerticalShadowShown)
+                    {
+                        ScrollBy(panGesture.Displacement.Y, false);
+                    }
+                    totalDisplacementForPan += panGesture.Displacement.Y;
+                    DragVerticalShadow(totalDisplacementForPan);
                 }
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "OnPanGestureDetected Continue totalDisplacementForPan:" + totalDisplacementForPan);
-
             }
-            else if (e.PanGesture.State == Gesture.StateType.Finished)
+            else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
             {
-                float axisVelocity = (ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y;
-                float flickDisplacement = CalculateDisplacementFromVelocity(axisVelocity);
+                PlayVerticalShadowAnimation();
+                OnScrollDragEnded();
+                StopScroll(); // Will replace previous animation so will stop existing one.
 
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "FlickDisplacement:" + flickDisplacement + "TotalDisplacementForPan:" + totalDisplacementForPan);
-                OnScrollDragEnd();
+                if (scrollAnimation == null)
+                {
+                    scrollAnimation = new Animation();
+                    scrollAnimation.Finished += ScrollAnimationFinished;
+                }
 
-                if (flickDisplacement > 0 | flickDisplacement < 0)// Flick detected
+                float panVelocity = (ScrollingDirection == Direction.Horizontal) ? panGesture.Velocity.X : panGesture.Velocity.Y;
+
+                if (SnapToPage)
                 {
-                    Flick(flickDisplacement);
+                    PageSnap(panVelocity);
                 }
                 else
                 {
-                    // End of panning gesture but was not a flick
-                    if (SnapToPage)
+                    if (panVelocity == 0)
                     {
-                        PageSnap();
+                        float currentScrollPosition = (ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+                        scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+                        scrollAnimation.Duration = 0;
+                        scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", currentScrollPosition);
+                        scrollAnimation.Play();
+                    }
+                    else
+                    {
+                        Decelerating(panVelocity, scrollAnimation);
                     }
                 }
+
                 totalDisplacementForPan = 0;
+                scrolling = true;
+                readyToNotice = true;
+                OnScrollAnimationStarted();
             }
         }
 
-        private new void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
+        internal override bool OnAccessibilityPan(PanGesture gestures)
         {
-            if (e.TapGesture.Type == Gesture.GestureType.Tap)
+            if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
             {
-                // Stop scrolling if tap detected (press then relase).
-                // Unless in Pages mode, do not want a page change to stop part way.
-                if(scrolling && !SnapToPage)
+                return false;
+            }
+
+            OnPanGesture(gestures);
+            return true;
+        }
+
+        private float CustomScrollAlphaFunction(float progress)
+        {
+            if (panAnimationDelta == 0)
+            {
+                return 1.0f;
+            }
+            else
+            {
+                // Parameter "progress" is normalized value. We need to multiply target duration to calculate distance.
+                // Can get real distance using equation of deceleration (check Decelerating function)
+                // After get real distance, normalize it
+                float realDuration = progress * panAnimationDuration;
+                float realDistance = velocityOfLastPan * ((float)Math.Pow(decelerationRate, realDuration) - 1) / logValueOfDeceleration;
+                float result = Math.Min(realDistance / Math.Abs(panAnimationDelta), 1.0f);
+                return result;
+            }
+        }
+
+        /// <summary>
+        /// you can override it to custom your decelerating
+        /// </summary>
+        /// <param name="velocity">Velocity of current pan.</param>
+        /// <param name="animation">Scroll animation.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Decelerating(float velocity, Animation animation)
+        {
+            // Decelerating using deceleration equation ===========
+            //
+            // V   : velocity (pixel per milisecond)
+            // V0  : initial velocity
+            // d   : deceleration rate,
+            // t   : time
+            // X   : final position after decelerating
+            // log : natural logarithm
+            //
+            // V(t) = V0 * d pow t;
+            // X(t) = V0 * (d pow t - 1) / log d;  <-- Integrate the velocity function
+            // X(∞) = V0 * d / (1 - d); <-- Result using inifit T can be final position because T is tending to infinity.
+            //
+            // Because of final T is tending to inifity, we should use threshold value to finish.
+            // Final T = log(-threshold * log d / |V0| ) / log d;
+
+            velocityOfLastPan = Math.Abs(velocity);
+
+            float currentScrollPosition = -(ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+            panAnimationDelta = (velocityOfLastPan * decelerationRate) / (1 - decelerationRate);
+            panAnimationDelta = velocity > 0 ? -panAnimationDelta : panAnimationDelta;
+
+            float destination = -(panAnimationDelta + currentScrollPosition);
+            float adjustDestination = AdjustTargetPositionOfScrollAnimation(destination);
+            float maxPosition = ScrollAvailableArea != null ? ScrollAvailableArea.Y : maxScrollDistance;
+            float minPosition = ScrollAvailableArea != null ? ScrollAvailableArea.X : 0;
+
+            if (destination < -maxPosition || destination > minPosition)
+            {
+                panAnimationDelta = velocity > 0 ? (currentScrollPosition - minPosition) : (maxPosition - currentScrollPosition);
+                destination = velocity > 0 ? minPosition : -maxPosition;
+
+                if (panAnimationDelta == 0)
                 {
-                    StopScroll();
+                    panAnimationDuration = 0.0f;
                 }
+                else
+                {
+                    panAnimationDuration = (float)Math.Log((panAnimationDelta * logValueOfDeceleration / velocityOfLastPan + 1), decelerationRate);
+                }
+
+                Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
+                    "OverRange======================= \n" +
+                    "[decelerationRate] " + decelerationRate + "\n" +
+                    "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
+                    "[Velocity] " + velocityOfLastPan + "\n" +
+                    "[CurrentPosition] " + currentScrollPosition + "\n" +
+                    "[CandidateDelta] " + panAnimationDelta + "\n" +
+                    "[Destination] " + destination + "\n" +
+                    "[Duration] " + panAnimationDuration + "\n" +
+                    "================================ \n"
+                );
+            }
+            else
+            {
+                panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
+
+                if (adjustDestination != destination)
+                {
+                    destination = adjustDestination;
+                    panAnimationDelta = destination + currentScrollPosition;
+                    velocityOfLastPan = Math.Abs(panAnimationDelta * logValueOfDeceleration / ((float)Math.Pow(decelerationRate, panAnimationDuration) - 1));
+                    panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
+                }
+
+                Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
+                    "================================ \n" +
+                    "[decelerationRate] " + decelerationRate + "\n" +
+                    "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
+                    "[Velocity] " + velocityOfLastPan + "\n" +
+                    "[CurrentPosition] " + currentScrollPosition + "\n" +
+                    "[CandidateDelta] " + panAnimationDelta + "\n" +
+                    "[Destination] " + destination + "\n" +
+                    "[Duration] " + panAnimationDuration + "\n" +
+                    "================================ \n"
+                );
             }
+
+            finalTargetPosition = destination;
+
+            customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
+            animation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
+            GC.KeepAlive(customScrollAlphaFunction);
+            animation.Duration = (int)panAnimationDuration;
+            animation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
+            animation.Play();
         }
 
         private void ScrollAnimationFinished(object sender, EventArgs e)
         {
-            scrolling = false;
-            OnScrollAnimationEnd();
+            OnScrollAnimationEnded();
+        }
+
+        /// <summary>
+        /// Adjust scrolling position by own scrolling rules.
+        /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
+        /// </summary>
+        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual float AdjustTargetPositionOfScrollAnimation(float position)
+        {
+            return position;
+        }
+
+        /// <summary>
+        /// Scroll position given to ScrollTo.
+        /// This is the position in the opposite direction to the position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.Position);
+            }
+        }
+
+        /// <summary>
+        /// Current scroll position in the middle of ScrollTo animation.
+        /// This is the position in the opposite direction to the current position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollCurrentPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.CurrentPosition);
+            }
+        }
+
+        /// <summary>
+        /// Remove all children in ContentContainer.
+        /// </summary>
+        /// <param name="dispose">If true, removed child is disposed.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RemoveAllChildren(bool dispose = false)
+        {
+            RecursiveRemoveChildren(ContentContainer, dispose);
+        }
+
+        private void RecursiveRemoveChildren(View parent, bool dispose)
+        {
+            if (parent == null)
+            {
+                return;
+            }
+            int maxChild = (int)parent.GetChildCount();
+            for (int i = maxChild - 1; i >= 0; --i)
+            {
+                View child = parent.GetChildAt((uint)i);
+                if (child == null)
+                {
+                    continue;
+                }
+                RecursiveRemoveChildren(child, dispose);
+                parent.Remove(child);
+                if (dispose)
+                {
+                    child.Dispose();
+                }
+            }
         }
 
     }