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; /// <summary>
/// 这个类主要做了一件事,就是优化了,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的第一个元素
/// </summary>
Lzh_LoopItemObject firstItem;
/// <summary>
/// itemsList的最后一个元素
/// </summary>
Lzh_LoopItemObject lastItem; public delegate void DelegateHandler(Lzh_LoopItemObject item, Lzh_LoopItemData data);
/// <summary>
/// 响应
/// </summary>
public DelegateHandler OnItemInit; /// <summary>
/// 第一item的起始位置
/// </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)
{
// 推断要不要在它的前面补充一个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();
}
} } /// <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>
/// 构造一个 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前面补上一个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 *=-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);
}
} /// <summary>
/// 在itemsList后面补上一个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 *=-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 对象池性能相关
/// <summary>
/// 从对象池中取行一个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 = -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; /// <summary>
/// item对像的封装类Lzh_LoopItemObject。不要求详细的item类来继承它。
/// 但我们要示详细的item对像一定要包括UIWidget组件。 /// </summary>
[System.Serializable]
public class Lzh_LoopItemObject
{
/// <summary>
/// The widget.
/// </summary>
public UIWidget widget; /// <summary>
/// 本item。在实际整个scrollview中的索引位置,
/// 即对就数据。在数据列表中的索引
/// </summary>
public int dataIndex= -1; }
3、与item对关联的数据类:Lzh_LoopItemData。详细的item的数据类一定继承它
/*
* 描术:
*
* 作者:AnYuanLzh
* 时间:2014-xx-xx
*/
using UnityEngine;
using System.Collections; /// <summary>
/// 与item对关联的数据类。详细的item的数据类一定继承它
/// </summary>
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,仅仅为了表达一个主要的思路,并且简单了也方便交流与学习,可优化与扩展的空间是非常大的。
NGUI UIScrollView - 大量item子项的性能优化的更多相关文章
- JavaScript性能优化
如今主流浏览器都在比拼JavaScript引擎的执行速度,但最终都会达到一个理论极限,即无限接近编译后程序执行速度. 这种情况下决定程序速度的另一个重要因素就是代码本身. 在这里我们会分门别类的介绍J ...
- 【腾讯Bugly干货分享】跨平台 ListView 性能优化
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/FbiSLPxFdGqJ00WgpJ94yw 导语 精 ...
- Android之ListView性能优化——一行代码绑定数据——万能适配器
如下图,加入现在有一个这样的需求图,你会怎么做?作为一个初学者,之前我都是直接用SimpleAdapter结合一个Item的布局来实现的,感觉这样实现起来很方便(基本上一行代码就可以实现),而且也没有 ...
- mysql 性能优化方向
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- (转) Android开发性能优化简介
作者:贺小令 随着技术的发展,智能手机硬件配置越来越高,可是它和现在的PC相比,其运算能力,续航能力,存储空间等都还是受到很大的限制,同时用户对手机的体验要求远远高于PC的桌面应用程序.以上理由,足以 ...
- Android应用性能优化(转)
人类大脑与眼睛对一个画面的连贯性感知其实是有一个界限的,譬如我们看电影会觉得画面很自然连贯(帧率为24fps),用手机当然也需要感知屏幕操作的连贯性(尤其是动画过度),所以Android索性就把达到这 ...
- EntityFramework之原始查询及性能优化(六)
前言 在EF中我们可以通过Linq来操作实体类,但是有些时候我们必须通过原始sql语句或者存储过程来进行查询数据库,所以我们可以通过EF Code First来实现,但是SQL语句和存储过程无法进行映 ...
- mysql 性能优化方案
网 上有不少MySQL 性能优化方案,不过,mysql的优化同sql server相比,更为麻烦与复杂,同样的设置,在不同的环境下 ,由于内存,访问量,读写频率,数据差异等等情况,可能会出现不同的结果 ...
- mysql 性能优化方案1
网 上有不少mysql 性能优化方案,不过,mysql的优化同sql server相比,更为麻烦与复杂,同样的设置,在不同的环境下 ,由于内存,访问量,读写频率,数据差异等等情况,可能会出现不同的结果 ...
随机推荐
- cvs 文件无法上传debug
当时文件始终上传不成功时(一般先update后commit): cvs update filename report:move away filename ,it is in the way cvs ...
- CDC相关知识点总结
跨时钟域设计很重要,每一个项目都会有,这个要马上学会总结好.本能力属于重要而且急需的能力.
- Python9- 生成器函数进阶-day14
生成器进阶#send的获取下一个值的效果和next基本一致,#只不过在获取下一个值的时候,给上一个值的位置穿第一个数据 使用send的注册事项: #第一次使用生成器的时候,必须用next获取下一个值 ...
- JQuery基本事件函数
1,click单击事件 2,blur失去光标事件,focus获得光标事件 3,JQuery.on()函数:为html元素绑定事件,如下代码: $("div").on("c ...
- OpennSSL之基本了解
HTTPS是一种协议,等于HTTP+TLS(由于历史原因,SSL3.0之后就被TLS1.0替代了).openssl是一套开源工具集,主要有两个特性: 实现了ssl2,ssl3,TLSv1,TLSv1. ...
- 使用Phaser开发你的第一个H5游戏(一)
本文来自网易云社区 作者:王鸽 不知你是否还记得当年风靡一时的2048这个游戏,一个简单而又不简单的游戏,总会让你在空闲时间玩上一会儿. 在这篇文章里,我们将使用开源的H5框架--Phaser来重现这 ...
- Appium+python自动化-环境搭建
一.前言 本文是在windows10 X64系统下搭建appium,流程如下: 1.安装jdk1.8+python3.6 (64位) 2.安装node.js 3.安装Appium-desktop 4 ...
- NYOJ 745 蚂蚁的难题(二)
蚂蚁的难题(二) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 下雨了,下雨了,蚂蚁搬家了. 已知有n种食材需要搬走,这些食材从1到n依次排成了一个圈.小蚂蚁对每种 ...
- 2017 Multi-University Training Contest - Team 2
Regular polygon Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)T ...
- 【2018.10.18】noip模拟赛Day2 地球危机(2018年第九届蓝桥杯C/C++A组省赛 三体攻击)
题目描述 三体人将对地球发起攻击.为了抵御攻击,地球人派出了 $A × B × C$ 艘战舰,在太 空中排成一个 $A$ 层 $B$ 行 $C$ 列的立方体.其中,第 $i$ 层第 $j$ 行第 $k ...