| 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  | 
|     }  | 
| } |