UIWrapContent(NGUI长列表优化利器)

来源:互联网 发布:淘宝联盟pid怎么设置 编辑:程序博客网 时间:2024/06/13 23:55

UIWrapContent(NGUI长列表优化利器)

文章目录[点击展开](?)[+]


NGUI长列表优化利器

优化原理

NGUI3.7.x以上版本 有个新组件 UIWrapContent ,当我们的列表内容很多时,可以进行优化。它不是一次生成全部的child,而是只有固定数量的child,在滑动时更新child的内容。

当前NGUI3.6.X也有此组件,不过不完善,比如更新每一条渲染未实现,protected virtual void UpdateItem (Transform item, int index) ,还有未提供便捷的接口供外部调用。

 

UIWrapContent详解

无需循环滚动

如果你需要无限滚动,那么请设置Range Limit,这个范围是在:-最大数量+1 ~ 0。至于前面的负号,你可以去看看它的实现原理。比如你共显示20条数据,那么范围就是-20+1~0(-19~0)。

image

重叠?

如果你的内容之间会出现如下所示的重叠现象,那是Item Height的值过小

image

这个Item Height表示每两个Item之间间隔,这儿不是每个item的高度,所以请设置成和UIGrid的Height一样的值,当然如果你是水平滑动,就请和Cell Width一样。如果没有UIGrid,那么就设置比item的高度大一些。

image

名字被改了?

在运行的时候,如果是老版本的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,分两层结构

imageimage

 

组件源码

为了减少代码量,我对UIWrapContent进行了一层封装,代码如下:

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

update log

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

2016-05-28

    改掉foreach,减少GC,修改error

已知bug:invertOrder=true时,有莫名的表现,日后修复。

复制代码
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;        }        //.......    }}
复制代码
原创粉丝点击