59 子 【常规】邮件系统 / 【常规】邮件系统-客户端 摘取滚动条组件
20个文件已添加
3642 ■■■■■ 已修改文件
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs 2260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs.meta 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/EnhancedScrollerCellView.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/EnhancedScrollerCellView.cs.meta 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/IEnhancedScrollerDelegate.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/IEnhancedScrollerDelegate.cs.meta 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/SmallList.cs 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/SmallList.cs.meta 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/CellView.cs 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/CellView.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerController.cs 622 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerController.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerData.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerData.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerUI.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Skill/ScrollerUI.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/WindowBase.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/WindowBase/WidgetBehavior.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/WindowBase/WidgetBehavior.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs
New file
@@ -0,0 +1,2260 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;
using System.Collections.Generic;
namespace EnhancedUI.EnhancedScroller
{
    /// <summary>
    /// This delegate handles the visibility changes of cell views
    /// </summary>
    /// <param name="cellView">The cell view that changed visibility</param>
    public delegate void CellViewVisibilityChangedDelegate(EnhancedScrollerCellView cellView);
    //委托 当可见性改变时,传入的是当前改变的CellView对象(对象继承自EnhancedScrollerCellView) 事件在 AddCellView和_RecycleCell中触发
    /// <summary>
    /// This delegate handles the scrolling callback of the ScrollRect.
    /// </summary>
    /// <param name="scroller">The scroller that called the delegate</param>
    /// <param name="val">The scroll value of the scroll rect</param>
    /// <param name="scrollPosition">The scroll position in pixels from the start of the scroller</param>
    public delegate void ScrollerScrolledDelegate(EnhancedScroller scroller, Vector2 val, float scrollPosition);
    //委托 当scroll valuechange 时,在onvaluechange事件中触发
    /// <summary>
    /// This delegate handles the snapping of the scroller.
    /// </summary>
    /// <param name="scroller">The scroller that called the delegate</param>
    /// <param name="cellIndex">The index of the cell view snapped on (this may be different than the data index in case of looping)</param>
    /// <param name="dataIndex">The index of the data the view snapped on</param>
    public delegate void ScrollerSnappedDelegate(EnhancedScroller scroller, int cellIndex, int dataIndex);
    //委托 当snap完成时, 在SnapJumpComplete时触发(当开启snap时,每次滑动后都会去进行定位,当定位后就会调用SnapJumpComplete)
    /// <summary>
    /// This delegate handles the change in state of the scroller (scrolling or not scrolling)
    /// </summary>
    /// <param name="scroller">The scroller that changed state</param>
    /// <param name="scrolling">Whether or not the scroller is scrolling</param>
    public delegate void ScrollerScrollingChangedDelegate(EnhancedScroller scroller, bool scrolling);
    //委托 当scroll.velocity开始不为0 或为0 时 ,在Update中触发(即当开始滑动时或当停止滑动时触发,以velocity为基准,为0和不为0时分别触发一次)
    /// <summary>
    /// This delegate handles the change in state of the scroller (jumping or not jumping)
    /// </summary>
    /// <param name="scroller">The scroller that changed state</param>
    /// <param name="tweening">Whether or not the scroller is tweening</param>
    public delegate void ScrollerTweeningChangedDelegate(EnhancedScroller scroller, bool tweening);
    //委托 当开始播放Tween和停止Tween时调用 ,在JumpToDataIndex中如果使用Tween进行过渡定位时,会在开始播放时和结束时各触发一次
    /// <summary>
    /// The EnhancedScroller allows you to easily set up a dynamic scroller that will recycle views for you. This means
    /// that using only a handful of views, you can display thousands of rows. This will save memory and processing
    /// power in your application.
    /// </summary>
    [RequireComponent(typeof(ScrollRect))]
    public class EnhancedScroller : MonoBehaviour
    {
        #region Public
        /// <summary>
        /// The direction this scroller is handling
        /// </summary>
        public enum ScrollDirectionEnum//滑动方式
        {
            Vertical,
            Horizontal
        }
        /// <summary>
        /// Which side of a cell to reference.
        /// For vertical scrollers, before means above, after means below.
        /// For horizontal scrollers, before means to left of, after means to the right of.
        /// </summary>
        public enum CellViewPositionEnum//当前CellViewPrefab的上方还是下方
        {
            Before,
            After
        }
        /// <summary>
        /// This will set how the scroll bar should be shown based on the data. If no scrollbar
        /// is attached, then this is ignored. OnlyIfNeeded will hide the scrollbar based on whether
        /// the scroller is looping or there aren't enough items to scroll.
        /// </summary>
        public enum ScrollbarVisibilityEnum//是否有滑动条,在looping模式下是不会显示的
        {
            OnlyIfNeeded,
            Always,
            Never
        }
        /// <summary>
        /// The direction the scroller is handling
        /// </summary>
        public ScrollDirectionEnum scrollDirection;
        /// <summary>
        /// The number of pixels between cell views, starting after the first cell view
        /// </summary>
        public float spacing;
        /// <summary>
        /// The padding inside of the scroller: top, bottom, left, right.
        /// </summary>
        public RectOffset padding;
        /// <summary>
        /// Whether the scroller should loop the cell views
        /// </summary>
        [SerializeField]
        private bool loop;
        /// <summary>
        /// Whether the scollbar should be shown
        /// </summary>
        [SerializeField]
        private ScrollbarVisibilityEnum scrollbarVisibility;
        /// <summary>
        /// Whether snapping is turned on
        /// </summary>
        public bool snapping;//是否开启定位
        /// <summary>
        /// This is the speed that will initiate the snap. When the
        /// scroller slows down to this speed it will snap to the location
        /// specified.
        /// </summary>
        public float snapVelocityThreshold;//检测定位的阀值,当低于这个值的时候就会触发定位
        /// <summary>
        /// The snap offset to watch for. When the snap occurs, this
        /// location in the scroller will be how which cell to snap to
        /// is determined.
        /// Typically, the offset is in the range 0..1, with 0 being
        /// the top / left of the scroller and 1 being the bottom / right.
        /// In most situations the watch offset and the jump offset
        /// will be the same, they are just separated in case you need
        /// that added functionality.
        /// </summary>
        public float snapWatchOffset;//定位后当前CellVeiw在可视区域内偏移,当为0时定位在最上方,为1时定位在最下方
        /// <summary>
        /// The snap location to move the cell to. When the snap occurs,
        /// this location in the scroller will be where the snapped cell
        /// is moved to.
        /// Typically, the offset is in the range 0..1, with 0 being
        /// the top / left of the scroller and 1 being the bottom / right.
        /// In most situations the watch offset and the jump offset
        /// will be the same, they are just separated in case you need
        /// that added functionality.
        /// </summary>
        public float snapJumpToOffset;//设置定位的偏移,实质上结果和snapWatchOffset一样,也是在0~1之间,增加这个属性主要是考虑到可能会出现一些特殊情况要使用到它
        /// <summary>
        /// Once the cell has been snapped to the scroller location, this
        /// value will determine how the cell is centered on that scroller
        /// location.
        /// Typically, the offset is in the range 0..1, with 0 being
        /// the top / left of the cell and 1 being the bottom / right.
        /// </summary>
        public float snapCellCenterOffset;//设置被定位的CellVeiw定位后的中心偏移,0~1之间取值,如果默认的话会以它的上部为基准,不般来说应该设置为0.5,定位到正中心
        /// <summary>
        /// Whether to include the spacing between cells when determining the
        /// cell offset centering.
        /// </summary>
        public bool snapUseCellSpacing;//开启这个属性的时候,计算定位后的偏移将包括它的上下两个间距,否则即使有间距也不会纳入计算范围内
        /// <summary>
        /// What function to use when interpolating between the current
        /// scroll position and the snap location. This is also known as easing.
        /// If you want to go immediately to the snap location you can either
        /// set the snapTweenType to immediate or set the snapTweenTime to zero.
        /// </summary>
        public TweenType snapTweenType;//定位时播放动画的类型
        /// <summary>
        /// The time it takes to interpolate between the current scroll
        /// position and the snap location.
        /// If you want to go immediately to the snap location you can either
        /// set the snapTweenType to immediate or set the snapTweenTime to zero.
        /// </summary>
        public float snapTweenTime;//播放动画的总时长
        public Action OnCompLoad;
        /// <summary>
        /// This delegate is called when a cell view is hidden or shown
        /// </summary>
        public CellViewVisibilityChangedDelegate cellViewVisibilityChanged;
        /// <summary>
        /// This delegate is called when the scroll rect scrolls
        /// </summary>
        public ScrollerScrolledDelegate scrollerScrolled;
        /// <summary>
        /// This delegate is called when the scroller has snapped to a position
        /// </summary>
        public ScrollerSnappedDelegate scrollerSnapped;
        /// <summary>
        /// This delegate is called when the scroller has started or stopped scrolling
        /// </summary>
        public ScrollerScrollingChangedDelegate scrollerScrollingChanged;
        /// <summary>
        /// This delegate is called when the scroller has started or stopped tweening
        /// </summary>
        public ScrollerTweeningChangedDelegate scrollerTweeningChanged;
        /// <summary>
        /// The Delegate is what the scroller will call when it needs to know information about
        /// the underlying data or views. This allows a true MVC process.
        /// </summary>
        public IEnhancedScrollerDelegate Delegate { get { return _delegate; } set { _delegate = value; _reloadData = true; } }
        /*这里要将一个实现了IEnhancedScrollerDelegate接口的类的对象赋值过来,这个对象用于控制整个Scroll,通过实现这个接口的几个方法
         * 可以定义: 1. 当前CellVeiw的总数目
         *
         * 2. 某dataIndex对应的CellView元素的size
         *
         * 3.如何获取(从循环池里取,或实例化)一个CellView
         * 并得到其CellView对象,而这个对象又继承自EnhancedScrollerCellView,
         * 所以实际上获取了这个CellView的 :
         * 1.cellIdentifier(scroll存在不同Prefab时用于区分的字符串)
         * 2.cellIndex  dataIndex (dataIndex代表的是要循环的cellview元素索引,而cellIndex代表的是集合内元素的索引
         *                          当没有开启循环模式的时候,集合和元素是一致的,它们并没有什么不同,都能对应上相应的cellview元素
         *                          ,但当开启循环后,集合会被扩充(比如扩充3倍),此时dataIndex的整个周期才是真正对应cellview元素的整个周期)
         * 3. active 是否是激活状态
         * 4.提供一个虚方法供子类重写用来实现一些具体的功能
         * 5.通常这个对象本身也会提供一个方法用于对其进行初始化(比如Start()或者直接提供一个公有方法供外部主动调用)
         *
         *这个对象是整个scroll的核心,通过实现它继承的接口,定义了scroll下CellView的总数目,每个CellView的size,
         * 通过调用 EnhancedScroller 中的 ReloadData()方法来初始化整个Scroll
         * 通过实现GetCellView方法,可以调用EnhancedScrolelr类下的GetCellView()方法,传入相应的CellView Prefab,通过定义的cellIdentifier
         * 来从循环池里取对应的CellVeiw,如果没找到则实例化一个,而得到的这个对象又包含其相关的属性,索引和初始化的方法
         * 通过声明一系列的事件可以绑定EnhancedScroller中的委托用于各类事件的触发
         * */
        /// <summary>
        /// The absolute position in pixels from the start of the scroller
        /// </summary>
        public float ScrollPosition
        {
            get
            {
                return _scrollPosition;
            }
            set
            {
                // make sure the position is in the bounds of the current set of views
                value = Mathf.Clamp(value, 0, GetScrollPositionForCellViewIndex(_cellViewSizeArray.Count - 1, CellViewPositionEnum.Before));
                //将传入的Value的大小限制在整个_cellViewSizeArray集合内(注意是最后一个元素的上方,但是这个集合的长度也可能是复制过后的)
                // only if the value has changed
                if (_scrollPosition != value)
                {
                    _scrollPosition = value;
                    if (scrollDirection == ScrollDirectionEnum.Vertical)
                    {
                        // set the vertical position
                        scrollRect.verticalNormalizedPosition = 1f - (_scrollPosition / _ScrollSize);
                        //这里要注意,如果集合进行过复制,那么这里的_scrollSize也一样会比原来的增加,因为_ScrollSize永远比content的总大小小一个可视区大小
                    }
                    else
                    {
                        // set the horizontal position
                        scrollRect.horizontalNormalizedPosition = (_scrollPosition / _ScrollSize);
                    }
                    // flag that we need to refresh
                    _refreshActive = true;
                }
            }
        }
        /// <summary>
        /// Whether the scroller should loop the resulting cell views.
        /// Looping creates three sets of internal size data, attempting
        /// to keep the scroller in the middle set. If the scroller goes
        /// outside of this set, it will jump back into the middle set,
        /// giving the illusion of an infinite set of data.
        /// </summary>
        public bool Loop
        {
            get
            {
                return loop;
            }
            set
            {
                // only if the value has changed
                if (loop != value)//如果相关属性发生了变化
                {
                    // get the original position so that when we turn looping on
                    // we can jump back to this position
                    var originalScrollPosition = _scrollPosition;
                    loop = value;
                    // call resize to generate more internal elements if loop is on,
                    // remove the elements if loop is off
                    _Resize(false);
                    if (loop)
                    {
                        // set the new scroll position based on the middle set of data + the original position
                        ScrollPosition = _loopFirstScrollPosition + originalScrollPosition;
                    }
                    else
                    {
                        // set the new scroll position based on the original position and the first loop position
                        ScrollPosition = originalScrollPosition - _loopFirstScrollPosition;
                    }
                    // update the scrollbars
                    ScrollbarVisibility = scrollbarVisibility;
                }
            }
        }
        /// <summary>
        /// Sets how the visibility of the scrollbars should be handled
        /// </summary>
        public ScrollbarVisibilityEnum ScrollbarVisibility
        {
            get
            {
                return scrollbarVisibility;
            }
            set
            {
                scrollbarVisibility = value;
                // only if the scrollbar exists
                if (_scrollbar != null)
                {
                    // make sure we actually have some cell views
                    if (_cellViewOffsetArray != null && _cellViewOffsetArray.Count > 0)
                    {
                        if (_cellViewOffsetArray.Last() < ScrollRectSize || loop)
                        {
                            // if the size of the scrollable area is smaller than the scroller
                            // or if we have looping on, hide the scrollbar unless the visibility
                            // is set to Always.
                            _scrollbar.SetActive(scrollbarVisibility == ScrollbarVisibilityEnum.Always);
                        }
                        else
                        {
                            // if the size of the scrollable areas is larger than the scroller
                            // or looping is off, then show the scrollbars unless visibility
                            // is set to Never.
                            _scrollbar.SetActive(scrollbarVisibility != ScrollbarVisibilityEnum.Never);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// This is the velocity of the scroller.
        /// </summary>
        public Vector2 Velocity
        {
            get
            {
                return scrollRect.velocity;
            }
            set
            {
                scrollRect.velocity = value;
            }
        }
        /// <summary>
        /// The linear velocity is the velocity on one axis.
        /// The scroller should only be moving one one axix.
        /// </summary>
        public float LinearVelocity //实质上是操作ScrollRect.velocity
                       //Get : 将向量转为float类型
                       //Set:用一个float类型构造一个相应的向量
        //外部方法可以通过访问这个属性来使scroll运动起来
        {
            get
            {
                // return the velocity component depending on which direction this is scrolling
                return (scrollDirection == ScrollDirectionEnum.Vertical ? scrollRect.velocity.y : scrollRect.velocity.x);
            }
            set
            {
                // set the appropriate component of the velocity
                if (scrollDirection == ScrollDirectionEnum.Vertical)
                {
                    scrollRect.velocity = new Vector2(0, value);
                }
                else
                {
                    scrollRect.velocity = new Vector2(value, 0);
                }
            }
        }
        /// <summary>
        /// Whether the scroller is scrolling or not
        /// </summary>
        public bool IsScrolling
        {
            get; private set;
        }
        /// <summary>
        /// Whether the scroller is tweening or not
        /// </summary>
        public bool IsTweening
        {
            get; private set;
        }
        /// <summary>
        /// This is the first cell view index showing in the scroller's visible area
        /// </summary>
        public int StartCellViewIndex//可视区内第一个能看到的元素索引(只要能看到就行,不一定要完全能看到)
        {
            get
            {
                return _activeCellViewsStartIndex;
            }
        }
        /// <summary>
        /// This is the last cell view index showing in the scroller's visible area
        /// </summary>
        public int EndCellViewIndex
        {
            get
            {
                return _activeCellViewsEndIndex;
            }
        }
        /// <summary>
        /// This is the first data index showing in the scroller's visible area
        /// </summary>
        public int StartDataIndex//当循环开启时dataIndex代表循环内的索引,当开启新一轮循环时,dataIndex也会重新计数
        {
            get
            {
                return _activeCellViewsStartIndex % NumberOfCells;
            }
        }
        /// <summary>
        /// This is the last data index showing in the scroller's visible area
        /// </summary>
        public int EndDataIndex
        {
            get
            {
                return _activeCellViewsEndIndex % NumberOfCells;
            }
        }
        public int ActiveCellCnt {
            get {
                return _activeCellViews.Count;
            }
        }
        /// <summary>
        /// This is the number of cells in the scroller
        /// </summary>
        public int NumberOfCells//CellView的总个数,从最上方滑动到最下方总共显示的数目
        {
            get
            {
                return (_delegate != null ? _delegate.GetNumberOfCells(this) : 0);
            }
        }
        /// <summary>
        /// The size of the visible portion of the scroller
        /// </summary>
        public float ScrollRectSize//得到Scroll的高或宽
        {
            get
            {
                if (scrollDirection == ScrollDirectionEnum.Vertical)
                    return _scrollRectTransform.rect.height;
                else
                    return _scrollRectTransform.rect.width;
            }
        }
        public event Action OnFirstLoadAllEvent;
        private bool m_IsLoadAll = false;
        /// <summary>
        /// Create a cell view, or recycle one if it already exists
        /// </summary>
        /// <param name="cellPrefab">The prefab to use to create the cell view</param>
        /// <returns></returns>
        public EnhancedScrollerCellView GetCellView(EnhancedScrollerCellView cellPrefab)
        {
            // see if there is a view to recycle
            var cellView = _GetRecycledCellView(cellPrefab);
            if (cellView == null)
            {
                // no recyleable cell found, so we create a new view
                // and attach it to our container
                var go = Instantiate(cellPrefab.gameObject);
                cellView = go.GetComponent<EnhancedScrollerCellView>();
                cellView.transform.SetParent(_container);
            }
            Vector3 pos = cellView.transform.localPosition;
            if (pos.x == float.NaN || pos.y == float.NaN)
            {
                pos = Vector3.zero;
            }
            pos.z = 0;
            cellView.transform.localPosition = pos;
            return cellView;
            /*这个方法会通过一个cellPrefab来取出一个cellView对象,首先会先从缓冲池里取,如果有相同cellIdentifier属性的物体
             * 就把这个对象从缓冲池取出来,如果没有的话就实例化一个
             */
        }
        /// <summary>
        /// This resets the internal size list and refreshes the cell views
        /// </summary>
        public void ReloadData()
        {
            _reloadData = false;
            _scrollPosition = 0;
            // recycle all the active cells so
            // that we are sure to get fresh views
            _RecycleAllCells();
            // if we have a delegate handling our data, then
            // call the resize
            if (_delegate != null)
                _Resize(false);
            //这个方法用于初始化这个组件或重新加载这个组件
            /*1.将_reloadData置为false,用于标记此组件已经初始化,Update()方法不用再重新执行
             *2.将_scrollPosition 赋值为 0,当它为0时,后续的可视区域会以它为基准,滑动条将移动到最上边或最左边
             *3.如果有可见的元素,全部移动到缓冲池
             *4.执行Resize
             *  1)循环模式下,扩充循环集合到3倍的大小(实际上在循环模式下,不管怎么滑动都只会在中间那个循环内)
             *  2)如果传入false则定位到起始点,如果传入true则定位到当前_position(循环模式下定位到中间那个循环的对应点)
             */
        }
        /// <summary>
        /// This calls the RefreshCellView method on each active cell.
        /// If you override the RefreshCellView method in your cells
        /// then you can update the UI without having to reload the data.
        /// Note: this will not change the cell sizes, you will need
        /// to call ReloadData for that to work.
        /// </summary>
        public void RefreshActiveCellViews() //这是提供给外界的一个方法,用于去主动操作当前Scroll可视区内所有的可见元素
        {
            for (var i = 0; i < _activeCellViews.Count; i++)
            {
                _activeCellViews[i].RefreshCellView();//每一个可见元素都可以重写这个方法来具体执行某段逻辑
            }
            if (this.Delegate != null)
            {
                this.Delegate.OnRebuildComplete();
            }
        }
        public EnhancedScrollerCellView GetActiveCellView(int _index)
        {
            for (int i = 0; i < _activeCellViews.Count; i++)
            {
                if (_activeCellViews[i].index == _index)
                {
                    return _activeCellViews[i];
                }
            }
            return null;
        }
        public SmallList<EnhancedScrollerCellView> GetActiveCellViews()
        {
            return _activeCellViews;
        }
        internal float GetCellSize(int _dataIndex)
        {
            if (_dataIndex >= 0 && _dataIndex < _cellViewSizeArray.Count)
            {
                return _cellViewSizeArray[_dataIndex];
            }
            return 0;
        }
        /// <summary>
        /// Removes all the recycled cell views. This should only be used after you
        /// load in a completely different set of cell views that will not use the
        /// recycled views. This will call garbage collection.
        /// </summary>
        public void ClearRecycled()//主动调用这个方法会立即清空缓存池并释放掉它占用的内存,可以配合上面的方法动态更新scroll
        {
            for (var i = 0; i < _recycledCellViews.Count; i++)
            {
                DestroyImmediate(_recycledCellViews[i].gameObject);
            }
            _recycledCellViews.Clear();
        }
        /// <summary>
        /// Turn looping on or off. This is just a helper function so
        /// you don't have to keep track of the state of the looping
        /// in your own scripts.
        /// </summary>
        public void ToggleLoop()//主动调用会切换当前的循环模式
        {
            Loop = !loop;
        }
        /// <summary>
        /// Jump to a position in the scroller based on a dataIndex. This overload allows you
        /// to specify a specific offset within a cell as well.
        /// </summary>
        /// <param name="dataIndex">he data index to jump to</param>
        /// <param name="scrollerOffset">The offset from the start (top / left) of the scroller in the range 0..1.
        /// Outside this range will jump to the location before or after the scroller's viewable area</param>
        /// <param name="cellOffset">The offset from the start (top / left) of the cell in the range 0..1</param>
        /// <param name="useSpacing">Whether to calculate in the spacing of the scroller in the jump</param>
        /// <param name="tweenType">What easing to use for the jump</param>
        /// <param name="tweenTime">How long to interpolate to the jump point</param>
        /// <param name="jumpComplete">This delegate is fired when the jump completes</param>
        /// 跳转到dataIndex对应的cellview元素,可以选择相应的tween动画
        public void JumpToDataIndex(int dataIndex,
            float scrollerOffset = 0,
            float cellOffset = 0,
            bool useSpacing = true,
            TweenType tweenType = TweenType.immediate,
            float tweenTime = 0f,
            Action jumpComplete = null
            )
        {
            var cellOffsetPosition = 0f;
            if (cellOffset != 0)
            {
                // calculate the cell offset position
                // get the cell's size
                var cellSize = (_delegate != null ? _delegate.GetCellViewSize(this, dataIndex) : 0);
                if (useSpacing)
                {
                    // if using spacing add spacing from one side
                    cellSize += spacing;
                    // if this is not a bounday cell, then add spacing from the other side
                    if (dataIndex > 0 && dataIndex < (NumberOfCells - 1)) cellSize += spacing;
                }
                // calculate the position based on the size of the cell and the offset within that cell
                cellOffsetPosition = cellSize * cellOffset;
            }
            var newScrollPosition = 0f;
            // cache the offset for quicker calculation
            var offset = -(scrollerOffset * ScrollRectSize) + cellOffsetPosition;
            if (loop)
            {
                // if looping, then we need to determine the closest jump position.
                // we do that by checking all three sets of data locations, and returning the closest one
                // get the scroll positions for each data set.
                // Note: we are calculating the position based on the cell view index, not the data index here
                var set1Position = GetScrollPositionForCellViewIndex(dataIndex, CellViewPositionEnum.Before) + offset;
                var set2Position = GetScrollPositionForCellViewIndex(dataIndex + NumberOfCells, CellViewPositionEnum.Before) + offset;
                var set3Position = GetScrollPositionForCellViewIndex(dataIndex + (NumberOfCells * 2), CellViewPositionEnum.Before) + offset;
                // get the offsets of each scroll position from the current scroll position
                var set1Diff = (Mathf.Abs(_scrollPosition - set1Position));
                var set2Diff = (Mathf.Abs(_scrollPosition - set2Position));
                var set3Diff = (Mathf.Abs(_scrollPosition - set3Position));
                // choose the smallest offset from the current position (the closest position)
                if (set1Diff < set2Diff)
                {
                    if (set1Diff < set3Diff)
                    {
                        newScrollPosition = set1Position;
                    }
                    else
                    {
                        newScrollPosition = set3Position;
                    }
                }
                else
                {
                    if (set2Diff < set3Diff)
                    {
                        newScrollPosition = set2Position;
                    }
                    else
                    {
                        newScrollPosition = set3Position;
                    }
                }
            }
            else
            {
                // not looping, so just get the scroll position from the dataIndex
                newScrollPosition = GetScrollPositionForDataIndex(dataIndex, CellViewPositionEnum.Before) + offset;
            }
            // clamp the scroll position to a valid location
            newScrollPosition = Mathf.Clamp(newScrollPosition, 0, GetScrollPositionForCellViewIndex(_cellViewSizeArray.Count - 1, CellViewPositionEnum.Before));
            // if spacing is used, adjust the final position
            if (useSpacing)
            {
                // move back by the spacing if necessary
                newScrollPosition = Mathf.Clamp(newScrollPosition - spacing, 0, GetScrollPositionForCellViewIndex(_cellViewSizeArray.Count - 1, CellViewPositionEnum.Before));
            }
            // start tweening
            if (tweenType == TweenType.immediate || tweenTime == 0)
            {
                // if the easing is immediate or the time is zero, just jump to the end position
                ScrollPosition = newScrollPosition;
                return;
            }
            StartCoroutine(TweenPosition(tweenType, tweenTime, ScrollPosition, newScrollPosition, jumpComplete));
        }
        public void Tween(TweenType tweenType, float tweenTime, float newPosition, Action jumpComplete = null)
        {
            StartCoroutine(TweenPosition(tweenType, tweenTime, ScrollPosition, newPosition, jumpComplete));
        }
        /// <summary>
        /// Jump to a position in the scroller based on a dataIndex.
        /// </summary>
        /// <param name="dataIndex">The data index to jump to</param>
        /// <param name="position">Whether you should jump before or after the cell view</param>
        [System.Obsolete("This is an obsolete method, please use the version of this function with a cell offset.")]
        //过时的方法,可以选择定位到元素上方或下方(左右同理),可以通过上面的方法调整偏移达到同样的效果
        public void JumpToDataIndex(int dataIndex,
            CellViewPositionEnum position = CellViewPositionEnum.Before,
            bool useSpacing = true)
        {
            // if looping is on, we need to jump to the middle set of data, otherwise just use the dataIndex for the cellIndex
            ScrollPosition = GetScrollPositionForDataIndex(dataIndex, position);
            // if spacing is used, adjust the final position
            if (useSpacing)
            {
                if (position == CellViewPositionEnum.Before)
                    ScrollPosition = _scrollPosition - spacing;
                else
                    ScrollPosition = _scrollPosition + spacing;
            }
        }
        /// <summary>
        /// Snaps the scroller on command. This is called internally when snapping is set to true and the velocity
        /// has dropped below the threshold. You can use this to manually snap whenever you like.
        /// </summary>
        /// 当开启锁定位置功能时,在滑动条滑动后元素会进行相应的定位(定位的约束在面板中可以进行设置)
        public void Snap()
        {
            if (NumberOfCells == 0) return;
            // set snap jumping to true so other events won't process while tweening
            _snapJumping = true;
            // stop the scroller
            LinearVelocity = 0;
            // cache the current inertia state and turn off inertia
            _snapInertia = scrollRect.inertia;
            scrollRect.inertia = false;
            // calculate the snap position
            var snapPosition = ScrollPosition + (ScrollRectSize * Mathf.Clamp01(snapWatchOffset));
            // get the cell view index of cell at the watch location
            _snapCellViewIndex = GetCellViewIndexAtPosition(snapPosition);
            // get the data index of the cell at the watch location
            _snapDataIndex = _snapCellViewIndex % NumberOfCells;
            // jump the snapped cell to the jump offset location and center it on the cell offset
            JumpToDataIndex(_snapDataIndex, snapJumpToOffset, snapCellCenterOffset, snapUseCellSpacing, snapTweenType, snapTweenTime, SnapJumpComplete);
        }
        /// <summary>
        /// Gets the scroll position in pixels from the start of the scroller based on the cellViewIndex
        /// </summary>
        /// <param name="cellViewIndex">The cell index to look for. This is used instead of dataIndex in case of looping</param>
        /// <param name="insertPosition">Do we want the start or end of the cell view's position</param>
        /// <returns></returns>
        public float GetScrollPositionForCellViewIndex(int cellViewIndex, CellViewPositionEnum insertPosition)//根据相关的索引从_cellViewOffsetArray里取对应的position
        {
            /*这个方法的主要作用是通过一个索引来返回position,而这个position实际上可以认为是可滑动的content向上或向左滑动了多少
             * 因为之前通过_cellViewOffsetArray来存储了每个cellView下方相对于左上的偏移(TextAnchor.UpperLeft)所以通过索引实际上就能取出这个偏移量
             * 再通过Before(取上或左) After(取下或右)来判断最终position的值
             * -----scrollRect的NormallizedPosition实际上是可滑动区域的大小向另一方滑动中偏移量的比例--------------
             * 所以可以通过postion和可滑动区域大小的比值来定义滑动条滑动到了哪个位置
             */
            if (NumberOfCells == 0 || _cellViewOffsetArray.Count == 0) return 0;//如果scroll没有子物体直接返回
            if (cellViewIndex <= 0 && insertPosition == CellViewPositionEnum.Before)
            {
                return 0;//如果是第一个子物体且要得到它的上方位置,直接返回0
            }
            else
            {
                if (cellViewIndex < _cellViewOffsetArray.Count)
                {
                    // the index is in the range of cell view offsets
                    if (insertPosition == CellViewPositionEnum.Before)
                    {
                        // return the previous cell view's offset + the spacing between cell views
                        return _cellViewOffsetArray[cellViewIndex - 1] + spacing + (scrollDirection == ScrollDirectionEnum.Vertical ? padding.top : padding.left);
                        //返回当前索引上方的值
                        //这里要注意一点就是一般来说调用这个方法的时候就是处在loop模式之下,这里的_cellViewOffsetArray是经过复制后的集合
                    }
                    else
                    {
                        // return the offset of the cell view (offset is after the cell)
                        return _cellViewOffsetArray[cellViewIndex] + (scrollDirection == ScrollDirectionEnum.Vertical ? padding.top : padding.left);
                        //返回当前索引下方的值
                    }
                }
                else
                {
                    // get the start position of the last cell (the offset of the second to last cell)
                    return _cellViewOffsetArray[_cellViewOffsetArray.Count - 2];
                    //返回最后一个索引的上方值
                }
            }
        }
        /// <summary>
        /// Gets the scroll position in pixels from the start of the scroller based on the dataIndex
        /// </summary>
        /// <param name="dataIndex">The data index to look for</param>
        /// <param name="insertPosition">Do we want the start or end of the cell view's position</param>
        /// <returns></returns>
        public float GetScrollPositionForDataIndex(int dataIndex, CellViewPositionEnum insertPosition)
        {
            return GetScrollPositionForCellViewIndex(loop ? _delegate.GetNumberOfCells(this) + dataIndex : dataIndex, insertPosition);
            //通过dataIndex来确定ScrollPosition,要注意的是GetScrollPositionForCellViewIndex需要的是cellIndex而不是dataIndex
            //所以在循环模式下,dataIndex要加上所有元素的数量(因为在循环模式下,dataIndex对应的元素永远都是在集合中的第二组)
        }
        /// <summary>
        /// Gets the index of a cell view at a given position
        /// </summary>
        /// <param name="position">The pixel offset from the start of the scroller</param>
        /// <returns></returns>
        public int GetCellViewIndexAtPosition(float position)
        {
            // call the overrloaded method on the entire range of the list
            return _GetCellIndexAtPosition(position, 0, _cellViewOffsetArray.Count - 1);
        }
        #endregion
        #region Private
        /// <summary>
        /// Cached reference to the scrollRect
        /// </summary>
        private ScrollRect m_ScrollRect;
        public ScrollRect scrollRect {
            get {
                if (m_ScrollRect == null) {
                    m_ScrollRect = this.GetComponent<ScrollRect>();
                }
                return m_ScrollRect;
            }
        }
        /// <summary>
        /// Cached reference to the scrollRect's transform
        /// </summary>
        private RectTransform _scrollRectTransform;
        /// <summary>
        /// Cached reference to the scrollbar if it exists
        /// </summary>
        private Scrollbar _scrollbar;
        /// <summary>
        /// Cached reference to the active cell view container
        /// </summary>
        private RectTransform _container;
        /// <summary>
        /// Cached reference to the layout group that handles view positioning
        /// </summary>
        private HorizontalOrVerticalLayoutGroup _layoutGroup;
        public HorizontalOrVerticalLayoutGroup LayoutGroup
        {
            get
            {
                return _layoutGroup;
            }
        }
        /// <summary>
        /// Reference to the delegate that will tell this scroller information
        /// about the underlying data
        /// </summary>
        private IEnhancedScrollerDelegate _delegate;
        /// <summary>
        /// Flag to tell the scroller to reload the data
        /// </summary>
        private bool _reloadData;
        /// <summary>
        /// Flag to tell the scroller to refresh the active list of cell views
        /// </summary>
        private bool _refreshActive;
        /// <summary>
        /// List of views that have been recycled
        /// </summary>
        private SmallList<EnhancedScrollerCellView> _recycledCellViews = new SmallList<EnhancedScrollerCellView>();
        /// <summary>
        /// Cached reference to the element used to offset the first visible cell view
        /// </summary>
        private LayoutElement _firstPadder;
        /// <summary>
        /// Cached reference to the element used to keep the cell views at the correct size
        /// </summary>
        private LayoutElement _lastPadder;
        /// <summary>
        /// Cached reference to the container that holds the recycled cell views
        /// </summary>
        private RectTransform _recycledCellViewContainer;
        /// <summary>
        /// Internal list of cell view sizes. This is created when the data is reloaded
        /// to speed up processing.
        /// </summary>
        private SmallList<float> _cellViewSizeArray = new SmallList<float>();
        /// <summary>
        /// Internal list of cell view offsets. Each cell view offset is an accumulation
        /// of the offsets previous to it.
        /// This is created when the data is reloaded to speed up processing.
        /// </summary>
        private SmallList<float> _cellViewOffsetArray = new SmallList<float>();
        /// <summary>
        /// The scrollers position
        /// </summary>
        private float _scrollPosition;
        /// <summary>
        /// The list of cell views that are currently being displayed
        /// </summary>
        private SmallList<EnhancedScrollerCellView> _activeCellViews = new SmallList<EnhancedScrollerCellView>();
        /// <summary>
        /// The index of the first cell view that is being displayed
        /// </summary>
        private int _activeCellViewsStartIndex;
        /// <summary>
        /// The index of the last cell view that is being displayed
        /// </summary>
        private int _activeCellViewsEndIndex;
        /// <summary>
        /// The index of the first element of the middle section of cell view sizes.
        /// Used only when looping
        /// </summary>
        private int _loopFirstCellIndex;
        /// <summary>
        /// The index of the last element of the middle seciton of cell view sizes.
        /// used only when looping
        /// </summary>
        private int _loopLastCellIndex;
        /// <summary>
        /// The scroll position of the first element of the middle seciotn of cell views.
        /// Used only when looping
        /// </summary>
        private float _loopFirstScrollPosition;
        /// <summary>
        /// The scroll position of the last element of the middle section of cell views.
        /// Used only when looping
        /// </summary>
        private float _loopLastScrollPosition;
        /// <summary>
        /// The position that triggers the scroller to jump to the end of the middle section
        /// of cell views. This keeps the scroller in the middle section as much as possible.
        /// </summary>
        private float _loopFirstJumpTrigger;
        /// <summary>
        /// The position that triggers the scroller to jump to the start of the middle section
        /// of cell views. This keeps the scroller in the middle section as much as possible.
        /// </summary>
        private float _loopLastJumpTrigger;
        /// <summary>
        /// The cached value of the last scroll rect size. This is checked every frame to see
        /// if the scroll rect has resized. If so, it will refresh.
        /// </summary>
        private float _lastScrollRectSize;
        /// <summary>
        /// The cached value of the last loop setting. This is checked every frame to see
        /// if looping was toggled. If so, it will refresh.
        /// </summary>
        private bool _lastLoop;
        /// <summary>
        /// The cell view index we are snapping to
        /// </summary>
        private int _snapCellViewIndex;
        /// <summary>
        /// The data index we are snapping to
        /// </summary>
        private int _snapDataIndex;
        /// <summary>
        /// Whether we are currently jumping due to a snap
        /// </summary>
        private bool _snapJumping;
        /// <summary>
        /// What the previous inertia setting was before the snap jump.
        /// We cache it here because we need to turn off inertia while
        /// manually tweeing.
        /// </summary>
        private bool _snapInertia;
        /// <summary>
        /// The cached value of the last scrollbar visibility setting. This is checked every
        /// frame to see if the scrollbar visibility needs to be changed.
        /// </summary>
        private ScrollbarVisibilityEnum _lastScrollbarVisibility;
        /// <summary>
        /// Where in the list we are
        /// </summary>
        private enum ListPositionEnum
        {
            First,
            Last
        }
        /// <summary>
        /// The size of the active cell view container minus the visibile portion
        /// of the scroller
        /// </summary>
        public float _ScrollSize//这里并不是指scrllrect的大小,而是指真正在滑动的区域的大小
                                /*scrollrect的normallizePosition实际上就是这个真正能滑动的区域的大小
                                 * 在滑动过程中归一化的值,而这个大小就是所有子物体的大小与可视化区域的差
                                 * 当content的大小等于所有子物体的大小时,就可以简化操作直接用content的大小来代替
                                 */
        {
            get
            {
                if (scrollDirection == ScrollDirectionEnum.Vertical)
                {
                    if (_container != null && _scrollRectTransform != null)
                        return _container.rect.height - _scrollRectTransform.rect.height;
                    else
                        return 0;
                }
                else
                {
                    if (_container != null && _scrollRectTransform != null)
                        return _container.rect.width - _scrollRectTransform.rect.width;
                    else
                        return 0;
                }
            }
        }
        /// <summary>
        /// This function will create an internal list of sizes and offsets to be used in all calculations.
        /// It also sets up the loop triggers and positions and initializes the cell views.
        /// </summary>
        /// <param name="keepPosition">If true, then the scroller will try to go back to the position it was at before the resize</param>
        private void _Resize(bool keepPosition)
        {
            // cache the original position
            var originalScrollPosition = _scrollPosition;
            // clear out the list of cell view sizes and create a new list
            _cellViewSizeArray.Clear();
            var offset = _AddCellViewSizes();//每次清空后重新添加一次,并返回这个集合的值的和
            // if looping, we need to create three sets of size data
            if (loop)
            {
                // if the cells don't entirely fill up the scroll area,
                // make some more size entries to fill it up
                if (offset < ScrollRectSize)//所有子物体相加没有这块可视区域大
                {
                    int additionalRounds = Mathf.CeilToInt(ScrollRectSize / offset);
                    _DuplicateCellViewSizes(additionalRounds, _cellViewSizeArray.Count);//如果子物体的个数不足以填充可视区域,要进行复制
                }
                // set up the loop indices
                _loopFirstCellIndex = _cellViewSizeArray.Count;
                _loopLastCellIndex = _loopFirstCellIndex + _cellViewSizeArray.Count - 1;
                //这里的两个值是在集合没有进行任何复制时赋值的,_loopFirstCellIndex是第二次循环的第一个元素索引,_loopLastCellIndex是第二次循环的最后一个索引
                // create two more copies of the cell sizes
                _DuplicateCellViewSizes(2, _cellViewSizeArray.Count);
                /*当使用循环模式的时候,如果子物体不足以填充可视区就进行一次复制,使元素能够滑动
                 *当元素足够多,能够在可视区滑动的时候,再进行两次复制(最终结果是三个循环)
                 * firstCellIndex为第一个循环的第一个物体索引,比如10个元素,第一个循环的第一个索引就是10,因为是从(0~9)
                 * lastCellIndex为第一个循环的最后一个物体索引,比如10元素(0~9)+ 10 = 19
                 * 要注意: 上面说的复制在这里并没有复制游戏对象,只是对集合进行了一些扩充,这只是为了方便理解
                */
            }
            // calculate the offsets of each cell view
            _CalculateCellViewOffsets();//计算了第一个元素下方相对原点的偏移(其中也包括了间距)
            // set the size of the active cell view container based on the number of cell views there are and each of their sizes
            if (scrollDirection == ScrollDirectionEnum.Vertical)
                _container.sizeDelta = new Vector2(_container.sizeDelta.x, _cellViewOffsetArray.Last() + padding.top + padding.bottom);
            else
                _container.sizeDelta = new Vector2(_cellViewOffsetArray.Last() + padding.left + padding.right, _container.sizeDelta.y);
            //设置content的大小,根据_cellViewOffsetArray的最后一个值就可以得到所有元素和它们的间距的总和,再加上padding的值就可以让content和所有子物体完美匹配
            //且因为之前设置了pivot的原因,直接设置content的大小并不会让它往两边缩放,而是顺着我们需要的方向进行缩放
            // if looping, set up the loop positions and triggers
            if (loop)
            {
                _loopFirstScrollPosition = GetScrollPositionForCellViewIndex(_loopFirstCellIndex, CellViewPositionEnum.Before) + (spacing * 0.5f);
                _loopLastScrollPosition = GetScrollPositionForCellViewIndex(_loopLastCellIndex, CellViewPositionEnum.After) - ScrollRectSize + (spacing * 0.5f);
                _loopFirstJumpTrigger = _loopFirstScrollPosition - ScrollRectSize;
                _loopLastJumpTrigger = _loopLastScrollPosition + ScrollRectSize;
                //firstJumpTrigger是第一个循环的最后一个索引的下方 - 可视区大小,lastJumpTrigger是第二次循环的最后一个索引的下方
            }
            // create the visibile cells
            _ResetVisibleCellViews();//根据可视区两端索引(通过_scrollPosition来计算索引)来重建可视区域内的子物体
            if (this.Delegate != null)
            {
                this.Delegate.OnRebuildComplete();
            }
            // if we need to maintain our original position
            if (keepPosition)//如果为true则保持调用这个方法时对应的位置
            {
                ScrollPosition = originalScrollPosition;
            }
            else
            {
                if (loop)
                {
                    ScrollPosition = _loopFirstScrollPosition;//循环模式时定义的是第二次循环的起始点(相当于两边都存在一个循环,定位在中间那个循环的起始点)
                }
                else
                {
                    ScrollPosition = 0;
                }
            }
            // set up the visibility of the scrollbar
            ScrollbarVisibility = scrollbarVisibility;
            if (!m_IsLoadAll)
            {
                m_IsLoadAll = true;
                if (OnFirstLoadAllEvent != null)
                {
                    OnFirstLoadAllEvent();
                }
            }
        }
        /// <summary>
        /// Creates a list of cell view sizes for faster access
        /// </summary>
        /// <returns></returns>
        private float _AddCellViewSizes()//创建一个用于存放所有子物体的大小的集合,并返回所有子物体相加后的总尺寸
        {
            var offset = 0f;
            // add a size for each row in our data based on how many the delegate tells us to create
            for (var i = 0; i < NumberOfCells; i++)
            {
                // add the size of this cell based on what the delegate tells us to use. Also add spacing if this cell isn't the first one
                _cellViewSizeArray.Add(_delegate.GetCellViewSize(this, i) + (i == 0 ? 0 : _layoutGroup.spacing));
                offset += _cellViewSizeArray[_cellViewSizeArray.Count - 1];
            }
            return offset;
        }
        /// <summary>
        /// Create a copy of the cell view sizes. This is only used in looping
        /// </summary>
        /// <param name="numberOfTimes">How many times the copy should be made</param>
        /// <param name="cellCount">How many cells to copy</param>
        private void _DuplicateCellViewSizes(int numberOfTimes, int cellCount)//当使用循环模式时,如果子物体不足以填充可视区域,就进行复制
        {
            for (var i = 0; i < numberOfTimes; i++)             //注意此处的复制并不是复制游戏对象,而只是集合的扩充
            {
                for (var j = 0; j < cellCount; j++)
                {
                    _cellViewSizeArray.Add(_cellViewSizeArray[j] + (j == 0 ? _layoutGroup.spacing : 0));//存储子物体size的集合也要跟着变
                    /*j == 0 ? _layoutGroup.spacing : 0  注意这个三元表达式,在进行复制时,如果是复制集合的第一个元素要多加一个spacing
                     * 因为滑动条中只有第一个元素前方或左方是没有spacing的,所以当复制n份时,就要增加n个spacing
                     */
                }
            }
        }
        /// <summary>
        /// Calculates the offset of each cell, accumulating the values from previous cells
        /// </summary>
        private void _CalculateCellViewOffsets()//此处的偏移集合存储的是当前索引下的物体下方相对原点的偏移量,也就是说最后一个元素的大小就是所有子物体大小加间距的总和
        {
            _cellViewOffsetArray.Clear();
            var offset = 0f;
            for (var i = 0; i < _cellViewSizeArray.Count; i++)
            {
                offset += _cellViewSizeArray[i];
                _cellViewOffsetArray.Add(offset);
            }
        }
        /// <summary>
        /// Get a recycled cell with a given identifier if available
        /// </summary>
        /// <param name="cellPrefab">The prefab to check for</param>
        /// <returns></returns>
        private EnhancedScrollerCellView _GetRecycledCellView(EnhancedScrollerCellView cellPrefab)//从缓存池中取一个cellPrefab
        {
            for (var i = 0; i < _recycledCellViews.Count; i++)
            {
                if (_recycledCellViews[i].cellIdentifier == cellPrefab.cellIdentifier)
                {
                    // the cell view was found, so we use this recycled one.
                    // we also remove it from the recycled list
                    var cellView = _recycledCellViews.RemoveAt(i);
                    return cellView;
                }
            }
            return null;
        }
        /// <summary>
        /// This sets up the visible cells, adding and recycling as necessary
        /// </summary>
        private void _ResetVisibleCellViews()//通过某个_scrollPosition来判断可视区两端能看到的索引,再通过这两个索引来重建可视区内的子物体,并更新相应的集合
        {
            int startIndex;
            int endIndex;
            // calculate the range of the visible cells
            _CalculateCurrentActiveCellRange(out startIndex, out endIndex);//这个方法返回当前_scrollPosition对应的可视区两端能看到的子物体索引(并不一定要完全在可视区内)
            // go through each previous active cell and recycle it if it no longer falls in the range
            var i = 0;
            SmallList<int> remainingCellIndices = new SmallList<int>();
            while (i < _activeCellViews.Count)
            {
                if (_activeCellViews[i].cellIndex < startIndex || _activeCellViews[i].cellIndex > endIndex)
                {
                    _RecycleCell(_activeCellViews[i]);//如果原本可见的元素不在可视区域内将其移动到_RecycleCell节点下
                }
                else
                {
                    // this cell index falls in the new range, so we add its
                    // index to the reusable list
                    remainingCellIndices.Add(_activeCellViews[i].cellIndex);
                    //剩下的肯定是可见元素,放入remainingCellIndices集合中,这里要注意的是,这一集合并不一定能够填充整个可视区域,
                    //startIndex和endIndex内的部分索引可能并没有对应的可见子物体
                    i++;
                }
            }
            if (remainingCellIndices.Count == 0)//在跳转至较远位置时,会导致原来可见元素全部都不可见了,又或者此组件刚进行初始化时,并没有任何可见元素
            {
                // there were no previous active cells remaining,
                // this list is either brand new, or we jumped to
                // an entirely different part of the list.
                // just add all the new cell views
                for (i = startIndex; i <= endIndex; i++)
                {
                    _AddCellView(i, ListPositionEnum.Last);
                    /*正序添加,当remainingCellIndices集合没有元素时,直接按顺序添加就可以了,先添加的在前面,后添加的在后面
                     * 在当前_scrollPosition下能看到的就只有从startIndex到endIndex这几个元素,只需要实例化(从缓冲池取)这几个元素就行了*/
                }
            }
            else
            {
                // we are able to reuse some of the previous
                // cell views
                // first add the views that come before the
                // previous list, going backward so that the
                // new views get added to the front
                for (i = endIndex; i >= startIndex; i--)
                {
                    if (i < remainingCellIndices.First())
                    {
                        _AddCellView(i, ListPositionEnum.First);//倒序添加,即当前添加的元素一定在第一个,完成循环后第一个被添加的元素就放到了最后一个
                    }
                    /*当remainingCellIndices集合中存在元素时,说明集合中的元素是可见的,但是不一定能够完全填充可视区,所以还需要根据情况来添加新的可见元素
                     * 如果此集合的最小元素索引大于startIndex和endIndex之内的某一索引,说明这一索引对应的子物体要进行实例化(从缓冲池取),且一定排在这个集合的第一个元素之前
                     * 这里要注意的是循环中i是递减的,当索引i比集合中最小的元素索引都要小时说明了i索引此时并没有可对应的可见子物体,实例化子物体时要
                     * 将它设置在最前面,以此类推,循环完成后,后添加的会比先添加的排在前面
                     */
                }
                // next add teh views that come after the
                // previous list, going forward and adding
                // at the end of the list
                for (i = startIndex; i <= endIndex; i++)
                {
                    if (i > remainingCellIndices.Last())
                    {
                        _AddCellView(i, ListPositionEnum.Last);
                    }
                    /*当此集合最大的元素索引小于startIndex到endIndex中某一索引时,说明这一索引一样没有对应的可见子物体,
                     * 又因为此循环是递增的,所以先进行实例化(缓冲池取)的子物体一定是要比后添加的子物体排在前面
                     */
                }
            }
            // update the start and end indices
            _activeCellViewsStartIndex = startIndex;
            _activeCellViewsEndIndex = endIndex;
            // adjust the padding elements to offset the cell views correctly
            _SetPadders();//当可见区域被填充满后,就可以将其它区域设置为padding
        }
        /// <summary>
        /// Recycles all the active cells
        /// </summary>
        private void _RecycleAllCells()//移除全部可见元素到缓冲池
        {
            while (_activeCellViews.Count > 0) _RecycleCell(_activeCellViews[0]);
            _activeCellViewsStartIndex = 0;
            _activeCellViewsEndIndex = 0;
        }
        /// <summary>
        /// Recycles one cell view
        /// </summary>
        /// <param name="cellView"></param>
        public void _RecycleCell(EnhancedScrollerCellView cellView)//从_activeCellVeiws集合中移除元素到_RecycleCell集合中
        {
            // remove the cell view from the active list
            _activeCellViews.Remove(cellView);
            // add the cell view to the recycled list
            _recycledCellViews.Add(cellView);
            // move the GameObject to the recycled container
            cellView.transform.SetParent(_recycledCellViewContainer);
            // reset the cellView's properties
            cellView.dataIndex = 0;
            cellView.cellIndex = 0;
            cellView.active = false;
            if (cellViewVisibilityChanged != null) cellViewVisibilityChanged(cellView);
        }
        /// <summary>
        /// Creates a cell view, or recycles if it can
        /// </summary>
        /// <param name="cellIndex">The index of the cell view</param>
        /// <param name="listPosition">Whether to add the cell to the beginning or the end</param>
        private void _AddCellView(int cellIndex, ListPositionEnum listPosition)
        {
            //通过某个索引添加(从缓冲池中取)子物体到_activeCellViews集合中,并触发相关事件
            //这个方法本身也会根据传过来的索引来初始化CellView
            if (NumberOfCells == 0) return;
            // get the dataIndex. Modulus is used in case of looping so that the first set of cells are ignored
            var dataIndex = cellIndex % NumberOfCells;
            // request a cell view from the delegate
            var cellView = _delegate.GetCellView(this, dataIndex, cellIndex);
            // set the cell's properties
            cellView.cellIndex = cellIndex;
            cellView.dataIndex = dataIndex;
            cellView.active = true;
            // add the cell view to the active container
            cellView.transform.SetParent(_container, false);
            cellView.transform.localScale = Vector3.one;
            // add a layout element to the cellView
            LayoutElement layoutElement = cellView.GetComponent<LayoutElement>();
            if (layoutElement == null) layoutElement = cellView.gameObject.AddComponent<LayoutElement>();
            // set the size of the layout element
            if (scrollDirection == ScrollDirectionEnum.Vertical)
                layoutElement.minHeight = _cellViewSizeArray[cellIndex] - (cellIndex > 0 ? _layoutGroup.spacing : 0);
            else
                layoutElement.minWidth = _cellViewSizeArray[cellIndex] - (cellIndex > 0 ? _layoutGroup.spacing : 0);
            // add the cell to the active list
            if (listPosition == ListPositionEnum.First)
                _activeCellViews.AddStart(cellView);
            else
                _activeCellViews.Add(cellView);
            // set the hierarchy position of the cell view in the container
            if (listPosition == ListPositionEnum.Last)
                cellView.transform.SetSiblingIndex(_container.childCount - 2);//这里的添加方式决定了两个padder的位置是夹着所有的cellView的
            else if (listPosition == ListPositionEnum.First)
                cellView.transform.SetSiblingIndex(1);
            // call the visibility change delegate if available
            if (cellViewVisibilityChanged != null) cellViewVisibilityChanged(cellView);
        }
        /// <summary>
        /// This function adjusts the two padders that control the first cell view's
        /// offset and the overall size of each cell.
        /// </summary>
        private void _SetPadders()//当处理完可见区域后,Padder就可以计算出来,padder的作用就是用来支撑整个scroll内的元素,保证它们的位置
        {
            if (NumberOfCells == 0) return;
            // calculate the size of each padder
            var firstSize = _cellViewOffsetArray[_activeCellViewsStartIndex] - _cellViewSizeArray[_activeCellViewsStartIndex];
            var lastSize = _cellViewOffsetArray.Last() - _cellViewOffsetArray[_activeCellViewsEndIndex];
            if (scrollDirection == ScrollDirectionEnum.Vertical)
            {
                // set the first padder and toggle its visibility
                _firstPadder.minHeight = firstSize;
                _firstPadder.SetActive(_firstPadder.minHeight > 0);
                // set the last padder and toggle its visibility
                _lastPadder.minHeight = lastSize;
                _lastPadder.SetActive(_lastPadder.minHeight > 0);
            }
            else
            {
                // set the first padder and toggle its visibility
                _firstPadder.minWidth = firstSize;
                _firstPadder.SetActive(_firstPadder.minWidth > 0);
                // set the last padder and toggle its visibility
                _lastPadder.minWidth = lastSize;
                _lastPadder.SetActive(_lastPadder.minWidth > 0);
            }
        }
        /// <summary>
        /// This function is called if the scroller is scrolled, updating the active list of cells
        /// </summary>
        private void _RefreshActive()
        {
            _refreshActive = false;
            int startIndex;
            int endIndex;
            var velocity = Vector2.zero;
            // if looping, check to see if we scrolled past a trigger
            if (loop)
            {
                if (_scrollPosition < _loopFirstJumpTrigger)
                {
                    velocity = scrollRect.velocity;
                    ScrollPosition = _loopLastScrollPosition - (_loopFirstJumpTrigger - _scrollPosition);
                    scrollRect.velocity = velocity;
                }
                else if (_scrollPosition > _loopLastJumpTrigger)
                {
                    velocity = scrollRect.velocity;
                    ScrollPosition = _loopFirstScrollPosition + (_scrollPosition - _loopLastJumpTrigger);
                    scrollRect.velocity = velocity;
                }
                //循环模式下要将ScrollPosition限制在第二个循环内,这里的几个变量都是用于控制这一属性的
                /*注意这里的ScrollPosition的赋值操作,只有当在循环模式下,且滑动的偏移已经达到了触发_loopJumpTrigger的时候
                 * 它才会发生赋值操作,当它被赋值以后,即此时的content的位置发生了改变,然后会再次调用此方法刷新可视区,直到再次触发loopJumpTirgger
                 * 之前,ScrollPosition都不会再次被赋值了
                 */
            }
            // get the range of visibile cells
            _CalculateCurrentActiveCellRange(out startIndex, out endIndex);
            // if the index hasn't changed, ignore and return
            if (startIndex == _activeCellViewsStartIndex && endIndex == _activeCellViewsEndIndex) return;
            // recreate the visibile cells
            _ResetVisibleCellViews();
            if (this.Delegate != null)
            {
                this.Delegate.OnRebuildComplete();
            }
        }
        /// <summary>
        /// Determines which cells can be seen
        /// </summary>
        /// <param name="startIndex">The index of the first cell visible</param>
        /// <param name="endIndex">The index of the last cell visible</param>
        private void _CalculateCurrentActiveCellRange(out int startIndex, out int endIndex)//这个方法返回的是基于_scrollPosition得到的可视区域两端索引
        {
            startIndex = 0;
            endIndex = 0;
            // get the positions of the scroller
            var startPosition = _scrollPosition;
            var endPosition = _scrollPosition + (scrollDirection == ScrollDirectionEnum.Vertical ? _scrollRectTransform.rect.height : _scrollRectTransform.rect.width);
            // calculate each index based on the positions
            startIndex = GetCellViewIndexAtPosition(startPosition);
            endIndex = GetCellViewIndexAtPosition(endPosition);
        }
        /// <summary>
        /// Gets the index of a cell at a given position based on a subset range.
        /// This function uses a recursive binary sort to find the index faster.
        /// </summary>
        /// <param name="position">The pixel offset from the start of the scroller</param>
        /// <param name="startIndex">The first index of the range</param>
        /// <param name="endIndex">The last index of the rnage</param>
        /// <returns></returns>
        private int _GetCellIndexAtPosition(float position, int startIndex, int endIndex)//这个方法的startIndex为0 ,endIndex为_cellViewOffsetArray的元素数量(但会根据计算的值进行调整)
        {
            // if the range is invalid, then we found our index, return the start index
            if (startIndex >= endIndex) return startIndex;
            // determine the middle point of our binary search
            var middleIndex = (startIndex + endIndex) / 2;
            // if the middle index is greater than the position, then search the last
            // half of the binary tree, else search the first half
            if ((_cellViewOffsetArray[middleIndex] + (scrollDirection == ScrollDirectionEnum.Vertical ? padding.top : padding.left)) >= position)
                return _GetCellIndexAtPosition(position, startIndex, middleIndex);
            else
                return _GetCellIndexAtPosition(position, middleIndex + 1, endIndex);
        }
        /// <summary>
        /// Caches and initializes the scroller
        /// </summary>
        void Awake()
        {
            GameObject go;
            // cache some components
            //_scrollRect = this.GetComponent<ScrollRect>();
            _scrollRectTransform = scrollRect.GetComponent<RectTransform>();
            // destroy any content objects if they exist. Likely there will be
            // one at design time because Unity gives errors if it can't find one.
            if (scrollRect.content != null)
            {
                DestroyImmediate(scrollRect.content.gameObject);//如果存在content则销毁
            }
            // Create a new active cell view container with a layout group
            go = new GameObject("Container", typeof(RectTransform));
            go.transform.SetParent(_scrollRectTransform);
            if (scrollDirection == ScrollDirectionEnum.Vertical)
                go.AddComponent<VerticalLayoutGroup>();
            else
                go.AddComponent<HorizontalLayoutGroup>();
            _container = go.GetComponent<RectTransform>();
            // set the containers anchor and pivot
            if (scrollDirection == ScrollDirectionEnum.Vertical)
            {
                _container.anchorMin = new Vector2(0, 1);
                _container.anchorMax = Vector2.one;
                _container.pivot = new Vector2(0.5f, 1f);
                //设置了content的锚点和轴心,主要作用是便于计算,以它上方中心的点到scroll上方的距离为它的y轴值,它的长度即为它的Height属性
                //当设置它的高的时候就是设置了他的大小
            }
            else
            {
                _container.anchorMin = Vector2.zero;
                _container.anchorMax = new Vector2(0, 1f);
                _container.pivot = new Vector2(0, 0.5f);//同上
            }
            _container.offsetMax = Vector2.zero;
            _container.offsetMin = Vector2.zero;
            _container.localScale = Vector3.one;
            Vector3 pos = _container.transform.localPosition;
            pos.z = 0;
            _container.transform.localPosition = pos;
            //将它的位置固定在scrol上方或左方,通过这个位置和上面设置的锚点来保证设置大小的时候不会跑偏
            scrollRect.content = _container;
            // cache the scrollbar if it exists
            if (scrollDirection == ScrollDirectionEnum.Vertical)
            {
                _scrollbar = scrollRect.verticalScrollbar;
            }
            else
            {
                _scrollbar = scrollRect.horizontalScrollbar;
            }
            // cache the layout group and set up its spacing and padding
            _layoutGroup = _container.GetComponent<HorizontalOrVerticalLayoutGroup>();
            _layoutGroup.spacing = spacing;
            _layoutGroup.padding = padding;
            _layoutGroup.childAlignment = TextAnchor.UpperLeft;//添加锚点
            _layoutGroup.childForceExpandHeight = true;
            _layoutGroup.childForceExpandWidth = true;
            // force the scroller to scroll in the direction we want
            //_scrollRect.horizontal = scrollDirection == ScrollDirectionEnum.Horizontal;
            //_scrollRect.vertical = scrollDirection == ScrollDirectionEnum.Vertical;
            // create the padder objects
            go = new GameObject("First Padder", typeof(RectTransform), typeof(LayoutElement));
            go.transform.SetParent(_container, false);
            _firstPadder = go.GetComponent<LayoutElement>();
            go = new GameObject("Last Padder", typeof(RectTransform), typeof(LayoutElement));
            go.transform.SetParent(_container, false);
            _lastPadder = go.GetComponent<LayoutElement>();
            // create the recycled cell view container
            go = new GameObject("Recycled Cells", typeof(RectTransform));
            go.transform.SetParent(scrollRect.transform, false);
            _recycledCellViewContainer = go.GetComponent<RectTransform>();
            _recycledCellViewContainer.SetActive(false);
            //实例化了两个padder和一个缓冲池
            // set up the last values for updates
            _lastScrollRectSize = ScrollRectSize;
            _lastLoop = loop;
            _lastScrollbarVisibility = scrollbarVisibility;
            //一些变量进行了初始化
            if(OnCompLoad!=null) OnCompLoad();
            inited = true;
        }
        void Update()
        {
            if (_reloadData)//当外部脚本设置Delegate的时候就会将_reloadData置为true
            {
                // if the reload flag is true, then reload the data
                ReloadData();//理论上不用在外部调用ReloadData()因为当成功赋值Delegate后这个方法就会执行一次
                               //但如果想在update之前执行就主动调用ReloadData()
            }
            // if the scroll rect size has changed and looping is on,
            // or the loop setting has changed, then we need to resize
            if (
                    (loop && _lastScrollRectSize != ScrollRectSize)//当Scroll的大小发生改变并且开启循环时
                    ||
                    (loop != _lastLoop)//当循环开关发生了改变
                )
            {
                _Resize(true);
                _lastScrollRectSize = ScrollRectSize;
                _lastLoop = loop;
            }
            // update the scroll bar visibility if it has changed
            if (_lastScrollbarVisibility != scrollbarVisibility)//如果滑动条的可见性发生改变也需要更新一下相关变量
            {
                ScrollbarVisibility = scrollbarVisibility;
                _lastScrollbarVisibility = scrollbarVisibility;
            }
            // determine if the scroller has started or stopped scrolling
            // and call the delegate if so.
            if (LinearVelocity != 0 && !IsScrolling)//当Scroll.velocity不为0时说明在滑动,此时如果IsScrolling为false(标记为没有滑动)
            {                                       //触发当滑动开始时的事件
                IsScrolling = true;
                if (scrollerScrollingChanged != null) scrollerScrollingChanged(this, true);
            }
            else if (LinearVelocity == 0 && IsScrolling)//同理触发当滑动结束时的事件,这里的两个事件都只会在一个滑动周期时各执行一次
            {
                IsScrolling = false;
                if (scrollerScrollingChanged != null) scrollerScrollingChanged(this, false);
            }
        }
        void LateUpdate()
        {
            if (_refreshActive)//当设置ScrollPosition时就会将_refreshActive置为true,此时就会调用_RefreshActive()方法
            {
                // if the refresh toggle is on, then
                // refresh the list
                _RefreshActive();
            }
        }
        void OnEnable()
        {
            // when the scroller is enabled, add a listener to the onValueChanged handler
            scrollRect.onValueChanged.AddListener(_ScrollRect_OnValueChanged);//当显示时注册
        }
        void OnDisable()
        {
            // when the scroller is disabled, remove the listener
            scrollRect.onValueChanged.RemoveListener(_ScrollRect_OnValueChanged);//当隐藏时移除注册
        }
        /// <summary>
        /// Handler for when the scroller changes value
        /// </summary>
        /// <param name="val">The scroll rect's value</param>
        private void _ScrollRect_OnValueChanged(Vector2 val)
        {
            // set the internal scroll position
            if (scrollDirection == ScrollDirectionEnum.Vertical)
            {
                _scrollPosition = (1f - val.y) * _ScrollSize;//注意这里只是给私有字段赋值,并不会调用相关属性的Set方法(如果调用了Set方法会导致无限循环,Set方法会触发valuechange,valuechange又触发Set)
            }
            else
            {
                _scrollPosition = val.x * _ScrollSize;
            }
            _refreshActive = true;//标记要进行刷新
            // call the handler if it exists
            if (scrollerScrolled != null) scrollerScrolled(this, val, _scrollPosition);//滑动过程中触发的相关事件
            // if the snapping is turned on, handle it
            if (snapping && !_snapJumping)//如果开启锁定位置,但此时没有定位
            {
                // if the speed has dropped below the threshhold velocity
                if (Mathf.Abs(LinearVelocity) <= snapVelocityThreshold)
                {
                    // Call the snap function
                    Snap();//进行定位
                }
            }
            _RefreshActive();//刷新
            /*在滑动的过程中,通过NormalizePosition来求得_scrollPosition
             * 如果满足定位条件就进行定位
             * 以上代码执行完成后就会去刷新整个Scroll
             */
        }
        /// <summary>
        /// This is fired by the tweener when the snap tween is completed
        /// </summary>
        private void SnapJumpComplete()//当定位完成时
        {
            // reset the snap jump to false and restore the inertia state
            _snapJumping = false;
            scrollRect.inertia = _snapInertia;
            // fire the scroller snapped delegate
            if (scrollerSnapped != null) scrollerSnapped(this, _snapCellViewIndex, _snapDataIndex);
        }
        #endregion
        #region Tweening
        /// <summary>
        /// The easing type
        /// </summary>
        public enum TweenType
        {
            immediate,
            linear,
            spring,
            easeInQuad,
            easeOutQuad,
            easeInOutQuad,
            easeInCubic,
            easeOutCubic,
            easeInOutCubic,
            easeInQuart,
            easeOutQuart,
            easeInOutQuart,
            easeInQuint,
            easeOutQuint,
            easeInOutQuint,
            easeInSine,
            easeOutSine,
            easeInOutSine,
            easeInExpo,
            easeOutExpo,
            easeInOutExpo,
            easeInCirc,
            easeOutCirc,
            easeInOutCirc,
            easeInBounce,
            easeOutBounce,
            easeInOutBounce,
            easeInBack,
            easeOutBack,
            easeInOutBack,
            easeInElastic,
            easeOutElastic,
            easeInOutElastic
        }
        private float _tweenTimeLeft;
        public bool inited;
        /// <summary>
        /// Moves the scroll position over time between two points given an easing function. When the
        /// tween is complete it will fire the jumpComplete delegate.
        /// </summary>
        /// <param name="tweenType">The type of easing to use</param>
        /// <param name="time">The amount of time to interpolate</param>
        /// <param name="start">The starting scroll position</param>
        /// <param name="end">The ending scroll position</param>
        /// <param name="jumpComplete">The action to fire when the tween is complete</param>
        /// <returns></returns>
        IEnumerator TweenPosition(TweenType tweenType, float time, float start, float end, Action tweenComplete)
        {
            if (tweenType == TweenType.immediate || time == 0)
            {
                // if the easing is immediate or the time is zero, just jump to the end position
                ScrollPosition = end;
            }
            else
            {
                // zero out the velocity
                scrollRect.velocity = Vector2.zero;
                // fire the delegate for the tween start
                IsTweening = true;
                if (scrollerTweeningChanged != null) scrollerTweeningChanged(this, true);
                _tweenTimeLeft = 0;
                var newPosition = 0f;
                // while the tween has time left, use an easing function
                while (_tweenTimeLeft < time)
                {
                    switch (tweenType)
                    {
                        case TweenType.linear: newPosition = linear(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.spring: newPosition = spring(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInQuad: newPosition = easeInQuad(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutQuad: newPosition = easeOutQuad(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutQuad: newPosition = easeInOutQuad(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInCubic: newPosition = easeInCubic(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutCubic: newPosition = easeOutCubic(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutCubic: newPosition = easeInOutCubic(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInQuart: newPosition = easeInQuart(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutQuart: newPosition = easeOutQuart(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutQuart: newPosition = easeInOutQuart(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInQuint: newPosition = easeInQuint(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutQuint: newPosition = easeOutQuint(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutQuint: newPosition = easeInOutQuint(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInSine: newPosition = easeInSine(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutSine: newPosition = easeOutSine(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutSine: newPosition = easeInOutSine(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInExpo: newPosition = easeInExpo(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutExpo: newPosition = easeOutExpo(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutExpo: newPosition = easeInOutExpo(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInCirc: newPosition = easeInCirc(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutCirc: newPosition = easeOutCirc(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutCirc: newPosition = easeInOutCirc(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInBounce: newPosition = easeInBounce(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutBounce: newPosition = easeOutBounce(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutBounce: newPosition = easeInOutBounce(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInBack: newPosition = easeInBack(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutBack: newPosition = easeOutBack(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutBack: newPosition = easeInOutBack(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInElastic: newPosition = easeInElastic(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeOutElastic: newPosition = easeOutElastic(start, end, (_tweenTimeLeft / time)); break;
                        case TweenType.easeInOutElastic: newPosition = easeInOutElastic(start, end, (_tweenTimeLeft / time)); break;
                    }
                    if (loop)
                    {
                        // if we are looping, we need to make sure the new position isn't past the jump trigger.
                        // if it is we need to reset back to the jump position on the other side of the area.
                        if (end > start && newPosition > _loopLastJumpTrigger)
                        {
                            //DesignDebug.Log("name: " + name + " went past the last jump trigger, looping back around");
                            newPosition = _loopFirstScrollPosition + (newPosition - _loopLastJumpTrigger);
                        }
                        else if (start > end && newPosition < _loopFirstJumpTrigger)
                        {
                            //DesignDebug.Log("name: " + name + " went past the first jump trigger, looping back around");
                            newPosition = _loopLastScrollPosition - (_loopFirstJumpTrigger - newPosition);
                        }
                    }
                    // set the scroll position to the tweened position
                    ScrollPosition = newPosition;
                    // increase the time elapsed
                    _tweenTimeLeft += Time.unscaledDeltaTime;
                    yield return null;
                }
                // the time has expired, so we make sure the final scroll position
                // is the actual end position.
                ScrollPosition = end;
            }
            // the tween jump is complete, so we fire the delegate
            if (tweenComplete != null) tweenComplete();
            // fire the delegate for the tween ending
            IsTweening = false;
            if (scrollerTweeningChanged != null) scrollerTweeningChanged(this, false);
        }
        private float linear(float start, float end, float val)
        {
            return Mathf.Lerp(start, end, val);
        }
        private static float spring(float start, float end, float val)
        {
            val = Mathf.Clamp01(val);
            val = (Mathf.Sin(val * Mathf.PI * (0.2f + 2.5f * val * val * val)) * Mathf.Pow(1f - val, 2.2f) + val) * (1f + (1.2f * (1f - val)));
            return start + (end - start) * val;
        }
        private static float easeInQuad(float start, float end, float val)
        {
            end -= start;
            return end * val * val + start;
        }
        private static float easeOutQuad(float start, float end, float val)
        {
            end -= start;
            return -end * val * (val - 2) + start;
        }
        private static float easeInOutQuad(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return end / 2 * val * val + start;
            val--;
            return -end / 2 * (val * (val - 2) - 1) + start;
        }
        private static float easeInCubic(float start, float end, float val)
        {
            end -= start;
            return end * val * val * val + start;
        }
        private static float easeOutCubic(float start, float end, float val)
        {
            val--;
            end -= start;
            return end * (val * val * val + 1) + start;
        }
        private static float easeInOutCubic(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return end / 2 * val * val * val + start;
            val -= 2;
            return end / 2 * (val * val * val + 2) + start;
        }
        private static float easeInQuart(float start, float end, float val)
        {
            end -= start;
            return end * val * val * val * val + start;
        }
        private static float easeOutQuart(float start, float end, float val)
        {
            val--;
            end -= start;
            return -end * (val * val * val * val - 1) + start;
        }
        private static float easeInOutQuart(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return end / 2 * val * val * val * val + start;
            val -= 2;
            return -end / 2 * (val * val * val * val - 2) + start;
        }
        private static float easeInQuint(float start, float end, float val)
        {
            end -= start;
            return end * val * val * val * val * val + start;
        }
        private static float easeOutQuint(float start, float end, float val)
        {
            val--;
            end -= start;
            return end * (val * val * val * val * val + 1) + start;
        }
        private static float easeInOutQuint(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return end / 2 * val * val * val * val * val + start;
            val -= 2;
            return end / 2 * (val * val * val * val * val + 2) + start;
        }
        private static float easeInSine(float start, float end, float val)
        {
            end -= start;
            return -end * Mathf.Cos(val / 1 * (Mathf.PI / 2)) + end + start;
        }
        private static float easeOutSine(float start, float end, float val)
        {
            end -= start;
            return end * Mathf.Sin(val / 1 * (Mathf.PI / 2)) + start;
        }
        private static float easeInOutSine(float start, float end, float val)
        {
            end -= start;
            return -end / 2 * (Mathf.Cos(Mathf.PI * val / 1) - 1) + start;
        }
        private static float easeInExpo(float start, float end, float val)
        {
            end -= start;
            return end * Mathf.Pow(2, 10 * (val / 1 - 1)) + start;
        }
        private static float easeOutExpo(float start, float end, float val)
        {
            end -= start;
            return end * (-Mathf.Pow(2, -10 * val / 1) + 1) + start;
        }
        private static float easeInOutExpo(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return end / 2 * Mathf.Pow(2, 10 * (val - 1)) + start;
            val--;
            return end / 2 * (-Mathf.Pow(2, -10 * val) + 2) + start;
        }
        private static float easeInCirc(float start, float end, float val)
        {
            end -= start;
            return -end * (Mathf.Sqrt(1 - val * val) - 1) + start;
        }
        private static float easeOutCirc(float start, float end, float val)
        {
            val--;
            end -= start;
            return end * Mathf.Sqrt(1 - val * val) + start;
        }
        private static float easeInOutCirc(float start, float end, float val)
        {
            val /= .5f;
            end -= start;
            if (val < 1) return -end / 2 * (Mathf.Sqrt(1 - val * val) - 1) + start;
            val -= 2;
            return end / 2 * (Mathf.Sqrt(1 - val * val) + 1) + start;
        }
        private static float easeInBounce(float start, float end, float val)
        {
            end -= start;
            float d = 1f;
            return end - easeOutBounce(0, end, d - val) + start;
        }
        private static float easeOutBounce(float start, float end, float val)
        {
            val /= 1f;
            end -= start;
            if (val < (1 / 2.75f))
            {
                return end * (7.5625f * val * val) + start;
            }
            else if (val < (2 / 2.75f))
            {
                val -= (1.5f / 2.75f);
                return end * (7.5625f * (val) * val + .75f) + start;
            }
            else if (val < (2.5 / 2.75))
            {
                val -= (2.25f / 2.75f);
                return end * (7.5625f * (val) * val + .9375f) + start;
            }
            else
            {
                val -= (2.625f / 2.75f);
                return end * (7.5625f * (val) * val + .984375f) + start;
            }
        }
        private static float easeInOutBounce(float start, float end, float val)
        {
            end -= start;
            float d = 1f;
            if (val < d / 2) return easeInBounce(0, end, val * 2) * 0.5f + start;
            else return easeOutBounce(0, end, val * 2 - d) * 0.5f + end * 0.5f + start;
        }
        private static float easeInBack(float start, float end, float val)
        {
            end -= start;
            val /= 1;
            float s = 1.70158f;
            return end * (val) * val * ((s + 1) * val - s) + start;
        }
        private static float easeOutBack(float start, float end, float val)
        {
            float s = 1.70158f;
            end -= start;
            val = (val / 1) - 1;
            return end * ((val) * val * ((s + 1) * val + s) + 1) + start;
        }
        private static float easeInOutBack(float start, float end, float val)
        {
            float s = 1.70158f;
            end -= start;
            val /= .5f;
            if ((val) < 1)
            {
                s *= (1.525f);
                return end / 2 * (val * val * (((s) + 1) * val - s)) + start;
            }
            val -= 2;
            s *= (1.525f);
            return end / 2 * ((val) * val * (((s) + 1) * val + s) + 2) + start;
        }
        private static float easeInElastic(float start, float end, float val)
        {
            end -= start;
            float d = 1f;
            float p = d * .3f;
            float s = 0;
            float a = 0;
            if (val == 0) return start;
            val = val / d;
            if (val == 1) return start + end;
            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }
            val = val - 1;
            return -(a * Mathf.Pow(2, 10 * val) * Mathf.Sin((val * d - s) * (2 * Mathf.PI) / p)) + start;
        }
        private static float easeOutElastic(float start, float end, float val)
        {
            end -= start;
            float d = 1f;
            float p = d * .3f;
            float s = 0;
            float a = 0;
            if (val == 0) return start;
            val = val / d;
            if (val == 1) return start + end;
            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }
            return (a * Mathf.Pow(2, -10 * val) * Mathf.Sin((val * d - s) * (2 * Mathf.PI) / p) + end + start);
        }
        private static float easeInOutElastic(float start, float end, float val)
        {
            end -= start;
            float d = 1f;
            float p = d * .3f;
            float s = 0;
            float a = 0;
            if (val == 0) return start;
            val = val / (d / 2);
            if (val == 2) return start + end;
            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }
            if (val < 1)
            {
                val = val - 1;
                return -0.5f * (a * Mathf.Pow(2, 10 * val) * Mathf.Sin((val * d - s) * (2 * Mathf.PI) / p)) + start;
            }
            val = val - 1;
            return a * Mathf.Pow(2, -10 * val) * Mathf.Sin((val * d - s) * (2 * Mathf.PI) / p) * 0.5f + end + start;
        }
        #endregion
    }
}
Main/Component/UI/EnhancedScroller/EnhancedScroller.cs.meta
New file
@@ -0,0 +1,25 @@
fileFormatVersion: 2
guid: 9c1b74f910281224a8cae6d8e4fc1f43
labels:
- Data
- Inventory
- Cells
- Cell
- Reuse
- UI
- ScrollRect
- Scroller
- Virtualize
- Recycle
- Scroll
- List
timeCreated: 1435585470
licenseType: Store
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Component/UI/EnhancedScroller/EnhancedScrollerCellView.cs
New file
@@ -0,0 +1,53 @@
using UnityEngine;
using System;
using System.Collections;
namespace EnhancedUI.EnhancedScroller
{
    /// <summary>
    /// This is the base class that all cell views should derive from
    /// </summary>
    public class EnhancedScrollerCellView : WidgetBehavior
    {
        /// <summary>
        /// The cellIdentifier is a unique string that allows the scroller
        /// to handle different types of cells in a single list. Each type
        /// of cell should have its own identifier
        /// </summary>
        public string cellIdentifier; //当存在不同prefab的时候,就可以用这个identifier来区分
        /// <summary>
        /// The cell index of the cell view
        /// This will differ from the dataIndex if the list is looping
        /// </summary>
        [NonSerialized]
        public int cellIndex;//_activeCellViews中的索引
        [NonSerialized]
        public int index = -1;//_activeCellViews中的索引
        /// <summary>
        /// The data index of the cell view
        /// </summary>
        [NonSerialized]
        public int dataIndex;//非循环模式下和cellIndex相同,循环模式下为当前循环的子物体的索引
        /// <summary>
        /// Whether the cell is active or recycled
        /// </summary>
        [NonSerialized]
        public bool active;
        /// <summary>
        /// This method is called by the scroller when the RefreshActiveCellViews is called on the scroller
        /// You can override it to update your cell's view UID
        /// </summary>
        public virtual void RefreshCellView() { }//当主动调用EnhancedScrller类中RefreshActiveCellViews()方法时,会遍历当前处于Active状态的CellVeiw,并调用它们重写的这个方法
                                                 //这个方法可以用于主动刷新当前处于Active状态的CellVeiw
        protected virtual void Awake()
        {
            InitWidgts();
        }
    }
}
Main/Component/UI/EnhancedScroller/EnhancedScrollerCellView.cs.meta
New file
@@ -0,0 +1,25 @@
fileFormatVersion: 2
guid: 1f75717e94199704f82f26fcf6953e84
labels:
- Data
- Inventory
- Cells
- Cell
- Reuse
- UI
- ScrollRect
- Scroller
- Virtualize
- Recycle
- Scroll
- List
timeCreated: 1435585579
licenseType: Store
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Component/UI/EnhancedScroller/IEnhancedScrollerDelegate.cs
New file
@@ -0,0 +1,53 @@
using UnityEngine;
using System.Collections;
namespace EnhancedUI.EnhancedScroller
{
    /// <summary>
    /// All scripts that handle the scroller's callbacks should inherit from this interface
    /// </summary>
    public interface IEnhancedScrollerDelegate
    {
        /// <summary>
        /// Gets the number of cells in a list of data
        /// </summary>
        /// <param name="scroller"></param>
        /// <returns></returns>
        int GetNumberOfCells(EnhancedScroller scroller);//得到Scroll里面CellVeiw的总数目
        /// <summary>
        /// Gets the size of a cell view given the index of the data set.
        /// This allows you to have different sized cells
        /// </summary>
        /// <param name="scroller"></param>
        /// <param name="dataIndex"></param>
        /// <returns></returns>
        float GetCellViewSize(EnhancedScroller scroller, int dataIndex);//得到某个dataIndex的size(因为有可能会存在不同的CellVeiw)
        /// <summary>
        /// Gets the cell view that should be used for the data index. Your implementation
        /// of this function should request a new cell from the scroller so that it can
        /// properly recycle old cells.
        /// </summary>
        /// <param name="scroller"></param>
        /// <param name="dataIndex"></param>
        /// <param name="cellIndex"></param>
        /// <returns></returns>
        EnhancedScrollerCellView GetCellView(EnhancedScroller scroller, int dataIndex, int cellIndex);
        /*
         * 用于得到(或从循环集合中取,或实例化)一个CellView并取其CellView对象
           注意这里返回的是CellView的父类型,CellVeiw是要自己来写的,这样就可以通过重写父类的虚方法实现多态
           */
        /*要想使用这个插件,需要我们添加两个类,
         * 1)一个类继承自EnHancedScrollerCellView,用于保存单个CellView的相关信息
         * 2)另一个类实现这个接口,用于控制整个Scroll,并提供给外界一些相关信息(总的CellView数目,每个CellView的size等)
         *      其中,GetCellView()方法在实现的时候直接调用 EnhancedScroller类中的GetCellView()方法来进行实现就可以了
         * 这两个类也可根据自己的需要进行扩展
         */
        void OnRebuildComplete();
    }
}
Main/Component/UI/EnhancedScroller/IEnhancedScrollerDelegate.cs.meta
New file
@@ -0,0 +1,25 @@
fileFormatVersion: 2
guid: 7701b31137fe72d47b256054738fd57d
labels:
- Data
- Inventory
- Cells
- Cell
- Reuse
- UI
- ScrollRect
- Scroller
- Virtualize
- Recycle
- Scroll
- List
timeCreated: 1435585741
licenseType: Store
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Component/UI/EnhancedScroller/SmallList.cs
New file
@@ -0,0 +1,219 @@
using UnityEngine;
using System.Collections.Generic;
namespace EnhancedUI
{
    /// <summary>
    /// This is a super light implementation of an array that
    /// behaves like a list, automatically allocating new memory
    /// when needed, but not releasing it to garbage collection.
    /// </summary>
    /// <typeparam name="T">The type of the list</typeparam>
    public class SmallList<T>
    {
        /// <summary>
        /// internal storage of list data
        /// </summary>
        public T[] data;
        /// <summary>
        /// The number of elements in the list
        /// </summary>
        public int Count = 0;
        /// <summary>
        /// Indexed access to the list items
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public T this[int i]
        {
            get { return data[i]; }
            set { data[i] = value; }
        }
        /// <summary>
        /// Resizes the array when more memory is needed.
        /// </summary>
        private void ResizeArray()
        {
            T[] newData;
            if (data != null)
                newData = new T[Mathf.Max(data.Length << 1, 64)];
            else
                newData = new T[64];
            if (data != null && Count > 0)
                data.CopyTo(newData, 0);
            data = newData;
        }
        /// <summary>
        /// Instead of releasing the memory to garbage collection,
        /// the list size is set back to zero
        /// </summary>
        public void Clear()
        {
            Count = 0;
        }
        /// <summary>
        /// Returns the first element of the list
        /// </summary>
        /// <returns></returns>
        public T First()
        {
            if (data == null || Count == 0) return default(T);
            return data[0];
        }
        /// <summary>
        /// Returns the last element of the list
        /// </summary>
        /// <returns></returns>
        public T Last()
        {
            if (data == null || Count == 0) return default(T);
            return data[Count - 1];
        }
        /// <summary>
        /// Adds a new element to the array, creating more
        /// memory if necessary
        /// </summary>
        /// <param name="item"></param>
        public void Add(T item)
        {
            if (data == null || Count == data.Length)
                ResizeArray();
            data[Count] = item;
            Count++;
        }
        /// <summary>
        /// Adds a new element to the start of the array, creating more
        /// memory if necessary
        /// </summary>
        /// <param name="item"></param>
        public void AddStart(T item)
        {
            Insert(item, 0);
        }
        /// <summary>
        /// Inserts a new element to the array at the index specified, creating more
        /// memory if necessary
        /// </summary>
        /// <param name="item"></param>
        public void Insert(T item, int index)
        {
            if (data == null || Count == data.Length)
                ResizeArray();
            for (var i = Count; i > index; i--)
            {
                data[i] = data[i - 1];
            }
            data[index] = item;
            Count++;
        }
        /// <summary>
        /// Removes an item from the start of the data
        /// </summary>
        /// <returns></returns>
        public T RemoveStart()
        {
            return RemoveAt(0);
        }
        /// <summary>
        /// Removes an item from the index of the data
        /// </summary>
        /// <returns></returns>
        public T RemoveAt(int index)
        {
            if (data != null && Count != 0)
            {
                T val = data[index];
                for (var i = index; i < Count - 1; i++)
                {
                    data[i] = data[i + 1];
                }
                Count--;
                data[Count] = default(T);
                return val;
            }
            else
            {
                return default(T);
            }
        }
        /// <summary>
        /// Removes an item from the data
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public T Remove(T item)
        {
            if (data != null && Count != 0)
            {
                for (var i = 0; i < Count; i++)
                {
                    if (data[i].Equals(item))
                    {
                        return RemoveAt(i);
                    }
                }
            }
            return default(T);
        }
        /// <summary>
        /// Removes an item from the end of the data
        /// </summary>
        /// <returns></returns>
        public T RemoveEnd()
        {
            if (data != null && Count != 0)
            {
                Count--;
                T val = data[Count];
                data[Count] = default(T);
                return val;
            }
            else
            {
                return default(T);
            }
        }
        /// <summary>
        /// Determines if the data contains the item
        /// </summary>
        /// <param name="item">The item to compare</param>
        /// <returns>True if the item exists in teh data</returns>
        public bool Contains(T item)
        {
            if (data == null)
                return false;
            for (var i = 0; i < Count; i++)
            {
                if (data[i].Equals(item))
                    return true;
            }
            return false;
        }
    }
}
Main/Component/UI/EnhancedScroller/SmallList.cs.meta
New file
@@ -0,0 +1,25 @@
fileFormatVersion: 2
guid: aab29d68fb4a95d49bca25f359d8f076
labels:
- Data
- Inventory
- Cells
- Cell
- Reuse
- UI
- ScrollRect
- Scroller
- Virtualize
- Recycle
- Scroll
- List
timeCreated: 1435585634
licenseType: Store
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Skill.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c1ae185223108c54388b2570cb6b4758
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Skill/CellView.cs
New file
@@ -0,0 +1,106 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EnhancedUI.EnhancedScroller;
using UnityEngine.UI;
using System;
public class CellView : EnhancedScrollerCellView
{
    Button m_btn;
    public Action<CellView> OnClick;
    public ScrollerDataType type;
    public float height = 100;
    public CellInfo? info;
    public ScrollerController controller { get; private set; }
    private ScrollerUI scrollerUI;
    public ScrollerUI diplayCell
    {
        get
        {
            if (scrollerUI == null)
            {
                scrollerUI = GetComponent<ScrollerUI>();
            }
            return scrollerUI;
        }
    }
    private void Start()
    {
        m_btn = GetComponent<Button>();
        if (m_btn != null)
        {
            m_btn.onClick.AddListener(OnBtnClick);
        }
    }
    public void SetData(ScrollerData data, ScrollerDataType type, ScrollerController controller)
    {
        index = data.index;
        this.type = type;
        this.controller = controller;
    }
    void OnBtnClick()
    {
        if (OnClick != null)
        {
            OnClick(this);
        }
    }
    public override void RefreshCellView()
    {
        if (controller != null)
        {
            controller.OnRefreshCellActive(this);
        }
        RefreshUI();
    }
    public void RefreshUI()
    {
        if (diplayCell != null)
        {
            diplayCell.Refresh(this);
        }
    }
}
public struct CellInfo
{
    public int infoInt1;
    public int infoInt2;
    public int infoInt3;
    public string infoStr1;
    public CellInfo(int infoInt1, int infoInt2, string infoStr1)
    {
        this.infoInt1 = infoInt1;
        this.infoInt2 = infoInt2;
        this.infoStr1 = infoStr1;
        this.infoInt3 = 0;
    }
    public void SetInfo(int _val)
    {
        infoInt3 = _val;
    }
    public static CellInfo Default
    {
        get
        {
            return new CellInfo(0, 0, string.Empty);
        }
    }
}
Main/System/Skill/CellView.cs.meta
New file
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b1a0224d44a749a47ae59084c72f4ddc
timeCreated: 1501126153
licenseType: Free
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Skill/ScrollerController.cs
New file
@@ -0,0 +1,622 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EnhancedUI.EnhancedScroller;
using System;
using UnityEngine.UI;
public enum EnhanceLockType
{
    None,
    LockHorizonRight,
    LockHorizonLeft,
    LockVerticalTop,
    LockVerticalBottom,
    KeepHorizon,
    KeepVertical,
    KeepVertical2,
}
public class ScrollerController : MonoBehaviour, IEnhancedScrollerDelegate {
    private List<ScrollerData> _data = new List<ScrollerData>();
    public delegate void OnRefreshCellDelegate(ScrollerDataType type, CellView cell);
    public event OnRefreshCellDelegate OnRefreshCell;
    public delegate bool OnDynamicCell(ScrollerDataType type,int index,out float height);
    public event OnDynamicCell OnGetDynamicSize;
    public event Action OnRefreshCompleteEvent;
    public event Action<Vector2> onValueChange;
    public EnhancedScrollerCellView GetCellView(EnhancedScroller scroller, int dataIndex, int cellIndex)
    {
        CellView cell = GetCellView(scroller, dataIndex);
        if (!cell.gameObject.activeSelf) cell.SetActive(true);
        cell.SetData(_data[dataIndex], _data[dataIndex].m_ScrollerType,this);
        if (_data[dataIndex].OnClick != null) {
            cell.OnClick = _data[dataIndex].OnClick;
        }
        cell.info = _data[dataIndex].info;
        if (OnRefreshCell != null) {
            OnRefreshCell(_data[dataIndex].m_ScrollerType, cell);
        }
        cell.RefreshUI();
        return cell;
    }
    private CellView GetCellView(EnhancedScroller scroller, int dataIndex)
    {
        CellView cell = null;
        switch (_data[dataIndex].m_ScrollerType) {
            case ScrollerDataType.Header:
                cell = scroller.GetCellView(m_CellHeaderPrefab) as CellView;
                break;
            case ScrollerDataType.Normal:
                cell = scroller.GetCellView(m_CellNomalPrefab) as CellView;
                break;
            case ScrollerDataType.Tail:
                cell = scroller.GetCellView(m_CellTailPrefab) as CellView;
                break;
            case ScrollerDataType.Extra1:
                cell = scroller.GetCellView(m_CellExtraPrefab1) as CellView;
                break;
            case ScrollerDataType.Extra2:
                cell = scroller.GetCellView(m_CellExtraPrefab2) as CellView;
                break;
            case ScrollerDataType.Extra3:
                cell = scroller.GetCellView(m_CellExtraPrefab3) as CellView;
                break;
        }
        return cell;
    }
    public float GetCellViewSize(EnhancedScroller scroller, int dataIndex)
    {
        if (OnGetDynamicSize != null) {
            float height = 0;
            if (OnGetDynamicSize(_data[dataIndex].m_ScrollerType, _data[dataIndex].index,out height)) {
                return height;
            }
        }
        switch (_data[dataIndex].m_ScrollerType) {
            case ScrollerDataType.Header:
                return m_CellHeaderPrefab.height;
            case ScrollerDataType.Normal:
                return m_CellNomalPrefab.height;
            case ScrollerDataType.Tail:
                return m_CellTailPrefab.height;
            case ScrollerDataType.Extra1:
                return m_CellExtraPrefab1.height;
            case ScrollerDataType.Extra2:
                return m_CellExtraPrefab2.height;
            case ScrollerDataType.Extra3:
                return m_CellExtraPrefab3.height;
        }
        return 80;
    }
    public void OnRefreshCellActive(CellView cell)
    {
        if (OnRefreshCell != null) {
            OnRefreshCell(cell.type, cell);
        }
    }
    public int GetNumberOfCells(EnhancedScroller scroller)
    {
        return _data.Count;
    }
    public EnhancedScroller m_Scorller;
    private ScrollRect m_ScrollRect;
    public ScrollRect mScrollRect {
        get {
            if (m_ScrollRect == null)
                m_ScrollRect = m_Scorller.GetComponent<ScrollRect>();
            return m_ScrollRect;
        }
    }
    public CellView m_CellHeaderPrefab;
    public CellView m_CellNomalPrefab;
    public CellView m_CellTailPrefab;
    public CellView m_CellExtraPrefab1;
    public CellView m_CellExtraPrefab2;
    public CellView m_CellExtraPrefab3;
    public int maxCellCnt = 0;
    public bool fakeMiddleCenter = false; //是否需要假居中, 居中时当cell个数少 尺寸小于最大尺寸时 不可滑动; 只有header类型时才准确
    int jiaMiddleWithMaxSize = 0; //假居中的最大尺寸,自动设为预制体里的初始尺寸
    public bool horizontal = false;
    public bool vertical = false;
    private bool inited = false;
    private int cacheJump = -1;
    private void Awake()
    {
        m_Scorller.Delegate = this;
        horizontal = mScrollRect.horizontal;
        vertical = mScrollRect.vertical;
        mScrollRect.onValueChanged.AddListener(OnValueChangle);
        inited = m_Scorller.inited;
        m_Scorller.OnFirstLoadAllEvent += OnFirstLoadAllEvent;
        if (m_Scorller.OnCompLoad == null && !m_Scorller.inited)
        {
            m_Scorller.OnCompLoad = OnCompLoad;
        }
        HideDefaultCell();
    }
    private void OnValueChangle(Vector2 delta)
    {
        if (onValueChange != null)
        {
            onValueChange(delta);
        }
    }
    private void OnFirstLoadAllEvent()
    {
        if (cacheJump != -1)
        {
            JumpIndex(cacheJump);
        }
    }
    private void HideDefaultCell()
    {
        HideDefaultCell(m_CellHeaderPrefab);
        HideDefaultCell(m_CellNomalPrefab);
        HideDefaultCell(m_CellTailPrefab);
        HideDefaultCell(m_CellExtraPrefab1);
        HideDefaultCell(m_CellExtraPrefab2);
        HideDefaultCell(m_CellExtraPrefab3);
    }
    private void HideDefaultCell(CellView _cell)
    {
        if (_cell != null)
        {
            _cell.SetActive(false);
        }
    }
    List<CellView> list = new List<CellView>();
    private void OnCompLoad()
    {
        inited = true;
        if (_data.Count > 0)
        {
            Restart();
        }
    }
    public void Refresh()
    {
        inited = m_Scorller.inited;
        if (m_Scorller.OnCompLoad == null && !m_Scorller.inited)
        {
            m_Scorller.OnCompLoad = OnCompLoad;
        }
        _data.Clear();
        if (!inited)
        {
            return;
        }
        //假居中恢复尺寸
        if (fakeMiddleCenter && jiaMiddleWithMaxSize != 0)
        {
            var rect = (transform as RectTransform);
            if (horizontal)
            {
                rect.sizeDelta = rect.sizeDelta.SetX(jiaMiddleWithMaxSize);
            }
            else if (vertical)
            {
                rect.sizeDelta = rect.sizeDelta.SetY(jiaMiddleWithMaxSize);
            }
        }
        if (m_Scorller.LayoutGroup != null)
        {
            m_Scorller.LayoutGroup.spacing = m_Scorller.spacing;
        }
        lastScrollPos = horizontal ? mScrollRect.horizontalNormalizedPosition : mScrollRect.verticalNormalizedPosition;
        lastContentSize = m_Scorller._ScrollSize;
        mScrollRect.verticalNormalizedPosition = 1;
        mScrollRect.horizontalNormalizedPosition = 0;
        lastScrollPos = Mathf.Clamp01(lastScrollPos);
    }
    public CellView GetActiveCellView(int _index)
    {
        if (m_Scorller == null)
        {
            return null;
        }
        var _cellView = m_Scorller.GetActiveCellView(_index);
        if (_cellView != null)
        {
            return _cellView as CellView;
        }
        return null;
    }
    public CellView GetActiveCellView(int _index, ScrollerDataType type)
    {
        if (m_Scorller == null)
        {
            return null;
        }
        var views = m_Scorller.GetActiveCellViews();
        for (int i = 0; i < views.Count; i++)
        {
            var _cellView = views[i] as CellView;
            if (views[i].index == _index && _cellView.type == type)
            {
                return _cellView;
            }
        }
        return null;
    }
    public List<CellView> GetActiveCellViews()
    {
        if (m_Scorller == null)
        {
            return null;
        }
        var views = m_Scorller.GetActiveCellViews();
        List<CellView> list = new List<CellView>();
        for (int i = 0; i < views.Count; i++)
        {
            var _cellView = views[i] as CellView;
            list.Add(_cellView);
        }
        return list;
    }
    public void AddCell(ScrollerDataType type, int index, Action<CellView> action = null, CellInfo? info = null)
    {
        ScrollerData data = null;
        switch (type) {
            case ScrollerDataType.Header:
                data = new ScrollerHeaderData(index, action);
                break;
            case ScrollerDataType.Normal:
                data = new ScrollerNormalData(index, action);
                break;
            case ScrollerDataType.Tail:
                data = new ScrollerTailData(index, action);
                break;
            case ScrollerDataType.Extra1:
            case ScrollerDataType.Extra2:
            case ScrollerDataType.Extra3:
                data = new ScrollerExtraData(index, type, action);
                break;
        }
        if (data != null) {
            data.info = info;
            _data.Add(data);
        }
    }
    public void AddCell(ScrollerDataType type, int index, CellInfo info)
    {
        ScrollerData data = null;
        switch (type) {
            case ScrollerDataType.Header:
                data = new ScrollerHeaderData(index);
                break;
            case ScrollerDataType.Normal:
                data = new ScrollerNormalData(index);
                break;
            case ScrollerDataType.Tail:
                data = new ScrollerTailData(index);
                break;
            case ScrollerDataType.Extra1:
            case ScrollerDataType.Extra2:
            case ScrollerDataType.Extra3:
                data = new ScrollerExtraData(index, type);
                break;
        }
        if (data != null) {
            data.info = info;
            _data.Add(data);
        }
    }
    public void OnRefreshAdd()
    {
        Restart();
    }
    public void Restart()
    {
        if (!inited) return;
        m_Scorller.ReloadData();
        if (fakeMiddleCenter && _data.Count <= maxCellCnt)
        {
            //只有设置header时,进行假居中处理才能达到效果
            if (jiaMiddleWithMaxSize == 0)
            {
                jiaMiddleWithMaxSize = (int)m_Scorller.ScrollRectSize;
            }
            var rect = (transform as RectTransform);
            if (horizontal)
            {
                rect.sizeDelta = rect.sizeDelta.SetX(Math.Min(jiaMiddleWithMaxSize, (m_CellHeaderPrefab.height + m_Scorller.spacing) * _data.Count - m_Scorller.spacing));
            }
            else if (vertical)
            {
                rect.sizeDelta = rect.sizeDelta.SetY(Math.Min(jiaMiddleWithMaxSize, (m_CellHeaderPrefab.height + m_Scorller.spacing) * _data.Count - m_Scorller.spacing));
            }
            if (rect.sizeDelta.x < jiaMiddleWithMaxSize)
            {
                mScrollRect.horizontal = false;
                mScrollRect.vertical = false;
            }
            else
            {
                mScrollRect.horizontal = horizontal;
                mScrollRect.vertical = vertical;
            }
        }
        else if (maxCellCnt != 0 && _data.Count <= maxCellCnt)
        {
            mScrollRect.horizontal = false;
            mScrollRect.vertical = false;
        }
        else
        {
            mScrollRect.horizontal = horizontal;
            mScrollRect.vertical = vertical;
        }
        ResetScrollPos();
        lastDataCnt = _data.Count;
    }
    public void JumpIndex(int index)
    {
        if (!inited)
        {
            cacheJump = index;
            return;
        }
        if ((index < 0 || index >= _data.Count) && !m_Scorller.Loop)
        {
            return;
        }
        var _size = m_Scorller.GetScrollPositionForDataIndex(index, EnhancedScroller.CellViewPositionEnum.Before);
        var _contentSize = vertical ? m_Scorller.scrollRect.content.rect.size.y : m_Scorller.scrollRect.content.rect.size.x;
        if (_contentSize - _size < m_Scorller.ScrollRectSize)
        {
            _size = _contentSize - m_Scorller.ScrollRectSize;
        }
        m_Scorller.ScrollPosition = _size;
    }
    public void JumpIndex(int index,ref float offset)
    {
        if (!inited)
        {
            cacheJump = index;
            return;
        }
        if ((index < 0 || index >= _data.Count) && !m_Scorller.Loop)
        {
            return;
        }
        var _size = m_Scorller.GetScrollPositionForDataIndex(index, EnhancedScroller.CellViewPositionEnum.Before);
        var _contentSize = vertical ? m_Scorller.scrollRect.content.rect.size.y : m_Scorller.scrollRect.content.rect.size.x;
        offset = 0f;
        if (_contentSize - _size < m_Scorller.ScrollRectSize)
        {
            offset = _size - (_contentSize - m_Scorller.ScrollRectSize);
            _size = _size - offset;
        }
        m_Scorller.ScrollPosition = _size;
    }
    public float GetCellSize(int _dataIndex)
    {
        if (!inited)
        {
            return 0;
        }
        return m_Scorller.GetCellSize(_dataIndex);
    }
    //滑动过去
    public void MoveToIndex(int index, float _time, EnhancedScroller.TweenType _tweenType)
    {
        if (!inited)
        {
            cacheJump = index;
            return;
        }
        if ((index < 0 || index >= _data.Count) && !m_Scorller.Loop)
        {
            return;
        }
        var _size = m_Scorller.GetScrollPositionForDataIndex(index, EnhancedScroller.CellViewPositionEnum.Before);
        m_Scorller.Tween(_tweenType, _time, _size);
    }
    public void JumpIndex(float _delta, float _time, EnhancedScroller.TweenType _tweenType)
    {
        if (!inited)
        {
            return;
        }
        m_Scorller.Tween(_tweenType, _time, m_Scorller.ScrollPosition + _delta);
    }
    public float GetPreScrollSize(int index)
    {
        float _size = 0;
        for (int i = 0; i < _data.Count; i++) {
            _size += GetCellViewSize(m_Scorller, i);
            if (i >= index) break;
        }
        return _size;
    }
    public void RefreshSingleCellView(int _index)
    {
        CellView _cell;
        if (IsDisplay(_index, out _cell))
        {
            _cell.RefreshCellView();
        }
    }
    public bool IsDisplay(int index, out CellView _cell)
    {
        _cell = null;
        if (m_Scorller == null)
        {
            return false;
        }
        var _base = m_Scorller.GetActiveCellView(index);
        if (_base != null)
        {
            _cell = _base as CellView;
            return true;
        }
        return false;
    }
    public bool IsDisplay(int index)
    {
        if (m_Scorller == null)
        {
            return false;
        }
        return m_Scorller.GetActiveCellView(index) != null;
    }
    public bool OutOfStartView(int index)
    {
        float value=m_Scorller.GetScrollPositionForCellViewIndex(index, EnhancedScroller.CellViewPositionEnum.Before);
        if (value < m_Scorller.ScrollPosition) {
            return true;
        }
        return false;
    }
    private void OnDestroy()
    {
        if (m_Scorller != null)
        {
            m_Scorller.OnFirstLoadAllEvent -= OnFirstLoadAllEvent;
        }
    }
    public void OnRebuildComplete()
    {
        if (OnRefreshCompleteEvent != null)
        {
            OnRefreshCompleteEvent();
        }
    }
    #region 滚动定位
    private EnhanceLockType m_lockType = EnhanceLockType.None;
    public EnhanceLockType lockType {
        get { return m_lockType; }
        set {
            m_lockType = value;
        }
    }
    private float lastContentSize = 0;
    private int lastDataCnt = 0;
    private float lastScrollPos = 0;
    public void ResetScrollPos()
    {
        if (_data.Count == 0 || !inited)
        {
            return;
        }
        switch (lockType) {
            case EnhanceLockType.LockHorizonRight: {
                    mScrollRect.horizontalNormalizedPosition = 1;
                }
                break;
            case EnhanceLockType.LockHorizonLeft: {
                    mScrollRect.horizontalNormalizedPosition = 0;
                }
                break;
            case EnhanceLockType.LockVerticalTop: {
                    mScrollRect.verticalNormalizedPosition = 1;
                }
                break;
            case EnhanceLockType.LockVerticalBottom: {
                    mScrollRect.verticalNormalizedPosition = 0;
                    m_Scorller.ScrollPosition = m_Scorller._ScrollSize;
                }
                break;
            case EnhanceLockType.KeepHorizon: {
                    mScrollRect.horizontalNormalizedPosition = Mathf.Clamp01(lastContentSize * lastScrollPos / m_Scorller._ScrollSize);
                }
                break;
            case EnhanceLockType.KeepVertical2: {
                    if(lastContentSize == m_Scorller._ScrollSize) {
                        mScrollRect.verticalNormalizedPosition = lastScrollPos;
                        m_Scorller.ScrollPosition = (1 - mScrollRect.verticalNormalizedPosition) * m_Scorller._ScrollSize;
                        return;
                    }
                    if (lastScrollPos == 1) {
                        mScrollRect.verticalNormalizedPosition = 1;
                    }
                    mScrollRect.verticalNormalizedPosition = Mathf.Clamp01(lastContentSize * lastScrollPos / m_Scorller._ScrollSize);
                    m_Scorller.ScrollPosition = (1 - mScrollRect.verticalNormalizedPosition) * m_Scorller._ScrollSize;
                }
                break;
            case EnhanceLockType.KeepVertical:
                {
                    if (lastContentSize == m_Scorller._ScrollSize)
                    {
                        mScrollRect.verticalNormalizedPosition = lastScrollPos;
                        m_Scorller.ScrollPosition = (1 - mScrollRect.verticalNormalizedPosition) * m_Scorller._ScrollSize;
                        return;
                    }
                    if (lastScrollPos == 1)
                    {
                        mScrollRect.verticalNormalizedPosition = 1;
                    }
                    mScrollRect.verticalNormalizedPosition = Mathf.Clamp01(1 - lastContentSize * (1 - lastScrollPos) / m_Scorller._ScrollSize);
                    m_Scorller.ScrollPosition = (1 - mScrollRect.verticalNormalizedPosition) * m_Scorller._ScrollSize;
                }
                break;
        }
    }
    #endregion
}
Main/System/Skill/ScrollerController.cs.meta
New file
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: db8a0c384b7dd034fa327df389b01390
timeCreated: 1501125931
licenseType: Free
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Skill/ScrollerData.cs
New file
@@ -0,0 +1,59 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScrollerData {
    public int index;
    public ScrollerDataType m_ScrollerType = ScrollerDataType.Normal;
    public ScrollerData(int index, Action<CellView> action)
    {
        this.index = index;
        OnClick = action;
    }
    public CellInfo? info;
    public Action<CellView> OnClick;
}
public class ScrollerHeaderData : ScrollerData
{
    public ScrollerHeaderData(int index, Action<CellView> action = null) :base(index, action)
    {
        m_ScrollerType = ScrollerDataType.Header;
    }
}
public class ScrollerTailData : ScrollerData
{
    public ScrollerTailData(int index, Action<CellView> action = null) : base(index, action)
    {
        m_ScrollerType = ScrollerDataType.Tail;
    }
}
public class ScrollerNormalData : ScrollerData
{
    public ScrollerNormalData(int index, Action<CellView> action = null) : base(index, action)
    {
        m_ScrollerType = ScrollerDataType.Normal;
    }
}
public class ScrollerExtraData : ScrollerData
{
    public ScrollerExtraData(int index, ScrollerDataType type, Action<CellView> action = null) : base(index, action)
    {
        m_ScrollerType = type;
    }
}
public enum ScrollerDataType
{
    Header,
    Normal,
    Tail,
    Extra1,
    Extra2,
    Extra3,
}
Main/System/Skill/ScrollerData.cs.meta
New file
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 11cfc3c4873e25c4db2a54cd2d565464
timeCreated: 1501126064
licenseType: Free
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Skill/ScrollerUI.cs
New file
@@ -0,0 +1,11 @@
using UnityEngine;
namespace EnhancedUI.EnhancedScroller
{
    public abstract class ScrollerUI : MonoBehaviour
    {
        public ScrollerDataType type=ScrollerDataType.Header;
        public abstract void Refresh(CellView cell);
    }
}
Main/System/Skill/ScrollerUI.cs.meta
New file
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 20a2854509a5a624ebcb26b536632821
timeCreated: 1508552655
licenseType: Free
MonoImporter:
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/WindowBase.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ba02f3312cb5175499b8702894548057
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/WindowBase/WidgetBehavior.cs
New file
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public struct Widget
{
    public string name;
    public GameObject gameObject;
}
public class WidgetBehavior : MonoBehaviour
{
    [HideInInspector]
    public Widget[] widgets;
    protected Dictionary<string, GameObject> dicWidgts;
    private bool hasInitWidgets = false;
    /// <summary>
    /// 在子类合适地方初始化此方法
    /// </summary>
    protected void InitWidgts()
    {
        dicWidgts = new Dictionary<string, GameObject>();
        foreach (var item in widgets)
        {
            if (dicWidgts.ContainsKey(item.name))
                Debug.LogErrorFormat("有重复的控件名称:{0}", item.name);
            dicWidgts.Add(item.name, item.gameObject);
        }
        hasInitWidgets = true;
    }
    public T GetWidgt<T>(string name) where T: Component
    {
        if (!hasInitWidgets)
        {
            Debug.LogError("控件还未初始化!");
            return default(T);
        }
        GameObject go;
        if (dicWidgts.TryGetValue(name, out go))
        {
            return go.GetComponent<T>();
        }
        Debug.LogErrorFormat("找不到控件:{0}", name);
        return default(T);
    }
    public Component GetWidgt(string type, string name)
    {
        if (!hasInitWidgets)
        {
            Debug.LogError("控件还未初始化!");
            return null;
        }
        GameObject go;
        if (dicWidgts.TryGetValue(name, out go))
        {
            return go.GetComponent(type);
        }
        Debug.LogErrorFormat("找不到控件:{0}", name);
        return null;
    }
    public Component GetWidgt(Type type, string name)
    {
        if (!hasInitWidgets)
        {
            Debug.LogError("控件还未初始化!");
            return null;
        }
        GameObject go;
        if (dicWidgts.TryGetValue(name, out go))
        {
            return go.GetComponent(type);
        }
        Debug.LogErrorFormat("找不到控件:{0}", name);
        return null;
    }
}
Main/System/WindowBase/WidgetBehavior.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a829f199a150a544b7022271869a09c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant: