NGUI无限滑动

http://www.unity蛮牛.com/blog-9383-1391.html

最近由于工作需要,就开始研究NGUI滑动。刚开始参考NGUI自带的循环滑动,利用隐藏和显示,提高GPU的渲染,但是效果依旧不是很理想。在同事提醒下,添加缓存列表,不断刷新当前裁剪区域里的数据,最总完成了需求。在网上也参考了很多资料,今天恰好闲下来,就拿出来大家分享下,哈哈。代码附上:

 
主要分为三部分
1.重写UIScrollView和UICustomDragScrollView两个脚本
////////////////////////////////////////////////////////////////////////////////
修改 UIScrollView 为 UICustomScrollView
////////////////////////////////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;

/// <summary>
/// 修改 UIScrollView 为 UICustomScrollView
/// </summary>

public class UICustomDragScrollView : MonoBehaviour
{
    /// <summary>
    /// Reference to the scroll view that will be dragged by the script.
    /// </summary>

    public UICustomScrollView scrollView;

    // Legacy functionality, kept for backwards compatibility. Use 'scrollView' instead.
    [HideInInspector][SerializeField] UICustomScrollView draggablePanel;

    Transform mTrans;
    UICustomScrollView mScroll;
    bool mAutoFind = false;
    bool mStarted = false;

    /// <summary>
    /// Automatically find the scroll view if possible.
    /// </summary>

    void OnEnable ()
    {
        mTrans = transform;

        // Auto-upgrade
        if (scrollView == null && draggablePanel != null)
        {
            scrollView = draggablePanel;
            draggablePanel = null;
        }

        if (mStarted && (mAutoFind || mScroll == null))
            FindScrollView();
    }

    /// <summary>
    /// Find the scroll view.
    /// </summary>

    void Start ()
    {
        mStarted = true;
        FindScrollView();
    }

    /// <summary>
    /// Find the scroll view to work with.
    /// </summary>

    void FindScrollView ()
    {
        // If the scroll view is on a parent, don't try to remember it (as we want it to be dynamic in case of re-parenting)
        UICustomScrollView sv = NGUITools.FindInParents<UICustomScrollView>(mTrans);

        if (scrollView == null)
        {
            scrollView = sv;
            mAutoFind = true;
        }
        else if (scrollView == sv)
        {
            mAutoFind = true;
        }
        mScroll = scrollView;
    }

    /// <summary>
    /// Create a plane on which we will be performing the dragging.
    /// </summary>

    void OnPress (bool pressed)
    {
        // If the scroll view has been set manually, don't try to find it again
        if (mAutoFind && mScroll != scrollView)
        {
            mScroll = scrollView;
            mAutoFind = false;
        }

        if (scrollView && enabled && NGUITools.GetActive(gameObject))
        {
            scrollView.Press(pressed);
            
            if (!pressed && mAutoFind)
            {
                scrollView = NGUITools.FindInParents<UICustomScrollView>(mTrans);
                mScroll = scrollView;
            }
        }
    }

    /// <summary>
    /// Drag the object along the plane.
    /// </summary>

    void OnDrag (Vector2 delta)
    {
        if (scrollView && NGUITools.GetActive(this))
            scrollView.Drag();
    }

    /// <summary>
    /// If the object should support the scroll wheel, do it.
    /// </summary>

    void OnScroll (float delta)
    {
        if (scrollView && NGUITools.GetActive(this))
            scrollView.Scroll(delta);
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
重写UICustomDragScrollView
////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2014 Tasharen Entertainment
//----------------------------------------------

using UnityEngine;

/// <summary>
/// This script, when attached to a panel turns it into a scroll view.
/// You can then attach UIDragScrollView to colliders within to make it draggable.
/// </summary>

public class UICustomScrollView : MonoBehaviour
{
    static public BetterList<UICustomScrollView> list = new BetterList<UICustomScrollView>();

    public enum Movement
    {
        Horizontal,
        Vertical,
        Unrestricted,
        Custom,
    }

    public enum DragEffect
    {
        None,
        Momentum,
        MomentumAndSpring,
    }

    public enum ShowCondition
    {
        Always,
        OnlyIfNeeded,
        WhenDragging,
    }

    public delegate void OnDragFinished ();

    /// <summary>
    /// Type of movement allowed by the scroll view.
    /// </summary>

    public Movement movement = Movement.Horizontal;

    /// <summary>
    /// Effect to apply when dragging.
    /// </summary>

    public DragEffect dragEffect = DragEffect.MomentumAndSpring;

    /// <summary>
    /// Whether the dragging will be restricted to be within the scroll view's bounds.
    /// </summary>

    public bool restrictWithinPanel = true;

    /// <summary>
    /// Whether dragging will be disabled if the contents fit.
    /// </summary>

    public bool disableDragIfFits = false;

    /// <summary>
    /// Whether the drag operation will be started smoothly, or if if it will be precise (but will have a noticeable "jump").
    /// </summary>

    public bool smoothDragStart = true;

    /// <summary>
    /// Whether to use iOS drag emulation, where the content only drags at half the speed of the touch/mouse movement when the content edge is within the clipping area.
    /// </summary>    
    
    public bool iOSDragEmulation = true;

    /// <summary>
    /// Effect the scroll wheel will have on the momentum.
    /// </summary>

    public float scrollWheelFactor = 0.25f;

    /// <summary>
    /// How much momentum gets applied when the press is released after dragging.
    /// </summary>

    public float momentumAmount = 35f;
    
    /// <summary>
    /// Horizontal scrollbar used for visualization.
    /// </summary>

    public UIProgressBar horizontalScrollBar;

    /// <summary>
    /// Vertical scrollbar used for visualization.
    /// </summary>

    public UIProgressBar verticalScrollBar;

    /// <summary>
    /// Condition that must be met for the scroll bars to become visible.
    /// </summary>

    public ShowCondition showScrollBars = ShowCondition.OnlyIfNeeded;

    /// <summary>
    /// Custom movement, if the 'movement' field is set to 'Custom'.
    /// </summary>

    public Vector2 customMovement = new Vector2(1f, 0f);

    /// <summary>
    /// Content's pivot point -- where it originates from by default.
    /// </summary>

    public UIWidget.Pivot contentPivot = UIWidget.Pivot.TopLeft;

    /// <summary>
    /// Event callback to trigger when the drag process finished. Can be used for additional effects, such as centering on some object.
    /// </summary>

    public OnDragFinished onDragFinished;

    // Deprecated functionality. Use 'movement' instead.
    [HideInInspector][SerializeField] Vector3 scale = new Vector3(1f, 0f, 0f);

    // Deprecated functionality. Use 'contentPivot' instead.
    [SerializeField][HideInInspector] Vector2 relativePositionOnReset = Vector2.zero;

    protected Transform mTrans;
    protected UIPanel mPanel;
    protected Plane mPlane;
    protected Vector3 mLastPos;
    protected bool mPressed = false;
    protected Vector3 mMomentum = Vector3.zero;
    protected float mScroll = 0f;
    protected Bounds mBounds;
    protected bool mCalculatedBounds = false;
    protected bool mShouldMove = false;
    protected bool mIgnoreCallbacks = false;
    protected int mDragID = -10;
    protected Vector2 mDragStartOffset = Vector2.zero;
    protected bool mDragStarted = false;

    /// <summary>
    /// Panel that's being dragged.
    /// </summary>

    public UIPanel panel { get { return mPanel; } }

    /// <summary>
    /// Whether the scroll view is being dragged.
    /// </summary>

    public bool isDragging { get { return mPressed && mDragStarted; } }

    /// <summary>
    /// Calculate the bounds used by the widgets.
    /// </summary>

    public virtual Bounds bounds
    {
        get
        {
            //if (!mCalculatedBounds)
            //{
            //    mCalculatedBounds = true;
            //    mTrans = transform;
            //    mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans, true);
            //}
            return mBounds;
        }
        set
        {
            mBounds = value;
        }
    }

    /// <summary>
    /// Whether the scroll view can move horizontally.
    /// </summary>

    public bool canMoveHorizontally
    {
        get
        {
            return movement == Movement.Horizontal ||
                movement == Movement.Unrestricted ||
                (movement == Movement.Custom && customMovement.x != 0f);
        }
    }

    /// <summary>
    /// Whether the scroll view can move vertically.
    /// </summary>

    public bool canMoveVertically
    {
        get
        {
            return movement == Movement.Vertical ||
                movement == Movement.Unrestricted ||
                (movement == Movement.Custom && customMovement.y != 0f);
        }
    }

    /// <summary>
    /// Whether the scroll view should be able to move horizontally (contents don't fit).
    /// </summary>

    public virtual bool shouldMoveHorizontally
    {
        get
        {
            float size = bounds.size.x;
            if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.x * 2f;
            return Mathf.RoundToInt(size - mPanel.width) > 0;
        }
    }

    /// <summary>
    /// Whether the scroll view should be able to move vertically (contents don't fit).
    /// </summary>

    public virtual bool shouldMoveVertically
    {
        get
        {
            float size = bounds.size.y;
            if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.y * 2f;
            return Mathf.RoundToInt(size - mPanel.height) > 0;
        }
    }

    /// <summary>
    /// Whether the contents of the scroll view should actually be draggable depends on whether they currently fit or not.
    /// </summary>

    protected virtual bool shouldMove
    {
        get
        {
            if (!disableDragIfFits) return true;

            if (mPanel == null) mPanel = GetComponent<UIPanel>();
            Vector4 clip = mPanel.finalClipRegion;
            Bounds b = bounds;

            float hx = (clip.z == 0f) ? Screen.width  : clip.z * 0.5f;
            float hy = (clip.w == 0f) ? Screen.height : clip.w * 0.5f;

            if (canMoveHorizontally)
            {
                if (b.min.x < clip.x - hx) return true;
                if (b.max.x > clip.x + hx) return true;
            }

            if (canMoveVertically)
            {
                if (b.min.y < clip.y - hy) return true;
                if (b.max.y > clip.y + hy) return true;
            }
            return false;
        }
    }

    /// <summary>
    /// Current momentum, exposed just in case it's needed.
    /// </summary>

    public Vector3 currentMomentum { get { return mMomentum; } set { mMomentum = value; mShouldMove = true; } }

    /// <summary>
    /// Cache the transform and the panel.
    /// </summary>

    void Awake ()
    {
        mTrans = transform;
        mPanel = GetComponent<UIPanel>();

        if (mPanel.clipping == UIDrawCall.Clipping.None)
            mPanel.clipping = UIDrawCall.Clipping.ConstrainButDontClip;
        
        // Auto-upgrade
        if (movement != Movement.Custom && scale.sqrMagnitude > 0.001f)
        {
            if (scale.x == 1f && scale.y == 0f)
            {
                movement = Movement.Horizontal;
            }
            else if (scale.x == 0f && scale.y == 1f)
            {
                movement = Movement.Vertical;
            }
            else if (scale.x == 1f && scale.y == 1f)
            {
                movement = Movement.Unrestricted;
            }
            else
            {
                movement = Movement.Custom;
                customMovement.x = scale.x;
                customMovement.y = scale.y;
            }
            scale = Vector3.zero;
#if UNITY_EDITOR
            NGUITools.SetDirty(this);
#endif
        }

        // Auto-upgrade
        if (contentPivot == UIWidget.Pivot.TopLeft && relativePositionOnReset != Vector2.zero)
        {
            contentPivot = NGUIMath.GetPivot(new Vector2(relativePositionOnReset.x, 1f - relativePositionOnReset.y));
            relativePositionOnReset = Vector2.zero;
#if UNITY_EDITOR
            NGUITools.SetDirty(this);
#endif
        }
    }

    void OnEnable () { list.Add(this); }
    void OnDisable () { list.Remove(this); }

    /// <summary>
    /// Set the initial drag value and register the listener delegates.
    /// </summary>

    protected virtual void Start ()
    {
        //UpdatePosition();

        if (Application.isPlaying)
        {
            if (horizontalScrollBar != null)
            {
                EventDelegate.Add(horizontalScrollBar.onChange, OnScrollBar);
                horizontalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveHorizontally) ? 1f : 0f;
            }

            if (verticalScrollBar != null)
            {
                EventDelegate.Add(verticalScrollBar.onChange, OnScrollBar);
                verticalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveVertically) ? 1f : 0f;
            }
        }
    }

    /// <summary>
    /// Restrict the scroll view's contents to be within the scroll view's bounds.
    /// </summary>

    public bool RestrictWithinBounds (bool instant) { return RestrictWithinBounds(instant, true, true); }

    /// <summary>
    /// Restrict the scroll view's contents to be within the scroll view's bounds.
    /// </summary>

    public bool RestrictWithinBounds (bool instant, bool horizontal, bool vertical)
    {
        Bounds b = bounds;
        Vector3 constraint = mPanel.CalculateConstrainOffset(b.min, b.max);

        if (!horizontal) constraint.x = 0f;
        if (!vertical) constraint.y = 0f;
       
        if (constraint.sqrMagnitude > 1f)
        {

            if (!instant && dragEffect == DragEffect.MomentumAndSpring)
            {
                // Spring back into place
                Vector3 pos = mTrans.localPosition + constraint;
                pos.x = Mathf.Round(pos.x);
                pos.y = Mathf.Round(pos.y);
                SpringPanel.Begin(mPanel.gameObject, pos, 13f);
            }
            else
            {
                // Jump back into place
                MoveRelative(constraint);
                mMomentum = Vector3.zero;
                mScroll = 0f;
            }
            return true;
        }
        return false;
    }

    /// <summary>
    /// Disable the spring movement.
    /// </summary>

    public void DisableSpring ()
    {
        SpringPanel sp = GetComponent<SpringPanel>();
        if (sp != null) sp.enabled = false;
    }

    /// <summary>
    /// Update the values of the associated scroll bars.
    /// </summary>

    public void UpdateScrollbars () { UpdateScrollbars(true); }

    /// <summary>
    /// Update the values of the associated scroll bars.
    /// </summary>

    public virtual void UpdateScrollbars (bool recalculateBounds)
    {
        if (mPanel == null) return;

        if (horizontalScrollBar != null || verticalScrollBar != null)
        {
            if (recalculateBounds)
            {
                mCalculatedBounds = false;
                mShouldMove = shouldMove;
            }

            Bounds b = bounds;
            Vector2 bmin = b.min;
            Vector2 bmax = b.max;

            if (horizontalScrollBar != null && bmax.x > bmin.x)
            {
                Vector4 clip = mPanel.finalClipRegion;
                int intViewSize = Mathf.RoundToInt(clip.z);
                if ((intViewSize & 1) != 0) intViewSize -= 1;
                float halfViewSize = intViewSize * 0.5f;
                halfViewSize = Mathf.Round(halfViewSize);

                if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
                    halfViewSize -= mPanel.clipSoftness.x;

                float contentSize = bmax.x - bmin.x;
                float viewSize = halfViewSize * 2f;
                float contentMin = bmin.x;
                float contentMax = bmax.x;
                float viewMin = clip.x - halfViewSize;
                float viewMax = clip.x + halfViewSize;

                contentMin = viewMin - contentMin;
                contentMax = contentMax - viewMax;

                UpdateScrollbars(horizontalScrollBar, contentMin, contentMax, contentSize, viewSize, false);
            }

            if (verticalScrollBar != null && bmax.y > bmin.y)
            {
                Vector4 clip = mPanel.finalClipRegion;
                int intViewSize = Mathf.RoundToInt(clip.w);
                if ((intViewSize & 1) != 0) intViewSize -= 1;
                float halfViewSize = intViewSize * 0.5f;
                halfViewSize = Mathf.Round(halfViewSize);

                if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
                    halfViewSize -= mPanel.clipSoftness.y;

                float contentSize = bmax.y - bmin.y;
                float viewSize = halfViewSize * 2f;
                float contentMin = bmin.y;
                float contentMax = bmax.y;
                float viewMin = clip.y - halfViewSize;
                float viewMax = clip.y + halfViewSize;

                contentMin = viewMin - contentMin;
                contentMax = contentMax - viewMax;

                UpdateScrollbars(verticalScrollBar, contentMin, contentMax, contentSize, viewSize, true);
            }
        }
        else if (recalculateBounds)
        {
            mCalculatedBounds = false;
        }
    }

    /// <summary>
    /// Helper function used in UpdateScrollbars(float) function above.
    /// </summary>

    protected void UpdateScrollbars (UIProgressBar slider, float contentMin, float contentMax, float contentSize, float viewSize, bool inverted)
    {
        if (slider == null) return;

        mIgnoreCallbacks = true;
        {
            float contentPadding;

            if (viewSize < contentSize)
            {
                contentMin = Mathf.Clamp01(contentMin / contentSize);
                contentMax = Mathf.Clamp01(contentMax / contentSize);

                contentPadding = contentMin + contentMax;
                slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
                    ((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);
            }
            else
            {
                contentMin = Mathf.Clamp01(-contentMin / contentSize);
                contentMax = Mathf.Clamp01(-contentMax / contentSize);

                contentPadding = contentMin + contentMax;
                slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
                    ((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);

                if (contentSize > 0)
                {
                    contentMin = Mathf.Clamp01(contentMin / contentSize);
                    contentMax = Mathf.Clamp01(contentMax / contentSize);
                    contentPadding = contentMin + contentMax;
                }
            }

            UIScrollBar sb = slider as UIScrollBar;
            if (sb != null) sb.barSize = 1f - contentPadding;
        }
        mIgnoreCallbacks = false;
    }

    /// <summary>
    /// Changes the drag amount of the scroll view to the specified 0-1 range values.
    /// (0, 0) is the top-left corner, (1, 1) is the bottom-right.
    /// </summary>

    public virtual void SetDragAmount (float x, float y, bool updateScrollbars)
    {
        if (mPanel == null) mPanel = GetComponent<UIPanel>();

        DisableSpring();

        Bounds b = bounds;
        if (b.min.x == b.max.x || b.min.y == b.max.y) return;

        Vector4 clip = mPanel.finalClipRegion;

        float hx = clip.z * 0.5f;
        float hy = clip.w * 0.5f;
        float left = b.min.x + hx;
        float right = b.max.x - hx;
        float bottom = b.min.y + hy;
        float top = b.max.y - hy;

        if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
        {
            left -= mPanel.clipSoftness.x;
            right += mPanel.clipSoftness.x;
            bottom -= mPanel.clipSoftness.y;
            top += mPanel.clipSoftness.y;
        }

        // Calculate the offset based on the scroll value
        float ox = Mathf.Lerp(left, right, x);
        float oy = Mathf.Lerp(top, bottom, y);

        // Update the position
        if (!updateScrollbars)
        {
            Vector3 pos = mTrans.localPosition;
            if (canMoveHorizontally) pos.x += clip.x - ox;
            if (canMoveVertically) pos.y += clip.y - oy;
            mTrans.localPosition = pos;
        }

        if (canMoveHorizontally) clip.x = ox;
        if (canMoveVertically) clip.y = oy;

        // Update the clipping offset
        Vector4 cr = mPanel.baseClipRegion;
        mPanel.clipOffset = new Vector2(clip.x - cr.x, clip.y - cr.y);

        // Update the scrollbars, reflecting this change
        if (updateScrollbars) UpdateScrollbars(mDragID == -10);
    }

    /// <summary>
    /// Reset the scroll view's position to the top-left corner.
    /// It's recommended to call this function before AND after you re-populate the scroll view's contents (ex: switching window tabs).
    /// Another option is to populate the scroll view's contents, reset its position, then call this function to reposition the clipping.
    /// </summary>

    [ContextMenu("Reset Clipping Position")]
    public void ResetPosition()
    {
        if (NGUITools.GetActive(this))
        {
            // Invalidate the bounds
            mCalculatedBounds = false;
            Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);

            // First move the position back to where it would be if the scroll bars got reset to zero
            SetDragAmount(pv.x, 1f - pv.y, false);

            // Next move the clipping area back and update the scroll bars
            SetDragAmount(pv.x, 1f - pv.y, true);
        }
    }

    /// <summary>
    /// Call this function after you adjust the scroll view's bounds if you want it to maintain the current scrolled position
    /// </summary>

    public void UpdatePosition ()
    {
        if (!mIgnoreCallbacks && (horizontalScrollBar != null || verticalScrollBar != null))
        {
            mIgnoreCallbacks = true;
            mCalculatedBounds = false;
            Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);
            float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : pv.x;
            float y = (verticalScrollBar != null) ? verticalScrollBar.value : 1f - pv.y;
            SetDragAmount(x, y, false);
            UpdateScrollbars(true);
            mIgnoreCallbacks = false;
        }
    }

    /// <summary>
    /// Triggered by the scroll bars when they change.
    /// </summary>

    public void OnScrollBar ()
    {
        if (!mIgnoreCallbacks)
        {
            mIgnoreCallbacks = true;
            float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : 0f;
            float y = (verticalScrollBar != null) ? verticalScrollBar.value : 0f;
            SetDragAmount(x, y, false);
            mIgnoreCallbacks = false;
        }
    }

    /// <summary>
    /// Move the scroll view by the specified amount.
    /// </summary>

    public virtual void MoveRelative (Vector3 relative)
    {
        mTrans.localPosition += relative;
        Vector2 co = mPanel.clipOffset;
        co.x -= relative.x;
        co.y -= relative.y;
        mPanel.clipOffset = co;

        // Update the scroll bars
        UpdateScrollbars(false);
    }

    /// <summary>
    /// Move the scroll view by the specified amount.
    /// </summary>

    public void MoveAbsolute (Vector3 absolute)
    {
        Vector3 a = mTrans.InverseTransformPoint(absolute);
        Vector3 b = mTrans.InverseTransformPoint(Vector3.zero);
        MoveRelative(a - b);
    }

    /// <summary>
    /// Create a plane on which we will be performing the dragging.
    /// </summary>

    public void Press (bool pressed)
    {
        if (smoothDragStart && pressed)
        {
            mDragStarted = false;
            mDragStartOffset = Vector2.zero;
        }

        if (enabled && NGUITools.GetActive(gameObject))
        {
            if (!pressed && mDragID == UICamera.currentTouchID) mDragID = -10;

            mCalculatedBounds = false;
            mShouldMove = shouldMove;
            if (!mShouldMove) return;
            mPressed = pressed;

            if (pressed)
            {
                // Remove all momentum on press
                mMomentum = Vector3.zero;
                mScroll = 0f;

                // Disable the spring movement
                DisableSpring();

                // Remember the hit position
                mLastPos = UICamera.lastHit.point;

                // Create the plane to drag along
                mPlane = new Plane(mTrans.rotation * Vector3.back, mLastPos);

                // Ensure that we're working with whole numbers, keeping everything pixel-perfect
                Vector2 co = mPanel.clipOffset;
                co.x = Mathf.Round(co.x);
                co.y = Mathf.Round(co.y);
                mPanel.clipOffset = co;

                Vector3 v = mTrans.localPosition;
                v.x = Mathf.Round(v.x);
                v.y = Mathf.Round(v.y);
                mTrans.localPosition = v;
            }
            else
            {
                if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None && dragEffect == DragEffect.MomentumAndSpring)
                    RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);

                if (onDragFinished != null)
                    onDragFinished();
            }
        }
    }

    /// <summary>
    /// Drag the object along the plane.
    /// </summary>

    public void Drag ()
    {
        if (enabled && NGUITools.GetActive(gameObject) && mShouldMove)
        {
            if (mDragID == -10) mDragID = UICamera.currentTouchID;
            UICamera.currentTouch.clickNotification = UICamera.ClickNotification.BasedOnDelta;

            // Prevents the drag "jump". Contributed by 'mixd' from the Tasharen forums.
            if (smoothDragStart && !mDragStarted)
            {
                mDragStarted = true;
                mDragStartOffset = UICamera.currentTouch.totalDelta;
            }

            Ray ray = smoothDragStart ?
                UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos - mDragStartOffset) :
                UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos);

            float dist = 0f;

            if (mPlane.Raycast(ray, out dist))
            {
                Vector3 currentPos = ray.GetPoint(dist);
                Vector3 offset = currentPos - mLastPos;
                mLastPos = currentPos;

                if (offset.x != 0f || offset.y != 0f || offset.z != 0f)
                {
                    offset = mTrans.InverseTransformDirection(offset);

                    if (movement == Movement.Horizontal)
                    {
                        offset.y = 0f;
                        offset.z = 0f;
                    }
                    else if (movement == Movement.Vertical)
                    {
                        offset.x = 0f;
                        offset.z = 0f;
                    }
                    else if (movement == Movement.Unrestricted)
                    {
                        offset.z = 0f;
                    }
                    else
                    {
                        offset.Scale((Vector3)customMovement);
                    }
                    offset = mTrans.TransformDirection(offset);
                }

                // Adjust the momentum
                mMomentum = Vector3.Lerp(mMomentum, mMomentum + offset * (0.01f * momentumAmount), 0.67f);

                // Move the scroll view
            
                if (!iOSDragEmulation || dragEffect != DragEffect.MomentumAndSpring)
                {
                    MoveAbsolute(offset);    
                }
                else
                {
                    Vector3 constraint = mPanel.CalculateConstrainOffset(bounds.min, bounds.max);

                    if (constraint.magnitude > 1f)
                    {
                        MoveAbsolute(offset * 0.5f);
                        mMomentum *= 0.5f;
                    }
                    else
                    {
                        MoveAbsolute(offset);
                    }
                }

                // We want to constrain the UI to be within bounds
                if (restrictWithinPanel &&
                    mPanel.clipping != UIDrawCall.Clipping.None &&
                    dragEffect != DragEffect.MomentumAndSpring)
                {
                    RestrictWithinBounds(true, canMoveHorizontally, canMoveVertically);
                }
            }
        }
    }

    /// <summary>
    /// If the object should support the scroll wheel, do it.
    /// </summary>

    public void Scroll (float delta)
    {
        if (enabled && NGUITools.GetActive(gameObject) && scrollWheelFactor != 0f)
        {
            DisableSpring();
            mShouldMove = shouldMove;
            if (Mathf.Sign(mScroll) != Mathf.Sign(delta)) mScroll = 0f;
            mScroll += delta * scrollWheelFactor;
        }
    }

    /// <summary>
    /// Apply the dragging momentum.
    /// </summary>

    void LateUpdate ()
    {
        if (!Application.isPlaying) return;
        float delta = RealTime.deltaTime;

        // Fade the scroll bars if needed
        if (showScrollBars != ShowCondition.Always && (verticalScrollBar || horizontalScrollBar))
        {
            bool vertical = false;
            bool horizontal = false;

            if (showScrollBars != ShowCondition.WhenDragging || mDragID != -10 || mMomentum.magnitude > 0.01f)
            {
                vertical = shouldMoveVertically;
                horizontal = shouldMoveHorizontally;
            }

            if (verticalScrollBar)
            {
                float alpha = verticalScrollBar.alpha;
                alpha += vertical ? delta * 6f : -delta * 3f;
                alpha = Mathf.Clamp01(alpha);
                if (verticalScrollBar.alpha != alpha) verticalScrollBar.alpha = alpha;
            }

            if (horizontalScrollBar)
            {
                float alpha = horizontalScrollBar.alpha;
                alpha += horizontal ? delta * 6f : -delta * 3f;
                alpha = Mathf.Clamp01(alpha);
                if (horizontalScrollBar.alpha != alpha) horizontalScrollBar.alpha = alpha;
            }
        }

        // Apply momentum
        if (mShouldMove && !mPressed)
        {
            if (movement == Movement.Horizontal)
            {
                mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, 0f, 0f));
            }
            else if (movement == Movement.Vertical)
            {
                mMomentum -= mTrans.TransformDirection(new Vector3(0f, mScroll * 0.05f, 0f));
            }
            else if (movement == Movement.Unrestricted)
            {
                mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, mScroll * 0.05f, 0f));
            }
            else
            {
                mMomentum -= mTrans.TransformDirection(new Vector3(
                    mScroll * customMovement.x * 0.05f,
                    mScroll * customMovement.y * 0.05f, 0f));
            }

            if (mMomentum.magnitude > 0.0001f)
            {
                mScroll = NGUIMath.SpringLerp(mScroll, 0f, 20f, delta);

                // Move the scroll view
                Vector3 offset = NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
                MoveAbsolute(offset);

                // Restrict the contents to be within the scroll view's bounds
                if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
                    RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
                
                if (mMomentum.magnitude < 0.0001f && onDragFinished != null) 
                    onDragFinished();
                
                return;
            }
            else
            {
                mScroll = 0f;
                mMomentum = Vector3.zero;
            }
        }
        else mScroll = 0f;

        // Dampen the momentum
        NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
    }

#if UNITY_EDITOR

    /// <summary>
    /// Draw a visible orange outline of the bounds.
    /// </summary>

    void OnDrawGizmos ()
    {
        if (mPanel != null)
        {
            if (!Application.isPlaying) mCalculatedBounds = false;
            Bounds b = bounds;
            Gizmos.matrix = transform.localToWorldMatrix;
            Gizmos.color = new Color(1f, 0.4f, 0f);
            Gizmos.DrawWireCube(new Vector3(b.center.x, b.center.y, b.min.z), new Vector3(b.size.x, b.size.y, 0f));
        }
    }
#endif
}

2.核心脚本NGUIDynamicScrollBase ,计算滑动和播放位移动画

[code]csharpcode:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// 扩展NGUIScroll滑动类,需要继承该类进行开发
/// </summary>
public abstract  class NGUIDynamicScrollBase : MonoBehaviour
{

    //每个列表项数据初始化
    abstract protected void ResetItemData(GameObject go, int index);
    public UIPanel panel;
    public UIGrid grid;
    //目标prefab
    public GameObject prefab;
    //宽度
    public int cellHeight = 60;
    //高度
    public int cellWidth = 700;
    //裁剪区的高度
    private float m_height;
    //裁剪区的宽度
    private int m_maxLine;
    //当前滑动的列表
    protected GameObject[] m_cellList;
    //当前需要滑动的列表总数
    private int m_dataListCount;
    //自定义滑动
    private UICustomScrollView mDrag;
    //最后一次的滑动位置
    private float lastY = -1;
    private Vector3 defaultVec;

    // Use this for initialization
    protected void BaseOnEnable()
    {
        GetConfiguration();
    }

    // Update is called once per frame
    protected void BaseUpdate()
    {
        if (panel.transform.localPosition.y != lastY)
        {
            Validate();
            lastY = panel.transform.localPosition.y;
        }
    }
    //设置当前列表中的
    protected int DataListCount
    {
        get { return m_dataListCount; }

        set{
            m_dataListCount = value;
            
            AddItem(m_dataListCount);
            PlayMoveAnimation(m_cellList);
        }
    }



    #region private Functions

    //初始化配置数据
    private void GetConfiguration()
    {
        //物体默认位置
        defaultVec = new Vector3(0, cellHeight, 0);
        //裁剪区域的高度
        m_height = panel.height;
        //裁剪区域中最多显示的cellItem数量
        m_maxLine = Mathf.CeilToInt(m_height / cellHeight) + 1;
        //初始化CellList
        m_cellList = new GameObject[m_maxLine];
        //创建Item,默认为不可显示状态
        CreateItem();
    }
    //创建Item
    private void CreateItem()
    {
        for (int i = 0; i < m_maxLine; i++)
        {
            GameObject go = null;
            go = (GameObject)Instantiate(prefab);
            go.gameObject.SetActive(false);
            go.GetComponent<UICustomDragScrollView>().scrollView = panel.GetComponent<UICustomScrollView>();
            AddChild(grid.gameObject, go);
            go.transform.localScale = Vector3.one;
            go.transform.localPosition = new Vector3(cellWidth * AllScale.ResolutionScale, -i * cellHeight, 0);
            m_cellList[i] = go;
            go.gameObject.SetActive(false);
        }
    }


    //验证当前区域中的需要显示的CellItem
    private void Validate()
    {
        Vector3 position = panel.transform.localPosition;

        float _ver = Mathf.Max(position.y, 0);

        int startIndex = Mathf.FloorToInt(_ver / cellHeight);

        int endIndex = Mathf.Min(DataListCount, startIndex + m_maxLine);

        GameObject cell;
        int index = 0;
        for (int i = startIndex; i < startIndex + m_maxLine; i++)
        {
            cell = m_cellList[index];

            if (i < endIndex)
            {
                //开始渲染
                cell.gameObject.SetActive(true);
                //重新填充数据
                ResetItemData(cell, i);
                //改变位置
                cell.transform.localPosition = new Vector3(cell.transform.localPosition.x, i * -cellHeight, 0);
                cell.name = "Item_" + index;
            }
            else
            {
                cell.transform.localPosition = defaultVec;
              //  cell.gameObject.SetActive(false);
            }

            index++;
        }
    }

    //重新计算包围合的大小
    private void UpdateBounds(int count)
    {
        Vector3 vMin = new Vector3();
        vMin.x = -grid.transform.localPosition.x;
        vMin.y = grid.transform.localPosition.y - count * cellHeight;
        vMin.z = grid.transform.localPosition.z;
        Bounds b = new Bounds(vMin, Vector3.one);
        b.Encapsulate(grid.transform.localPosition);
        if (mDrag == null) mDrag = panel.GetComponent<UICustomScrollView>();
        mDrag.bounds = b;
        mDrag.UpdateScrollbars(true);
        mDrag.RestrictWithinBounds(true);
    }


    //根据新的数量来重新绘制
    private void AddItem(int count)
    {
        Validate();
        UpdateBounds(count);
    }
    
    //增加孩子节点
    void AddChild(GameObject parent, GameObject go)
    {
        Transform t = go.transform;
        t.parent = parent.transform;
        t.localPosition = Vector3.zero;
        t.localRotation = Quaternion.identity;
        t.localScale = Vector3.one;
        go.layer = parent.layer;
    }

    //播放开始加载的位移动画
    void PlayMoveAnimation(GameObject[] list)
    {
        Vector3 to;
        Vector3 from;
        for (int i = 0; i < list.Length; i++)
        {
            from = list[i].transform.localPosition;
            from = new Vector3(cellWidth*AllScale.ResolutionScale, from.y, 0);
            to = new Vector3(0, from.y, 0);
            list[i].transform.localPosition = from;
            TweenPosition tp = TweenPosition.Begin(list[i], 0.8f, to);
            tp.delay = 0.1f;
            tp.from = from;
            tp.to = to;
            tp.duration = (i + 2) * 0.1f;
            tp.method = UITweener.Method.EaseIn;
        }
    }
    #endregion


   
}
3.将列表中的Prefab设置为向上对齐