NGUI UIScrollView - 大量item子项的性能优化

NGUI UIScrollView - 大量item子项的性能优化

一、当UIScrollView的以下的包括的子项太多(二三十个之上)时。它的滚动就会变的有些卡不流畅,尤其是在手机上。

对些网上也有非常多的优化它的相关,以下是我的一个优化:

1、将在超出裁剪框的一个item的距离的item,从scrollview中销毁掉 。

当它将要出如今裁剪框中时,再将它构造出来。-- 大家好你都是这么做的。

2、为避免频繁的构造、销毁,导致频繁的分配内存和产生大量的内存垃圾内,导致的性能问题。我添加了一个对象池来管理item的构造与移除工作。

3、scrollvew中的元素在普通情况下,当中的item是要求等距的。假设它的大小不一样,它们的距离就会參差不齐。而这个问题在我的优化中是不存在的。

4、最主要是代码量少、使用简单、扩展方便。

二、话不多说,上代码:

1、主类:Lzh_LoopScrollView.cs

/*

* 描术:

*

* 作者:AnYuanLzh

* 时间:2014-xx-xx

*/

using UnityEngine;

using System.Collections.Generic;

///

/// 这个类主要做了一件事,就是优化了,NGUI UIScrollView 在数据量非常多都时候,

/// 创建过多都GameObject对象,造成资源浪费.

///

public class Lzh_LoopScrollView : MonoBehaviour

{

public enum ArrangeDirection

{

Left_to_Right,

Right_to_Left,

Up_to_Down,

Down_to_Up,

}

///

/// items的排列方式

///

public ArrangeDirection arrangeDirection = ArrangeDirection.Up_to_Down;

///

/// 列表单项模板

///

public GameObject itemPrefab;

///

/// The items list.

///

public List itemsList;

///

/// The datas list.

///

public List datasList;

///

/// 列表脚本

///

public UIScrollView scrollView;

public GameObject itemParent;

///

/// itemsList的第一个元素

///

Lzh_LoopItemObject firstItem;

///

/// itemsList的最后一个元素

///

Lzh_LoopItemObject lastItem;

public delegate void DelegateHandler(Lzh_LoopItemObject item, Lzh_LoopItemData data);

///

/// 响应

///

public DelegateHandler OnItemInit;

///

/// 第一item的起始位置

///

public Vector3 itemStartPos = Vector3.zero;

///

/// 菜单项间隙

///

public float gapDis = 0f;

// 对象池

// 再次优化,频繁的创建与销毁

Queue itemLoop = new Queue();

void Awake()

{

if(itemPrefab==null || scrollView==null || itemParent==null)

{

Debug.LogError("Lzh_LoopScrollView.Awake() 有属性没有在inspector中赋值");

}

// 设置scrollview的movement

if(arrangeDirection == ArrangeDirection.Up_to_Down ||

arrangeDirection == ArrangeDirection.Down_to_Up)

{

scrollView.movement = UIScrollView.Movement.Vertical;

}

else

{

scrollView.movement = UIScrollView.Movement.Horizontal;

}

}

// Update is called once per frame

void Update ()

{

//if(scrollView.isDragging)

{

Validate();

}

}

///

/// 检验items的两端是否要补上或删除

///

void Validate()

{

if( datasList==null || datasList.Count==0)

{

return;

}

// 假设itemsList还不存在

if(itemsList==null || itemsList.Count==0)

{

itemsList = new List();

Lzh_LoopItemObject item = GetItemFromLoop();

InitItem(item, 0, datasList[0]);

firstItem = lastItem = item;

itemsList.Add(item);

//Validate();

}

//

bool all_invisible = true;

foreach(Lzh_LoopItemObject item in itemsList)

{

if(item.widget.isVisible==true)

{

all_invisible=false;

}

}

if (all_invisible == true)

return;

// 先推断前端是否要增减

if(firstItem.widget.isVisible)

{

// 推断要不要在它的前面补充一个item

if(firstItem.dataIndex>0)

{

Lzh_LoopItemObject item = GetItemFromLoop();

// 初化:数据索引、大小、位置、显示

int index = firstItem.dataIndex-1;

//InitItem(item, index, datasList[index]);

AddToFront(firstItem, item, index, datasList[index]);

firstItem = item;

itemsList.Insert(0,item);

//Validate();

}

}

else

{

// 推断要不要将它移除

// 条件:自身是不可见的;且它后一个item也是不可见的(或被被裁剪过半的).

// 这有个隐含条件是itemsList.Count>=2.

if(itemsList.Count>=2

&& itemsList[0].widget.isVisible==false

&& itemsList[1].widget.isVisible==false)

{

itemsList.Remove(firstItem);

PutItemToLoop(firstItem);

firstItem = itemsList[0];

//Validate();

}

}

// 再推断后端是否要增减

if(lastItem.widget.isVisible)

{

// 推断要不要在它的后面补充一个item

if(lastItem.dataIndex < datasList.Count-1)

{

Lzh_LoopItemObject item = GetItemFromLoop();

// 初化:数据索引、大小、位置、显示

int index = lastItem.dataIndex+1;

AddToBack(lastItem, item, index, datasList[index]);

lastItem = item;

itemsList.Add(item);

//Validate();

}

}

else

{

// 推断要不要将它移除

// 条件:自身是不可见的;且它前一个item也是不可见的(或被被裁剪过半的).

// 这有个隐含条件是itemsList.Count>=2.

if(itemsList.Count>=2

&& itemsList[itemsList.Count-1].widget.isVisible==false

&& itemsList[itemsList.Count-2].widget.isVisible==false)

{

itemsList.Remove(lastItem);

PutItemToLoop(lastItem);

lastItem = itemsList[itemsList.Count-1];

//Validate();

}

}

}

///

/// Init the specified datas.

///

/// Datas.

public void Init(List datas, DelegateHandler onItemInitCallback)

{

datasList = datas;

this.OnItemInit = onItemInitCallback;

Validate();

}

///

/// 构造一个 item 对象

///

/// The item.

Lzh_LoopItemObject CreateItem()

{

GameObject go = NGUITools.AddChild(itemParent,itemPrefab);

UIWidget widget = go.GetComponent();

Lzh_LoopItemObject item = new Lzh_LoopItemObject();

item.widget = widget;

go.SetActive(true);

return item;

}

///

/// 用数据列表来初始化scrollview

///

/// Item.

/// Index data.

/// Data.

void InitItem(Lzh_LoopItemObject item, int dataIndex, Lzh_LoopItemData data)

{

item.dataIndex = dataIndex;

if(OnItemInit!=null)

{

OnItemInit(item, data);

}

item.widget.transform.localPosition = itemStartPos;

}

///

/// 在itemsList前面补上一个item

///

void AddToFront(Lzh_LoopItemObject priorItem, Lzh_LoopItemObject newItem, int newIndex, Lzh_LoopItemData newData)

{

InitItem (newItem, newIndex, newData);

// 计算新item的位置

if(scrollView.movement == UIScrollView.Movement.Vertical)

{

float offsetY = priorItem.widget.height*0.5f + gapDis + newItem.widget.height*0.5f;

if(arrangeDirection == ArrangeDirection.Down_to_Up) offsetY *=-1f;

newItem.widget.transform.localPosition = priorItem.widget.cachedTransform.localPosition + new Vector3(0f, offsetY, 0f);

}

else

{

float offsetX = priorItem.widget.width*0.5f + gapDis + newItem.widget.width*0.5f;

if(arrangeDirection == ArrangeDirection.Right_to_Left) offsetX *=-1f;

newItem.widget.transform.localPosition = priorItem.widget.cachedTransform.localPosition - new Vector3(offsetX, 0f, 0f);

}

}

///

/// 在itemsList后面补上一个item

///

void AddToBack(Lzh_LoopItemObject backItem, Lzh_LoopItemObject newItem, int newIndex, Lzh_LoopItemData newData)

{

InitItem (newItem, newIndex, newData);

// 计算新item的位置

if(scrollView.movement == UIScrollView.Movement.Vertical)

{

float offsetY = backItem.widget.height*0.5f + gapDis + newItem.widget.height*0.5f;

if(arrangeDirection == ArrangeDirection.Down_to_Up) offsetY *=-1f;

newItem.widget.transform.localPosition = backItem.widget.cachedTransform.localPosition - new Vector3(0f, offsetY, 0f);

}

else

{

float offsetX = backItem.widget.width*0.5f + gapDis + newItem.widget.width*0.5f;

if(arrangeDirection == ArrangeDirection.Right_to_Left) offsetX *=-1f;

newItem.widget.transform.localPosition = backItem.widget.cachedTransform.localPosition + new Vector3(offsetX, 0f, 0f);

}

}

#region 对象池性能相关

///

/// 从对象池中取行一个item

///

/// The item from loop.

Lzh_LoopItemObject GetItemFromLoop()

{

Lzh_LoopItemObject item;

if(itemLoop.Count<=0)

{

item = CreateItem();

}

else

{

item = itemLoop.Dequeue();

}

item.widget.gameObject.SetActive(true);

return item;

}

///

/// 将要移除的item放入对象池中

/// --这个里我保证这个对象池中存在的对象不超过3个

///

/// Item.

void PutItemToLoop(Lzh_LoopItemObject item)

{

if(itemLoop.Count>=3)

{

Destroy(item.widget.gameObject);

return;

}

item.dataIndex = -1;

item.widget.gameObject.SetActive(false);

itemLoop.Enqueue(item);

}

#endregion

}

2、item对像的封装类:Lzh_LoopItemObject。不要求详细的item类来继承它,但我们要示详细的item对像一定要包括UIWidget组件。

/*

* 描术:

*

* 作者:AnYuanLzh

* 时间:2014-xx-xx

*/

using UnityEngine;

using System.Collections;

///

/// item对像的封装类Lzh_LoopItemObject。不要求详细的item类来继承它。

/// 但我们要示详细的item对像一定要包括UIWidget组件。

///

[System.Serializable]

public class Lzh_LoopItemObject

{

///

/// The widget.

///

public UIWidget widget;

///

/// 本item。在实际整个scrollview中的索引位置,

/// 即对就数据。在数据列表中的索引

///

public int dataIndex= -1;

}

3、与item对关联的数据类:Lzh_LoopItemData。详细的item的数据类一定继承它

/*

* 描术:

*

* 作者:AnYuanLzh

* 时间:2014-xx-xx

*/

using UnityEngine;

using System.Collections;

///

/// 与item对关联的数据类。详细的item的数据类一定继承它

///

public class Lzh_LoopItemData

{

// ***

}

4、上面三个是基本的类,凝视也比較具体。

四、demoproject

详细的使用请下载我的demoproject

1、demo的执行效果图:

2、demo下载:这个包中一份project码和一个build好的可执行的exe。

(注:其代码project中缺少NGUI插件。而要你们自行加上,这个demo我用的是ngui3.7.4。我认为相近的其他版ngui也是行的)

2014-11-04 补:Lzh_LoopItemData 这个基类事实上是不能够不要的。还可降低复杂度。

2015-01-27补:这仅仅一个非常easy的demo,仅仅为了表达一个主要的思路,并且简单了也方便交流与学习,可优化与扩展的空间是非常大的。

相关推荐

肝硬化视频直播
365bet赌场手机投注

肝硬化视频直播

📅 07-05 👁️ 1132
美版三星s6怎么样
百特365下载

美版三星s6怎么样

📅 09-05 👁️ 437
PyQT5 绑定函数的传参(connect 带参数)
365bet赌场手机投注

PyQT5 绑定函数的传参(connect 带参数)

📅 11-21 👁️ 830
什么牌子的男士内裤质量好?五大品牌最全测评大揭秘!
蜻蜓不見了?|蜻蜓消失的原因
365bet赌场手机投注

蜻蜓不見了?|蜻蜓消失的原因

📅 07-20 👁️ 7570
曾征战2002年韩日世界杯的前国脚张恩华去世 享年48岁
手机卡使用时长查询全攻略
365bet赌场手机投注

手机卡使用时长查询全攻略

📅 09-23 👁️ 7395
脱发中医看哪个科室
百特365下载

脱发中医看哪个科室

📅 08-25 👁️ 7533
魔兽7.2武器战天赋加点推荐
日博365投注

魔兽7.2武器战天赋加点推荐

📅 07-15 👁️ 9631