一、当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子项的性能优化的更多相关文章

  1. JavaScript性能优化

    如今主流浏览器都在比拼JavaScript引擎的执行速度,但最终都会达到一个理论极限,即无限接近编译后程序执行速度. 这种情况下决定程序速度的另一个重要因素就是代码本身. 在这里我们会分门别类的介绍J ...

  2. 【腾讯Bugly干货分享】跨平台 ListView 性能优化

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/FbiSLPxFdGqJ00WgpJ94yw 导语 精 ...

  3. Android之ListView性能优化——一行代码绑定数据——万能适配器

    如下图,加入现在有一个这样的需求图,你会怎么做?作为一个初学者,之前我都是直接用SimpleAdapter结合一个Item的布局来实现的,感觉这样实现起来很方便(基本上一行代码就可以实现),而且也没有 ...

  4. mysql 性能优化方向

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  5. (转) Android开发性能优化简介

    作者:贺小令 随着技术的发展,智能手机硬件配置越来越高,可是它和现在的PC相比,其运算能力,续航能力,存储空间等都还是受到很大的限制,同时用户对手机的体验要求远远高于PC的桌面应用程序.以上理由,足以 ...

  6. Android应用性能优化(转)

    人类大脑与眼睛对一个画面的连贯性感知其实是有一个界限的,譬如我们看电影会觉得画面很自然连贯(帧率为24fps),用手机当然也需要感知屏幕操作的连贯性(尤其是动画过度),所以Android索性就把达到这 ...

  7. EntityFramework之原始查询及性能优化(六)

    前言 在EF中我们可以通过Linq来操作实体类,但是有些时候我们必须通过原始sql语句或者存储过程来进行查询数据库,所以我们可以通过EF Code First来实现,但是SQL语句和存储过程无法进行映 ...

  8. mysql 性能优化方案

    网 上有不少MySQL 性能优化方案,不过,mysql的优化同sql server相比,更为麻烦与复杂,同样的设置,在不同的环境下 ,由于内存,访问量,读写频率,数据差异等等情况,可能会出现不同的结果 ...

  9. mysql 性能优化方案1

    网 上有不少mysql 性能优化方案,不过,mysql的优化同sql server相比,更为麻烦与复杂,同样的设置,在不同的环境下 ,由于内存,访问量,读写频率,数据差异等等情况,可能会出现不同的结果 ...

随机推荐

  1. js解析器

    1>js的预解析 找var function 参数等 所有的变量,在正式运行代码前,都提前赋了一个值:未定义 所有的函数,在正式运行代码前,都是整个函数块. 遇到重名的:只留一个 如果变量与函数 ...

  2. Spring Cloud Stream在同一通道根据消息内容分发不同的消费逻辑

    应用场景 有的时候,我们对于同一通道中的消息处理,会通过判断头信息或者消息内容来做一些差异化处理,比如:可能在消息头信息中带入消息版本号,然后通过if判断来执行不同的处理逻辑,其代码结构可能是这样的: ...

  3. teatime、

    Python之路,Day7 - 面向对象编程进阶   本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个 ...

  4. PAT Basic 1014

    1014 福尔摩斯的约会 大侦探福尔摩斯接到一张奇怪的字条:“我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm” ...

  5. re--模块【转】

    为什么要学正则表达式 实际上爬虫一共就四个主要步骤: 明确目标 (要知道你准备在哪个范围或者网站去搜索) 爬 (将所有的网站的内容全部爬下来) 取 (去掉对我们没用处的数据) 处理数据(按照我们想要的 ...

  6. struts2模版搭建

    点击MyEclipse菜单栏File按钮,点击new-->Web Project 输入Project name之后点击Finish 项目创建完成. 然后右键项目,点击MyEclipse--> ...

  7. vue.js+element-ui

    git地址:https://github.com/jerry9022/LitAdmin vue.js+element-ui 做后台管理系统 太方便了

  8. zoj 1760 Doubles

    Doubles Time Limit: 2 Seconds      Memory Limit: 65536 KB As part of an arithmetic competency progra ...

  9. 一堆Offer怎么选?这样做就不纠结了

    有个朋友,工作了10年左右,春节后换工作,拿了三个Offer(西安): 通信行业的一家研究所,软件开发工程师,月薪7K,承诺有月奖金.年终奖金 一家做大数据的公司,软件开发工程师,月薪15K,13薪 ...

  10. Split The Tree

    Split The Tree 时间限制: 1 Sec  内存限制: 128 MB 题目描述 You are given a tree with n vertices, numbered from 1 ...