盖UIPanel承担了UIWidget的富有渲染工作。在滑时更新child的内容。

UIWidget     

NGUI的UIWidget是拥有组件的基类,它承受了储存显示内容,颜色调配,显示深度,显示位置,显示大小,显示角度,显示的大举形形状,归属哪个UIPanel。这就算是UIWidget所而担的始末。在UIWidget的富有子类中还有所以上同等之属性与职责。UIWidget和UIPanel的干异常密切,因为UIPanel承担了UIWidget的持有渲染工作,而UIWidget只是负责了仓储需要渲染数据。所以,在UIWidget在换贴图,材质球,甚至更换UIPanel父节点时它们会马上通知UIPanel说:”我再易配置了,你得又取得自我之渲染数据”。

NGUI长列表优化利器

UIWidget源码分析

打开UIWidget.cs可以看看如下代码:

    [HideInInspector][SerializeField] protected Material mMat;//材质

    [HideInInspector][SerializeField] protected Texture mTex;//贴图

    [HideInInspector][SerializeField] Color mColor = Color.white;//颜色

    [HideInInspector][SerializeField] Pivot mPivot = Pivot.Center;//对齐位置

    [HideInInspector][SerializeField] int mDepth = 0;//深度

    protected Transform mTrans;//坐标转换

    protected UIPanel mPanel;//相应的UIPanel

    protected bool mChanged = true;//是否更改

    protected bool mPlayMode = true;//模式

    Vector3 mDiffPos;//位置差异

    Quaternion mDiffRot;//旋转差异

    Vector3 mDiffScale;//缩放差异

    int mVisibleFlag = -1;//可见标志

    // Widget's generated geometry

    UIGeometry mGeom = new UIGeometry();//多变形实例

优化原理

NGUI3.7.x以上版本 有只新组件 UIWrapContent
,当我们的列表内容很多时不时,可以展开优化。它不是一模一样不良变全部的child,而是只有一定数量的child,在滑行时更新child的情节。

即NGUI3.6.X也闹此组件,不过不健全,比如更新每一样长达渲染未实现,protected virtual void UpdateItem (Transform
item, int index) ,还生未供便民的接口供外部调用。

 

OnFill

    在UIWidget中极要的措施就是是OnFill(),这是创新渲染多边型的艺术。

 /// <summary>

/// Virtual function called by the UIPanel that fills the buffers.
/// </summary>

virtual public void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols) { }

 

UIWrapContent详解

ColorQuad组件

下面是咱封装的一个零部件写 UIColorQuad.cs

/// Author: KK
/// 
using UnityEngine;
using System.Collections;

/// <summary>
/// 纯色正方形,使用两个三角形组成,仅4个顶点
/// </summary>
[ExecuteInEditMode]
[AddComponentMenu("NGUI/AC-Plugins/ColorQuad")]
public class UIColorQuad : UIWidget
{
    /// <summary>
    /// 用于纯色矩形渲染的材质, 独立,不共享
    /// </summary>
    private static Material m_UIColorQuadMaterial = null;  // 静态,唯一,共享

    public override Material material
    {
        get { return UIColorQuad.m_UIColorQuadMaterial; }
    }

    protected override void Awake()
    {
        base.Awake();
    }
    protected override void OnStart()
    {
        base.OnStart();
        mChanged = true;  // Start时让其重新渲染一次,否则在客户端会加载后没东西
    }

    public void SetSize(float _widht, float _height)
    {
        base.width = (int)_widht;
        base.height = (int)_height;
        base.mChanged = true;
    }

    /// <summary>
    /// 负责显示内容,它的工作是填写如何显示,显示什么。就是把需要显示的内容存储在UIWidget
    /// </summary>
    /// <param name="verts"></param>
    /// <param name="uvs">显示的多边形形状</param>
    /// <param name="cols">颜色调配</param>
    public override void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
    {
        // 开始画网格, 顶点, 矩形
        Vector3[] arrVerts = localCorners;  // 直接由4个角组成矩形吧
        for (int i = 0; i < arrVerts.Length; i++)
        {
            verts.Add(arrVerts[i]);
        }

        // 贴图点
        for (int i = 0; i < arrVerts.Length; i++)
        {
            uvs.Add(new Vector2(0, 0));
        }

        // 顶点颜色
        Color pmaColor = NGUITools.ApplyPMA(this.color);  // NGUI PMA
        for (int i = 0; i < arrVerts.Length; i++)
        {
            cols.Add(pmaColor);
        }
    }

    // 创建材质
    void CheckQuadMaterial()
    {
        string szUseShaderName = "Unlit/Premultiplied Colored"; // NGUI的~

        if (UIColorQuad.m_UIColorQuadMaterial == null ||   // 下列情况下重新生成材质
            material == null ||
            material.shader == null ||
            material.shader.name != szUseShaderName
            )
        {
            GameObject.DestroyImmediate(UIColorQuad.m_UIColorQuadMaterial);

            UIColorQuad.m_UIColorQuadMaterial = new Material(Shader.Find(szUseShaderName));
            UIColorQuad.m_UIColorQuadMaterial.name = "UIColorQuadMaterial";

            // 生成一个1点的白色纹理
            Texture2D whiteTex = new Texture2D(1, 1);
            for (int y = 0; y < whiteTex.height; ++y)
            {
                for (int x = 0; x < whiteTex.width; ++x)
                {
                    whiteTex.SetPixel(x, y, new Color(1, 1, 1, 1));
                }
            }
            whiteTex.Apply();
            UIColorQuad.m_UIColorQuadMaterial.SetTexture("_MainTex", whiteTex);
        }
    }

    protected override void OnUpdate()
    {
        base.OnUpdate();

        if (mChanged)
        {
            mChanged = false;
            CheckQuadMaterial();
        }
    }
}

无须循环滚动

设您要极度滚动,那么要设置Range
Limit
,这个界定是于:-最充分数额+1 ~
0
。至于前面的负号,你可以去探望它的兑现原理。比如您同显示20久数,那么范围就是-20+1~0(-19~0)。

bet365娱乐场官网 1

重叠?

如果你的内容之间会现出如下所著之层现象,那是Item Height的价了多少

bet365娱乐场官网 2

这个Item
Height
代表每半独Item之间间隔,这儿不是每个item的万丈,所以恳请设置成和UIGrid的Height一样的价,当然要您是水平滑动,就请求和Cell
Width一样。如果没UIGrid,那么就是安装于item的冲天充分一部分。

bet365娱乐场官网 3

名为转移了?

在运行的时,如果是老版本的NGUI,那么大倒霉之凡,Item的名会让改,这个某些情况下还是发影响的。

倘您无思名字叫改动,打开
NGUI\Scripts\Interaction\UIWrapContent.cs,在 WrapContent
方法,查找 t.name = realIndex.ToString();
并删除。(在ngui3.7.3中一起用四实施,全删除)

 

主要措施

public delegate void OnInitializeItem
(GameObject go, int wrapIndex, int realIndex);

实施渲染的嘱托,DoRender(要渲染之目标,索引[0开始])
真正开始渲染

private void OnInitItem(GameObject go, int wrapindex, int realindex)
{
    var index = Mathf.Abs(realindex);// 取绝对值
    CacheObject2Index[go] = index; 
    if (CheckActive(go, index) && _hasRefresh)
    {
        DoRender(go, index);
    }
}

 

以滚动时调用,更新当前滚动未尾的Item

    protected virtual void UpdateItem(Transform item, int index)
    {
        if (onInitializeItem != null)
        {
            int realIndex = (mScroll.movement == UIScrollView.Movement.Vertical) ?
                Mathf.RoundToInt(item.localPosition.y / itemSize) :
                Mathf.RoundToInt(item.localPosition.x / itemSize);
            onInitializeItem(item.gameObject, index, realIndex);
        }
    } 

UIWrapContent封装

UI结构

动此Help,你的UI结构可以是以下任意一种(注:如果是NGUI3.9.x建使用结构二)

下图左UI结构:
ListPanel落得绑定了UIPanel、UIScrollView,UIWrapContent、UIGrid,即单独生同等叠构造

产图右结构:Scrollview达到绑定了UIPanel,UIScrollview,WrapContent达成绑定了UIGrid和UIWrapContent,分点儿重叠组织

bet365娱乐场官网 4bet365娱乐场官网 5

 

零件源码

以削减代码量,我对UIWrapContent进行了扳平重合封装,代码如下:

需要NGUI3.7.x之后的版本

update log

2015-10-25 增加可以装顺序(从左到右,从上到下)

2016-05-28

   
改掉foreach,减少GC,修改error

已知bug:invertOrder=truebet365娱乐场官网时,有莫名的展现,日后修补。

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

/// <summary>
/// 对NGUI的 UIWrapContent的封装,如果低于NGUI3.7.x,请使用高版本的UIWrapContent替换
/// 目录结构:(NGUI3.9.7)
///     -GameObject绑定 UIPanel,UIScrollView
///         -GameObject绑定 UIWrapContent,[UIGrid]
///             -Item (具体的滑动内容)
/// 使用方法:var WrapContentHelper = UIWrapContentHelper.Create(WrapContent);
/// by 赵青青  
/// </summary>
public class UIWrapContentHelper
{
    public delegate void UIWrapContentRenderDelegate(GameObject obj, int index);
    /// <summary>
    ///obj:要渲染的对象; index:索引,从0开始
    /// </summary>
    public UIWrapContentRenderDelegate OnRenderEvent;

    private int _count;//总数
    private bool _hasRefresh = false;//是否已刷新

    private UIWrapContent _wrapContent;
    private UIPanel _panel;
    private UIScrollView _scrollView;
    private Vector2 _initPanelClipOffset;
    private Vector3 _initPanelLocalPos;
    /// <summary>
    /// 缓存起来上次渲染的对象对应索引
    /// </summary>
    private Dictionary<GameObject, int> CacheObject2Index = new Dictionary<GameObject, int>();

    private UIWrapContentHelper(){}

    private UIWrapContentHelper(UIWrapContent uiWrapContent)
    {
        if (uiWrapContent == null)
        {
            Debug.LogError("UIWrapContentHelper 传入了NULL");
            return;
        }
        _wrapContent = uiWrapContent;
        //_wrapContent.hideInactive = false;
        _wrapContent.onInitializeItem = OnInitItem; //NOTE NGUI 3.7.x以上版本才有此功能
        //NOTE UIPanel 建议挂在UIWrapContent的父级,NGUI3.9.7非父级我这儿出现异怪现象
        _panel = _wrapContent.gameObject.GetComponent<UIPanel>();
        var panelParent = _wrapContent.transform.parent;
        if (_panel == null && panelParent != null)
        {
            _panel = panelParent.GetComponent<UIPanel>();
        }
        if (_panel == null)
        {
            Debug.LogError(uiWrapContent.name + "的父节点没有UIPanel");
            return;
        }
        _scrollView = _panel.GetComponent<UIScrollView>();
        _initPanelClipOffset = _panel.clipOffset;
        _initPanelLocalPos = _panel.cachedTransform.localPosition;
    }

    //初始化数据,Init或Open时调用
    public void ResetScroll()
    {
        if (_panel == null || _wrapContent == null || _scrollView == null)
        {
            Debug.LogWarning("panel or  wrapContent , scrollView is null ");
            return;
        }
        _panel.clipOffset = _initPanelClipOffset;
        _panel.cachedTransform.localPosition = _initPanelLocalPos;

        // 重设组件~索引和位置
        var index = 0;
        foreach (var oChildTransform in _wrapContent.transform)
        {
            var childTransform = (Transform)oChildTransform;
            // NOTE: 横方向未测试
            if (_scrollView.movement == UIScrollView.Movement.Vertical)
            {
                childTransform.SetLocalPositionY(-_wrapContent.itemSize * index);
            }
            else if (_scrollView.movement == UIScrollView.Movement.Horizontal)
            {
                childTransform.SetLocalPositionX(-_wrapContent.itemSize * index);
            }
            CacheObject2Index[childTransform.gameObject] = index;
            index++;
        }

        //fix soft clip panel
        if (_panel.clipping == UIDrawCall.Clipping.SoftClip) _panel.SetDirty();
    }

    /// <summary>
    /// 设置多少项
    /// </summary>
    /// <param name="count"></param>
    /// <param name="invertOrder">是否反转</param>
    private void SetCount(int count, bool invertOrder = false)
    {
        if (_panel == null || _wrapContent == null)
        {
            Debug.LogWarning("panel or  wrapContent is null ");
            return;
        }
        _count = count;
        //TODO: invertOrder有bug ,NGUI 3.7.x有此功能
        //if (invertOrder)
        //{
        //    _wrapContent.minIndex = 0;
        //    _wrapContent.maxIndex = count - 1;
        //}
        //else
        {
            _wrapContent.minIndex = -count + 1;
            _wrapContent.maxIndex = 0;
        }
        //fix: 按字母排序有bug:显示错乱
        //_wrapContent.SortAlphabetically();

        if (_scrollView != null)
        {
            var canDrag = _count >= GetActiveChilds(_wrapContent.transform).Count;
            if (count == 1) canDrag = false;
            _scrollView.restrictWithinPanel = canDrag;
            _scrollView.disableDragIfFits = !canDrag; // 只有一个的时候,不能滑动
        }
    }

    private void OnInitItem(GameObject go, int wrapindex, int realindex)
    {
        var index = Mathf.Abs(realindex);// 取绝对值
        CacheObject2Index[go] = index; 
        if (CheckActive(go, index) && _hasRefresh)
        {
            DoRender(go, index);
        }
    }

    /// <summary>
    /// 检查是否应该隐藏起来
    /// </summary>
    private bool CheckActive(GameObject go, int index)
    {
        bool needActive = index <= (_count - 1);//小于总数才显示
        go.SetActive(needActive);
        return needActive;
    }

    //触发渲染事件
    private void DoRender(GameObject go, int index)
    {
        if (OnRenderEvent == null)
        {
            Debug.LogError("UIWrapContent必须设置RenderFunc!");
            return;
        }
        OnRenderEvent(go, index);
    }

    /// <summary>
    /// 执行刷新,单个单个地渲染
    /// </summary>
    /// <param name="count"></param>
    /// <param name="invertOrder">反转:当有Scrollbar时才设置此值。指scrollbar的拖动方向,反转有bug,需完善</param>
    public void Refresh(int count, bool invertOrder = false)
    {
        SetCount(count, invertOrder);
        //fix:使用GetEnumerator 替代foreach,减少GC
        var enumerator = CacheObject2Index.GetEnumerator();
        while (enumerator.MoveNext())
        {
            if (CheckActive(enumerator.Current.Key, enumerator.Current.Value))
            {
                DoRender(enumerator.Current.Key, enumerator.Current.Value);
            }
        }

        _hasRefresh = true;
    }

    //强制设置scrollview是否可以滑动,
    //fix 前面在SetCount中有设此值,但判断依据不一定
    public void CanDragScrollview(bool canDrag)
    {
        if (_scrollView != null)
        {
            _scrollView.restrictWithinPanel = canDrag;
            _scrollView.disableDragIfFits = !canDrag; // 只有一个的时候,不能滑动
        }
    }

    public static UIWrapContentHelper Create(UIWrapContent uiWrapContent)
    {
        return new UIWrapContentHelper(uiWrapContent);
    }

    // 获取一个Transfrom下所有active=true的child
    public static List<GameObject> GetActiveChilds(Transform parent)
    {
        var list = new List<GameObject>();
        if (parent == null) return list;
        var max = parent.childCount;
        for (int idx = 0; idx < max; idx++)
        {
            var childObj = parent.GetChild(idx).gameObject;
            if (childObj.activeInHierarchy) list.Add(childObj);
        }
        return list;
    }
}

 

零件使用

一经急需每次打开UI时,复位UIScrollView到起状态,请调用 WrapContentHelper.ResetScroll();

OnRenderWrapContent
是有血有肉的渲染逻辑

 

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

public class CUIFriendList : CUINavController
{
    private UIWrapContent WrapContent;
    private CUIWrapContentHelper WrapContentHelper;
    private List<CPartnerVo> CachePartnerVoList;//显示的数据

    //初始化
    public override void OnInit()
    {
        base.OnInit();
        WrapContent = GetControl<UIWrapContent>("ListPanel");
        WrapContentHelper = CUIWrapContentHelper.Create(WrapContent);
        WrapContentHelper.RenderFunc = OnRenderWrapContent;

    }

    //界面打开前播放动画
    public override void BeforeShowTween(object[] onOpenArgs, System.Action doNext)
    {
        //NOTE 重设Scrollview的位置
        WrapContentHelper.ResetScroll();
       RefreshUI();

        //其它的业务逻辑
        base.BeforeShowTween(onOpenArgs, doNext);
    }

    //刷新列表
    private void RefreshUI()
    {
        var max = CachePartnerVoList.Count;//要渲染数据
        WrapContentHelper.Refresh(max);
    }

    //具体的渲染逻辑
    private void OnRenderWrapContent(GameObject gameObj, int idx)
    {
        if (idx >= CachePartnerVoList.Count)
        {
            gameObj.SetActive(false);
            Debug.LogWarning("超出索引");
            return;
        }
        var partnerVo = CachePartnerVoList[idx];
        var trans = gameObj.transform;
        //TODO 执行具体的渲染逻辑 
        //eg
        if(trans == null) return;
        var NameLabel_=trans.FindChild("NameLabel");
        if(NameLabel_)
        {
            NameLabel_.GetComponent<UILabel>().text=partnerVo.Name;
        }
        //.......
    }
}

相关文章