NGUI WrapContent显示多行多列

在游戏中我们时常看到像背包一样用ScrollView来拖动展示一些物品信息,物品一多那么开始克隆的Item就会多这样使得开始打开界面时耗时长,而且在滑动的时候会出现卡顿,滑动不流畅,滑动卡顿的主要原因是我们未将移出UIScrollView的Item设为false,这会导致Panel在重建DrawCall是将所有的Item都算进来,这时在构建Mesh时会很耗时特别是Item比较多的时候,针对这两个问题我们可以用少量的Item循环使用来解决;NGUI本身就给我们提供了这个代码:UIWrapContent,具体用法NGUI有事例,但它们都是单行单列的,这里我简单的说下怎么用UIWrapContent来展示多行多列的。

先来看看效果:

NGUI WrapContent显示多行多列_第1张图片这里循环使用15个Item来显示0~31,拉到底时不能再往下或往上拉。

Ok,进入主题,下面来说说我的做法,先来看看下面代码:

using UnityEngine;

public class MultiRowWrapContent : UIWrapContent
{
    public int RowNum = 3;

    protected override void ResetChildPositions()
    {
        for (int i = 0, imax = mChildren.Count; i < imax; ++i)
        {
            Transform t = mChildren[i];
            t.localPosition = mHorizontal ? new Vector3(i * itemSize, 0f, 0f) : new Vector3((i % RowNum) * itemSize, -(i / RowNum) * itemSize, 0f);
            UpdateItem(t, i);
        }
    }

    protected override void UpdateItem(Transform item, int index)
    {
        if (onInitializeItem != null)
        {
            int yIndex = Mathf.Abs(Mathf.RoundToInt(item.localPosition.y / itemSize));
            int xIndex = Mathf.Abs(Mathf.RoundToInt(item.localPosition.x / itemSize));
            int realIndex = RowNum * yIndex + xIndex;
            onInitializeItem(item.gameObject, index, realIndex);
        }
    }
    //重写这个函数主要是去掉排序
    //if (mHorizontal) mChildren.Sort(UIGrid.SortHorizontal);
	//	else mChildren.Sort(UIGrid.SortVertical);
    public override void SortBasedOnScrollMovement()
    {
        if (!CacheScrollView()) return;

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

        ResetChildPositions();
    }

    public override void WrapContent()
    {
        float extents = itemSize * (mChildren.Count / RowNum) * 0.5f;
        Vector3[] corners = mPanel.worldCorners;

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

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

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

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

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

                    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 (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
        {
            float min = corners[0].y - itemSize;
            float max = corners[2].y + itemSize;

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

                if (distance < -extents)
                {
                    Vector3 pos = t.localPosition;
                    pos.y += ext2;
                    distance = pos.y - center.y;
                    int realIndex = Mathf.RoundToInt(pos.y / itemSize);

                    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);

                    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);
                }
            }
        }
        mScroll.restrictWithinPanel = !allWithinRange;
        mScroll.InvalidateBounds();
    }
}
上面的代码是继承了UIWrapContent并对里面的几个函数进行重写的,主要的改动有:

1、各个Item的位置设置:t.localPosition = mHorizontal ? new Vector3(i * itemSize, 0f, 0f) : new Vector3((i % RowNum) * itemSize, -(i / RowNum) * itemSize, 0f);这里我只做了垂直方向的,(i%RowNum)*itemSize横向坐标,-(i/RowNum)*itemSize纵向坐标;

2、改变了 UpdateItem中realIndex的计算这个是要显示的数据角标:先算出要改变的item在的行号和列号,它要显示的数据的角标=行号*列数+列号;

3、WrapContent()里的float extents = itemSize * mChildren.Count / RowNum * 0.5f;这个是计算所有Item按照每行RowNum个排列纵向长度的一半;
主要就是这三个地方的改动,还有就是我在后显示做了处理,就是最后一行不满RowNum个时,将没有数据可替换的隐藏,当有时显示。代码如下:

void OnInitializeItem(GameObject go, int wrapIndex, int realIndex)
    {
        
        go.name = Mathf.Abs(realIndex).ToString();
        UILabel label = go.transform.FindChild("Label").GetComponent();
        int index = Mathf.Abs(realIndex);
        if (index < nums.Count)
        {
            go.SetActive(true);
            label.text = nums[index].ToString();
        }
        else
        {
            go.SetActive(false);
        }
    }
Ok,到此基本已经好了,这样就实现了多行多列的显示了。


你可能感兴趣的:(NGUI,ngui,uiscrollview,UIWrapContent)