天天看點

NGUI WrapContent

為了解決啟動成就、排名等UI時的卡頓問題,需要用wrapcontent腳本,在網上找了篇部落格,試了試,還行,支援多行單列,多行多列。(多行多列我的做法是用一個空gameobject作為多列子項的父物體)。

具體做法:

首先給UIGrid元件所在物體挂wrapcontent腳本,然後設定minIndex = 0, itemSize = grid.cellHeight, 最後設定重新整理函數onInitializeItem的回調。

這是從其它地方找的一個wrapcontent腳本:

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

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// This script makes it possible for a scroll view to wrap its content, creating endless scroll views.
/// Usage: simply attach this script underneath your scroll view where you would normally place a UIGrid:
/// 
/// + Scroll View
/// |- UIWrappedContent
/// |-- Item 1
/// |-- Item 2
/// |-- Item 3
/// </summary>

[AddComponentMenu("NGUI/Interaction/My Wrap Content")]
[ExecuteInEditMode]
public class MyWrapContent : MonoBehaviour
{
    public delegate void OnInitializeItem(GameObject go, int wrapIndex, int realIndex);

    /// <summary>
    /// Width or height of the child items for positioning purposes.
    /// </summary>

    public int itemSize = ;

    /// <summary>
    /// Whether the content will be automatically culled. Enabling this will improve performance in scroll views that contain a lot of items.
    /// </summary>

    public bool cullContent = true;

    /// <summary>
    /// Minimum allowed index for items. If "min" is equal to "max" then there is no limit.
    /// For vertical scroll views indices increment with the Y position (towards top of the screen).
    /// </summary>

    public int minIndex = ;

    /// <summary>
    /// Maximum allowed index for items. If "min" is equal to "max" then there is no limit.
    /// For vertical scroll views indices increment with the Y position (towards top of the screen).
    /// </summary>

    public int maxIndex = ;

    /// <summary>
    /// Whether hidden game objects will be ignored for the purpose of calculating bounds.
    /// </summary>

    public bool hideInactive = false;

    public bool wrapInCircle = false;

    /// <summary>
    /// Callback that will be called every time an item needs to have its content updated.
    /// The 'wrapIndex' is the index within the child list, and 'realIndex' is the index using position logic.
    /// </summary>

    public OnInitializeItem onInitializeItem;

    protected Transform mTrans;
    protected UIPanel mPanel;
    protected UIScrollView mScroll;
    protected bool mHorizontal = false;
    protected bool mFirstTime = true;
    protected List<Transform> mChildren = new List<Transform>();

    protected List<Transform> MChildren
    {
        get
        {
            if (mChildren == null || mChildren.Count == )
                CacheChild();
            return mChildren;
        }
    }


    /// <summary>
    /// Initialize everything and register a callback with the UIPanel to be notified when the clipping region moves.
    /// </summary>
    bool isStart = false;
    protected virtual void Start()
    {
        if (isStart) return;
        isStart = true;
        SortBasedOnScrollMovement();
        WrapContent();
        if (mScroll != null) mScroll.GetComponent<UIPanel>().onClipMove = OnMove;
        mFirstTime = false;

        adjustWrapPos();
    }

    /// <summary>
    /// Callback triggered by the UIPanel when its clipping region moves (for example when it's being scrolled).
    /// </summary>

    protected virtual void OnMove(UIPanel panel) { WrapContent(); }

    void CacheChild()
    {
        mChildren.Clear();
        for (int i = ; i < transform.childCount; ++i)
        {
            Transform t = transform.GetChild(i);
            if (hideInactive && !t.gameObject.active) continue;
            mChildren.Add(t);
        }
    }

    /// <summary>
    /// Immediately reposition all children.
    /// </summary>

    [ContextMenu("Sort Based on Scroll Movement")]
    public virtual void SortBasedOnScrollMovement()
    {
        if (!CacheScrollView()) return;

        // Cache all children and place them in order
        mChildren.Clear();
        for (int i = ; i < mTrans.childCount; ++i)
        {
            Transform t = mTrans.GetChild(i);
            if (hideInactive && !t.gameObject.active) continue;
            mChildren.Add(t);
        }

        // Sort the list of children so that they are in order
        /*if (mHorizontal) mChildren.Sort(UIGrid.SortHorizontal);
        else mChildren.Sort(UIGrid.SortVertical);*/
        ResetChildPositions();
    }



    /// <summary>
    /// Immediately reposition all children, sorting them alphabetically.
    /// </summary>

    [ContextMenu("Sort Alphabetically")]
    public virtual void SortAlphabetically()
    {
        if (!CacheScrollView()) return;

        // Cache all children and place them in order
        mChildren.Clear();
        for (int i = ; i < mTrans.childCount; ++i)
        {
            Transform t = mTrans.GetChild(i);
            if (hideInactive && !t.gameObject.active) continue;
            mChildren.Add(t);
        }

        // Sort the list of children so that they are in order
        mChildren.Sort(UIGrid.SortByName);
        ResetChildPositions();
    }

    /// <summary>
    /// Cache the scroll view and return 'false' if the scroll view is not found.
    /// </summary>

    protected bool CacheScrollView()
    {
        mTrans = transform;
        mPanel = NGUITools.FindInParents<UIPanel>(gameObject);
        mScroll = mPanel.GetComponent<UIScrollView>();
        if (mScroll == null) return false;
        if (mScroll.movement == UIScrollView.Movement.Horizontal) mHorizontal = true;
        else if (mScroll.movement == UIScrollView.Movement.Vertical) mHorizontal = false;
        else return false;
        return true;
    }

    /// <summary>
    /// Helper function that resets the position of all the children.
    /// </summary>
    [ContextMenu("ResetChildPosition")]
    public virtual void ResetChildPositions()
    {
        if (!Application.isPlaying)
        {
            CacheChild();
            adjustWrapPos();
        }

        for (int i = , imax = MChildren.Count; i < imax; ++i)
        {
            Transform t = MChildren[i];
            Vector3 pos = t.localPosition;
            t.localPosition = mHorizontal ? new Vector3(i * itemSize, pos.y, f) : new Vector3(pos.x, -i * itemSize, f);

            UpdateItem(t, i);
        }
    }

    /// <summary>
    /// 重新整理Item資料
    /// </summary>
    public void RefreshChildData()
    {
        for (int i = , imax = MChildren.Count; i < imax; ++i)
        {
            Transform t = MChildren[i];
            UpdateItem(t, i);
        }
    }

    /// <summary>
    /// Wrap all content, repositioning all children as needed.
    /// </summary>

    public virtual void WrapContent()
    {
        float extents = itemSize * MChildren.Count * f;
        Vector3[] corners = mPanel.worldCorners;

        for (int i = ; i < ; ++i)
        {
            Vector3 v = corners[i];
            v = mTrans.InverseTransformPoint(v);
            corners[i] = v;
        }

        Vector3 center = Vector3.Lerp(corners[], corners[], f);
        bool allWithinRange = true;
        float ext2 = extents * f;

        if (mHorizontal)
        {
            float min = corners[].x - itemSize;
            float max = corners[].x + itemSize;

            for (int i = , imax = MChildren.Count; i < imax; ++i)
            {
                Transform t = MChildren[i];
                float distance = t.localPosition.x - center.x;

                int index = Mathf.RoundToInt(t.localPosition.x / itemSize);
                if (wrapInCircle)
                    index = (index % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
                if (distance < -extents)
                {
                    Vector3 pos = t.localPosition;
                    pos.x += ext2;
                    distance = pos.x - center.x;
                    int realIndex = Mathf.RoundToInt(pos.x / itemSize);

                    if (wrapInCircle)
                        realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;

                    if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
                    {
                        t.localPosition = pos;
                        UpdateItem(t, i);
                    }
                    else allWithinRange = false;
                }
                else if (distance > extents)
                {
                    Vector3 pos = t.localPosition;
                    pos.x -= ext2;
                    distance = pos.x - center.x;
                    int realIndex = Mathf.RoundToInt(pos.x / itemSize);

                    if (wrapInCircle)
                        realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;

                    if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
                    {
                        t.localPosition = pos;
                        UpdateItem(t, i);
                    }
                    else allWithinRange = false;
                }
                else if (mFirstTime) UpdateItem(t, i);

                if (cullContent)
                {
                    distance += mPanel.clipOffset.x - mTrans.localPosition.x;
                    if (!UICamera.IsPressed(t.gameObject))
                        NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
                }
                else
                {
                    NGUITools.SetActive(t.gameObject, true);
                }
                if (!(maxIndex <  && maxIndex < ) && (index < minIndex || index >= maxIndex))
                    NGUITools.SetActive(t.gameObject, false);
            }
        }
        else
        {
            float min = corners[].y - itemSize;
            float max = corners[].y + itemSize;

            for (int i = , imax = MChildren.Count; i < imax; ++i)
            {
                Transform t = MChildren[i];
                float distance = t.localPosition.y - center.y;
                int index = Mathf.RoundToInt(-t.localPosition.y / itemSize);//y軸向下為負軸,是以index要取反
                if (wrapInCircle)
                    index = (index % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;

                if (distance < -extents)
                {
                    Vector3 pos = t.localPosition;
                    pos.y += ext2;
                    distance = pos.y - center.y;
                    int realIndex = Mathf.RoundToInt(-pos.y / itemSize);//y軸向下為負軸,是以index要取反
                    if (wrapInCircle)
                        realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;

                    if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
                    {
                        t.localPosition = pos;
                        UpdateItem(t, i);
                    }
                    else allWithinRange = false;
                }
                else if (distance > extents)
                {
                    Vector3 pos = t.localPosition;
                    pos.y -= ext2;
                    distance = pos.y - center.y;
                    int realIndex = Mathf.RoundToInt(-pos.y / itemSize);//y軸向下為負軸,是以index要取反

                    if (wrapInCircle)
                        realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;

                    if (minIndex == maxIndex || (minIndex <= realIndex && realIndex < maxIndex))
                    {
                        t.localPosition = pos;
                        UpdateItem(t, i);
                    }
                    else allWithinRange = false;
                }
                else if (mFirstTime) UpdateItem(t, i);

                if (cullContent)
                {
                    distance += mPanel.clipOffset.y - mTrans.localPosition.y;
                    if (!UICamera.IsPressed(t.gameObject))
                    {
                        NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
                    }
                }
                else
                {
                    NGUITools.SetActive(t.gameObject, true);
                }
                if (!(maxIndex <  && maxIndex < ) && (index < minIndex || index >= maxIndex))
                    NGUITools.SetActive(t.gameObject, false);
            }
        }

        //Start首次WrapContent的時候會影響ScrollView的restrictWithPanel是以注釋掉。
        //隻要能保證WrapContent還沒有滑到min或maxIndex的時候,底部的下一個Item的active為true,
        //注釋掉這句就不會有什麼影響
        //  mScroll.restrictWithinPanel = !allWithinRange;
        //mScroll.InvalidateBounds();

    }

    /// <summary>
    /// Sanity checks.
    /// </summary>

    void OnValidate()
    {
        if (maxIndex < minIndex)
            maxIndex = minIndex;
        if (minIndex > maxIndex)
            maxIndex = minIndex;

    }

    /// <summary>
    /// Want to update the content of items as they are scrolled? Override this function.
    /// </summary>

    protected virtual void UpdateItem(Transform item, int index)
    {
        int realIndex = getRealIndex(item.localPosition);
        if (!(minIndex <  && maxIndex < ) && (realIndex < minIndex || realIndex >= maxIndex))
            item.gameObject.SetActiveRecursively(false);
        else
        {
            item.gameObject.SetActiveRecursively(true);
            if (onInitializeItem != null)
            {
                onInitializeItem(item.gameObject, index, realIndex);
            }
        }

    }

    /// <summary>
    /// 調整Grid的初始坐标貼邊,為了自适應
    /// </summary>
    [ContextMenu("adjustWrapPos")]
    public void adjustWrapPos()
    {
        if (mScroll == null)
            CacheScrollView();
        Bounds b = NGUIMath.CalculateRelativeWidgetBounds(mScroll.transform, MChildren[], true);
        Vector2 pos = transform.localPosition;
        if (mHorizontal)
        {
            pos.x = -(mPanel.GetViewSize().x / f - mPanel.baseClipRegion.x - b.extents.x - mPanel.clipSoftness.x);
        }
        else
        {
            pos.y = mPanel.GetViewSize().y / f + mPanel.baseClipRegion.y - b.extents.y - mPanel.clipSoftness.y;
        }
        transform.localPosition = pos;
    }

    /// <summary>
    ///根據使用者設定的maxIndex 擷取ScrollView能滑動到的最小坐标 滑倒maxIndex實際對應的是最小坐标
    /// </summary>
    /// <returns></returns>
    public Vector2 getMinSpringPos()
    {
        if (mScroll == null) CacheScrollView();
        Vector2 pos = mScroll.transform.localPosition;

        if (mHorizontal)
        {
            if (minIndex == maxIndex)
                pos.x = mScroll.transform.localPosition.x - mPanel.GetViewSize().x;
            else
                pos.x = -(itemSize * maxIndex - mPanel.GetViewSize().x);
        }
        else
        {
            if (minIndex == maxIndex)
                pos.y = mScroll.transform.localPosition.y + mPanel.GetViewSize().y;
            else
                pos.y = (itemSize * minIndex);
        }

        return pos;
    }

    /// <summary>
    /// 根據使用者設定的minIndex 擷取ScrollView能滑動到的最大坐标 滑倒minIndex實際對應的是最大坐标
    /// </summary>
    /// <returns></returns>
    public Vector2 getMaxSpringPos()
    {
        if (mScroll == null) CacheScrollView();
        Vector2 pos = mScroll.transform.localPosition;
        if (mHorizontal)
        {
            if (minIndex == maxIndex)
                pos.x = mScroll.transform.localPosition.x + mPanel.GetViewSize().x;
            else
                pos.x = -(itemSize * minIndex);
        }
        else
        {
            if (minIndex == maxIndex)
                pos.y = mScroll.transform.localPosition.y - mPanel.GetViewSize().y;
            else
            {
                pos.y = (itemSize * maxIndex - mPanel.GetViewSize().y);
                pos.y = pos.y <  ?  : pos.y;
            }

        }
        return pos;
    }

    public bool couldSpringBack()
    {
        //因為x軸向負軸為變大,y軸向負軸為變小
        if (mHorizontal)
        {
            Vector2 pos = mScroll.transform.localPosition;
            Vector2 minPos = getMinSpringPos();
            return pos.x > minPos.x;
        }
        else
        {
            Vector2 pos = mScroll.transform.localPosition;
            Vector2 maxPos = getMaxSpringPos();
            return pos.y > maxPos.y;
        }

    }

    public bool couldSpringForward()
    {

        if (mHorizontal)
        {
            Vector2 pos = mScroll.transform.localPosition;
            Vector2 maxPos = getMaxSpringPos();
            return pos.x < maxPos.x;
        }
        else
        {
            Vector2 pos = mScroll.transform.localPosition;
            Vector2 minPos = getMinSpringPos();
            return pos.y < minPos.y;
        }
    }


    /// <summary>
    /// 限制ScrollView的滑動邊界
    /// </summary>
    /// <param name="pos"></param>
    public void RoundSpringLimit(ref Vector3 pos)
    {
        Vector2 minLimitPos = getMinSpringPos();
        Vector2 maxLimitPos = getMaxSpringPos();
        pos.x = pos.x < minLimitPos.x ? minLimitPos.x : pos.x;
        pos.x = pos.x > maxLimitPos.x ? maxLimitPos.x : pos.x;

        pos.y = pos.y < minLimitPos.y ? minLimitPos.y : pos.y;
        pos.y = pos.y > maxLimitPos.y ? maxLimitPos.y : pos.y;
    }

    /// <summary>
    /// 擷取一個locl坐标下對應的真實序号
    /// </summary>
    /// <param name="localPos"></param>
    /// <returns></returns>
    public int getRealIndex(Vector3 localPos)
    {

        int realIndex = (mHorizontal) ?
                Mathf.RoundToInt(localPos.x / itemSize) :
                Mathf.RoundToInt(-localPos.y / itemSize);//y軸向下為負軸,是以index要取反
        if (wrapInCircle)
            realIndex = (realIndex % (maxIndex - minIndex) + (maxIndex - minIndex)) % (maxIndex - minIndex) + minIndex;
        return realIndex;
    }

    /// <summary>
    /// 根據localPos擷取對應的序号
    /// </summary>
    /// <param name="localPos"></param>
    /// <returns></returns>
    public int getIndexWithLocalPos(Vector3 localPos)
    {
        return getIndexWithRealIndex(getRealIndex(localPos));
    }

    /// <summary>
    /// 根據真實序号擷取對應的序号
    /// </summary>
    /// <param name="realIndex"></param>
    /// <returns></returns>
    public int getIndexWithRealIndex(int realIndex)
    {
        int index = realIndex % MChildren.Count;
        return index;
    }

    /// <summary>
    /// 擷取一個真實序号對應的locl坐标
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public Vector3 getRealShouldPos(int realIndex)
    {
        if (MChildren.Count == ) return Vector3.zero;

        Vector3 pos = MChildren[].localPosition;
        int realIndex0 = getRealIndex(pos);

        if (mHorizontal)
        {
            pos.x = pos.x + (realIndex - realIndex0) * itemSize;
        }
        else
        {
            pos.y = pos.y - (realIndex - realIndex0) * itemSize;
        }
        return pos;
    }

    public Vector3 getRealIndexShouldSpringPOS(int realIndex)
    {
        if (!mScroll) CacheScrollView();
        Vector3 scrollPos = mScroll.transform.localPosition;
        if (mHorizontal)
        {
            scrollPos.x = -(realIndex * itemSize);
        }
        else
        {
            scrollPos.y = realIndex * itemSize;
        }

        RoundSpringLimit(ref scrollPos);
        return scrollPos;
    }

    /// <summary>
    /// 根據一個真實序号調整目前所有Item的坐标
    /// </summary>
    /// <param name="realIndex"></param>
    public void WrapChildPosWithRealIndex(int realIndex)
    {
        if (!isStart)
            Start();

        int index = getIndexWithRealIndex(realIndex);
        Vector3 targetPos = getRealShouldPos(realIndex);
        Vector3 offset = targetPos - MChildren[index].localPosition;
        for (int i = , Imax = MChildren.Count; i < Imax; i++)
        {
            Vector3 pos = MChildren[i].localPosition;
            pos += offset;
            MChildren[i].localPosition = pos;
        }
        WrapContent();
        RefreshChildData();
    }

    public bool isInBottom()
    {
        float totalLength = itemSize * maxIndex;
        if (mHorizontal)
        {
            return mPanel.clipOffset.x >= (totalLength - mPanel.GetViewSize().x);
        }
        else
        {
            return mPanel.clipOffset.y <= -(totalLength - mPanel.GetViewSize().y);
        }
    }
}
           

繼續閱讀