NGUI UIScrollView - 大量item子項的性能優化
來源:程序員人生 發布時間:2014-11-15 06:17:02 閱讀次數:3185次
1、當UIScrollView的下面的包括的子項太多(2310個之上)時,它的轉動就會變的很卡。
對些網上也有很多的優化它的相干,下面是我的1個優化:
1、將在超越裁剪框的1個item的距離的item,從scrollview中燒毀掉 。當它將要出現在裁剪框中時,再將它構造出來。-- 大家好你都是這么做的。
2、為避免頻繁的構造、燒毀,致使頻繁的分配內存和產生大量的內存垃圾內,致使的性能問題,我增加了1個對象池來管理item的構造與移除工作。
3、scrollvew中的元素在1般情況下,其中的item是要求等距的,如果它的大小不1樣,它們的距離就會良莠不齊。而這個問題在我的優化中是不存在的。
4、最主要是代碼量少、使用簡單、擴大方便。
2、話不多說,上代碼:
1、主類:Lzh_LoopScrollView.cs
/*
* 描術:
*
* 作者:AnYuanLzh
* 公司:lexun
* 時間:2014-xx-xx
*/
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// 這個類主要做了1件事,就是優化了,NGUI UIScrollView 在數據量很多都時候,
/// 創建過量都GameObject對象,造成資源浪費.
/// </summary>
public class Lzh_LoopScrollView : MonoBehaviour
{
public enum ArrangeDirection
{
Left_to_Right,
Right_to_Left,
Up_to_Down,
Down_to_Up,
}
/// <summary>
/// items的排列方式
/// </summary>
public ArrangeDirection arrangeDirection = ArrangeDirection.Up_to_Down;
/// <summary>
/// 列表單項模板
/// </summary>
public GameObject itemPrefab;
/// <summary>
/// The items list.
/// </summary>
public List<Lzh_LoopItemObject> itemsList;
/// <summary>
/// The datas list.
/// </summary>
public List<Lzh_LoopItemData> datasList;
/// <summary>
/// 列表腳本
/// </summary>
public UIScrollView scrollView;
public GameObject itemParent;
/// <summary>
/// itemsList的第1個元素
/// </summary>
Lzh_LoopItemObject firstItem;
/// <summary>
/// itemsList的最后1個元素
/// </summary>
Lzh_LoopItemObject lastItem;
public delegate void DelegateHandler(Lzh_LoopItemObject item, Lzh_LoopItemData data);
/// <summary>
/// 響應
/// </summary>
public DelegateHandler OnItemInit;
/// <summary>
/// 第1item的起始位置
/// </summary>
public Vector3 itemStartPos = Vector3.zero;
/// <summary>
/// 菜單項間隙
/// </summary>
public float gapDis = 0f;
// 對象池
// 再次優化,頻繁的創建與燒毀
Queue<Lzh_LoopItemObject> itemLoop = new Queue<Lzh_LoopItemObject>();
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();
}
}
/// <summary>
/// 檢驗items的兩端是不是要補上或刪除
/// </summary>
void Validate()
{
if( datasList==null || datasList.Count==0)
{
return;
}
// 如果itemsList還不存在
if(itemsList==null || itemsList.Count==0)
{
itemsList = new List<Lzh_LoopItemObject>();
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)
{
// 判斷要不要在它的前面補充1個item
if(firstItem.dataIndex>0)
{
Lzh_LoopItemObject item = GetItemFromLoop();
// 初化:數據索引、大小、位置、顯示
int index = firstItem.dataIndex⑴;
//InitItem(item, index, datasList[index]);
AddToFront(firstItem, item, index, datasList[index]);
firstItem = item;
itemsList.Insert(0,item);
//Validate();
}
}
else
{
// 判斷要不要將它移除
// 條件:本身是不可見的;且它后1個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)
{
// 判斷要不要在它的后面補充1個item
if(lastItem.dataIndex < datasList.Count⑴)
{
Lzh_LoopItemObject item = GetItemFromLoop();
// 初化:數據索引、大小、位置、顯示
int index = lastItem.dataIndex+1;
AddToBack(lastItem, item, index, datasList[index]);
lastItem = item;
itemsList.Add(item);
//Validate();
}
}
else
{
// 判斷要不要將它移除
// 條件:本身是不可見的;且它前1個item也是不可見的(或被被裁剪過半的).
// 這有個隱含條件是itemsList.Count>=2.
if(itemsList.Count>=2
&& itemsList[itemsList.Count⑴].widget.isVisible==false
&& itemsList[itemsList.Count⑵].widget.isVisible==false)
{
itemsList.Remove(lastItem);
PutItemToLoop(lastItem);
lastItem = itemsList[itemsList.Count⑴];
//Validate();
}
}
}
/// <summary>
/// Init the specified datas.
/// </summary>
/// <param name="datas">Datas.</param>
public void Init(List<Lzh_LoopItemData> datas, DelegateHandler onItemInitCallback)
{
datasList = datas;
this.OnItemInit = onItemInitCallback;
Validate();
}
/// <summary>
/// 構造1個 item 對象
/// </summary>
/// <returns>The item.</returns>
Lzh_LoopItemObject CreateItem()
{
GameObject go = NGUITools.AddChild(itemParent,itemPrefab);
UIWidget widget = go.GetComponent<UIWidget>();
Lzh_LoopItemObject item = new Lzh_LoopItemObject();
item.widget = widget;
go.SetActive(true);
return item;
}
/// <summary>
/// 用數據列表來初始化scrollview
/// </summary>
/// <param name="item">Item.</param>
/// <param name="indexData">Index data.</param>
/// <param name="data">Data.</param>
void InitItem(Lzh_LoopItemObject item, int dataIndex, Lzh_LoopItemData data)
{
item.dataIndex = dataIndex;
if(OnItemInit!=null)
{
OnItemInit(item, data);
}
item.widget.transform.localPosition = itemStartPos;
}
/// <summary>
/// 在itemsList前面補上1個item
/// </summary>
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 *=⑴f;
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 *=⑴f;
newItem.widget.transform.localPosition = priorItem.widget.cachedTransform.localPosition - new Vector3(offsetX, 0f, 0f);
}
}
/// <summary>
/// 在itemsList后面補上1個item
/// </summary>
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 *=⑴f;
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 *=⑴f;
newItem.widget.transform.localPosition = backItem.widget.cachedTransform.localPosition + new Vector3(offsetX, 0f, 0f);
}
}
#region 對象池性能相干
/// <summary>
/// 從對象池中取行1個item
/// </summary>
/// <returns>The item from loop.</returns>
Lzh_LoopItemObject GetItemFromLoop()
{
Lzh_LoopItemObject item;
if(itemLoop.Count<=0)
{
item = CreateItem();
}
else
{
item = itemLoop.Dequeue();
}
item.widget.gameObject.SetActive(true);
return item;
}
/// <summary>
/// 將要移除的item放入對象池中
/// --這個里我保證這個對象池中存在的對象不超過3個
/// </summary>
/// <param name="item">Item.</param>
void PutItemToLoop(Lzh_LoopItemObject item)
{
if(itemLoop.Count>=3)
{
Destroy(item.widget.gameObject);
return;
}
item.dataIndex = ⑴;
item.widget.gameObject.SetActive(false);
itemLoop.Enqueue(item);
}
#endregion
}
2、item對像的封裝類:Lzh_LoopItemObject,不要求具體的item類來繼承它,但我們要示具體的item對像1定要包括UIWidget組件。
/*
* 描術:
*
* 作者:AnYuanLzh
* 公司:lexun
* 時間:2014-xx-xx
*/
using UnityEngine;
using System.Collections;
/// <summary>
/// item對像的封裝類Lzh_LoopItemObject,不要求具體的item類來繼承它。
/// 但我們要示具體的item對像1定要包括UIWidget組件。
/// </summary>
[System.Serializable]
public class Lzh_LoopItemObject
{
/// <summary>
/// The widget.
/// </summary>
public UIWidget widget;
/// <summary>
/// 本item,在實際全部scrollview中的索引位置,
/// 即對就數據,在數據列表中的索引
/// </summary>
public int dataIndex= ⑴;
}
3、與item對關聯的數據類:Lzh_LoopItemData,具體的item的數據類1定繼承它
/*
* 描術:
*
* 作者:AnYuanLzh
* 公司:lexun
* 時間:2014-xx-xx
*/
using UnityEngine;
using System.Collections;
/// <summary>
/// 與item對關聯的數據類,具體的item的數據類1定繼承它
/// </summary>
public class Lzh_LoopItemData
{
// ***
}
4、上面3個是主要的類,注釋也比較詳細。
4、demo工程
具體的使用請下載我的demo工程
1、demo的運行效果圖:

2、demo下載:這個包中1份工程碼和1個build好的可運行的exe。
(注:其代碼工程中缺少NGUI插件,而要你們自行加上,這個demo我用的是ngui3.7.4。我覺得相近的其它版ngui也是行的)
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈