[UGUI]滑动列表优化(循环利用)
需要注意的有下面几点:
1.
区分好表现上的index和逻辑上的index。表现上的index是指这个go是go列表中的第几项,但实际上这个index的意义并不大,因为在滚动的过程中go列表是轮转的;逻辑上的index是指这个go对应数据中的第几项,在滑动的过程中不断地更新逻辑上的index,然后取对应的数据去刷新显示即可。在一般的滑动列表中,有几项数据就生成几个go,因此表现上的index和逻辑上的index是一致的;而在循环利用的循环列表中,这两个是不一致的。
那么,在实现上,就是需要知道每个go对应的逻辑index是多少了。而这个可以简化为,只需要知道第一个对应的逻辑index是多少,因为后面的就是依次递增的。
2.
做好缓存策略。对于循环利用的列表,需要生成的个数等于能显示的最大个数加上2-3个的缓存个数,防止滑动过快时出现穿帮。
3.
关于循环利用的实现。其实就是在滑动过程中,收集被移除显示的go,然后对这些go重新调整位置,并刷新go上的控件显示。那么如何收集呢,就是将go对应的逻辑index和当前的逻辑index范围进行比较,将不在这个范围内的go收集即可。然后在需要的时候取出来,刷新这些go即可。
代码如下:
using UnityEngine;
using System.Collections.Generic;
using System;
using UnityEngine.UI; [RequireComponent(typeof(ScrollRect))]
public class LoopScrollView : MonoBehaviour { private List<GameObject> goList;//当前显示的go列表
private Queue<GameObject> freeGoQueue;//空闲的go队列,存放未显示的go
private Dictionary<GameObject, int> goIndexDic;//key:所有的go value:真实索引
private ScrollRect scrollRect;
private RectTransform contentRectTra;
private Vector2 scrollRectSize;
private Vector2 cellSize;
private int startIndex;//起始索引
private int maxCount;//创建的最大数量
private int createCount;//当前显示的数量 private const int cacheCount = ;//缓存数目
private const int invalidStartIndex = -;//非法的起始索引 private int dataCount;
private GameObject prefabGo;
private Action<GameObject, int> updateCellCB;
private float cellPadding; //初始化SV并刷新
public void Show(int dataCount, GameObject prefabGo, Action<GameObject, int> updateCellCB, float cellPadding = 0f)
{
//数据和组件初始化
this.dataCount = dataCount;
this.prefabGo = prefabGo;
this.updateCellCB = updateCellCB;
this.cellPadding = cellPadding; goList = new List<GameObject>();
freeGoQueue = new Queue<GameObject>();
goIndexDic = new Dictionary<GameObject, int>();
scrollRect = GetComponent<ScrollRect>();
contentRectTra = scrollRect.content;
scrollRectSize = scrollRect.GetComponent<RectTransform>().sizeDelta;
cellSize = prefabGo.GetComponent<RectTransform>().sizeDelta;
startIndex = ;
maxCount = GetMaxCount();
createCount = ; if (scrollRect.horizontal)
{
contentRectTra.anchorMin = new Vector2(, );
contentRectTra.anchorMax = new Vector2(, );
}
else
{
contentRectTra.anchorMin = new Vector2(, );
contentRectTra.anchorMax = new Vector2(, );
}
scrollRect.onValueChanged.RemoveAllListeners();
scrollRect.onValueChanged.AddListener(OnValueChanged);
ResetSize(dataCount);
} //重置数量
public void ResetSize(int dataCount)
{
this.dataCount = dataCount;
contentRectTra.sizeDelta = GetContentSize(); //回收显示的go
for (int i = goList.Count - ; i >= ; i--)
{
GameObject go = goList[i];
RecoverItem(go);
} //创建或显示需要的go
createCount = Mathf.Min(dataCount, maxCount);
for (int i = ; i < createCount; i++)
{
CreateItem(i);
} //刷新数据
startIndex = -;
contentRectTra.anchoredPosition = Vector3.zero;
OnValueChanged(Vector2.zero);
} //更新当前显示的列表
public void UpdateList()
{
for (int i = ; i < goList.Count; i++)
{
GameObject go = goList[i];
int index = goIndexDic[go];
updateCellCB(go, index);
}
} //创建或显示一个item
private void CreateItem(int index)
{
GameObject go;
if (freeGoQueue.Count > )//使用原来的
{
go = freeGoQueue.Dequeue();
goIndexDic[go] = index;
go.SetActive(true);
}
else//创建新的
{
go = Instantiate<GameObject>(prefabGo);
goIndexDic.Add(go, index);
go.transform.SetParent(contentRectTra.transform); RectTransform rect = go.GetComponent<RectTransform>();
rect.pivot = new Vector2(, );
rect.anchorMin = new Vector2(, );
rect.anchorMax = new Vector2(, );
}
goList.Add(go);
go.transform.localPosition = GetPosition(index);
updateCellCB(go, index);
} //回收一个item
private void RecoverItem(GameObject go)
{
go.SetActive(false);
goList.Remove(go);
freeGoQueue.Enqueue(go);
goIndexDic[go] = invalidStartIndex;
} //滑动回调
private void OnValueChanged(Vector2 vec)
{
int curStartIndex = GetStartIndex();
//Debug.LogWarning(curStartIndex); if ((startIndex != curStartIndex) && (curStartIndex > invalidStartIndex))
{
startIndex = curStartIndex; //收集被移出去的go
//索引的范围:[startIndex, startIndex + createCount - 1]
for (int i = goList.Count - ; i >= ; i--)
{
GameObject go = goList[i];
int index = goIndexDic[go];
if (index < startIndex || index > (startIndex + createCount - ))
{
RecoverItem(go);
}
} //对移除出的go进行重新排列
for (int i = startIndex; i < startIndex + createCount; i++)
{
if (i >= dataCount)
{
break;
} bool isExist = false;
for (int j = ; j < goList.Count; j++)
{
GameObject go = goList[j];
int index = goIndexDic[go];
if (index == i)
{
isExist = true;
break;
}
}
if (isExist)
{
continue;
} CreateItem(i);
}
}
} //获取需要创建的最大prefab数目
private int GetMaxCount()
{
if (scrollRect.horizontal)
{
return Mathf.CeilToInt(scrollRectSize.x / (cellSize.x + cellPadding)) + cacheCount;
}
else
{
return Mathf.CeilToInt(scrollRectSize.y / (cellSize.y + cellPadding)) + cacheCount;
}
} //获取起始索引
private int GetStartIndex()
{
if (scrollRect.horizontal)
{
return Mathf.FloorToInt(-contentRectTra.anchoredPosition.x / (cellSize.x + cellPadding));
}
else
{
return Mathf.FloorToInt(contentRectTra.anchoredPosition.y / (cellSize.y + cellPadding));
}
} //获取索引所在位置
private Vector3 GetPosition(int index)
{
if (scrollRect.horizontal)
{
return new Vector3(index * (cellSize.x + cellPadding), , );
}
else
{
return new Vector3(, index * -(cellSize.y + cellPadding), );
}
} //获取内容长宽
private Vector2 GetContentSize()
{
if (scrollRect.horizontal)
{
return new Vector2(cellSize.x * dataCount + cellPadding * (dataCount - ), contentRectTra.sizeDelta.y);
}
else
{
return new Vector2(contentRectTra.sizeDelta.x, cellSize.y * dataCount + cellPadding * (dataCount - ));
}
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI; public class TestLoopScrollView : MonoBehaviour { public LoopScrollView loopScrollView;
public LoopScrollView loopScrollView2;
public GameObject prefabGo;
public GameObject prefabGo2; private void Start ()
{ } private void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
loopScrollView.Show(, prefabGo, UpdateSV);
}
else if(Input.GetKeyDown(KeyCode.W))
{
loopScrollView.ResetSize();
}
else if (Input.GetKeyDown(KeyCode.E))
{
loopScrollView.ResetSize();
}
else if (Input.GetKeyDown(KeyCode.R))
{
loopScrollView.UpdateList();
} if (Input.GetKeyDown(KeyCode.A))
{
loopScrollView2.Show(, prefabGo2, UpdateSV);
}
else if (Input.GetKeyDown(KeyCode.S))
{
loopScrollView2.ResetSize();
}
else if (Input.GetKeyDown(KeyCode.D))
{
loopScrollView2.ResetSize();
}
else if (Input.GetKeyDown(KeyCode.F))
{
loopScrollView.UpdateList();
}
} private void UpdateSV(GameObject go, int index)
{
Text text = go.transform.Find("Text").GetComponent<Text>();
text.text = index.ToString();
}
}
效果:
[UGUI]滑动列表优化(循环利用)的更多相关文章
- iOS边练边学--UITableView性能优化之三种方式循环利用
一.cell的循环利用方式1: /** * 什么时候调用:每当有一个cell进入视野范围内就会调用 */ - (UITableViewCell *)tableView:(UITableView *)t ...
- UIWrapContent(NGUI长列表优化利器)
NGUI长列表优化利器 优化原理 NGUI3.7.x以上版本 有个新组件 UIWrapContent ,当我们的列表内容很多时,可以进行优化.它不是一次生成全部的child,而是只有固定数量的chil ...
- iOS开发UI篇—无限轮播(循环利用)
iOS开发UI篇—无限轮播(循环利用) 一.无限轮播 1.简单说明 在开发中常需要对广告或者是一些图片进行自动的轮播,也就是所谓的无限滚动. 在开发的时候,我们通常的做法是使用一个UIScrollV ...
- 使用泛型简单封装NGUI的ScrollView实现滑动列表
懒,是老毛病了,周末跑了半马,跑完也是一通累,好久没锻炼了..也是懒的,有时都懒的写博客..最近看到项目中各种滑动列表框,本着要懒出水平来的原则,决定花点时间简单处理下(暂时未做列表太多时的优化):1 ...
- App自动化之dom结构和元素定位方式(包含滑动列表定位)
900×383 38 KB 先来看几个名词和解释: dom: Document Object Model 文档对象模型 dom应用: 最早应用于html和js的交互.界面的结构化描述, 常见的格式为h ...
- iOS开发小技巧--TableView中headerView的循环利用,以及自定义的headerView
一.首先要搞清楚,tableView中有两种headerView,一个是tableHeaderView,另一个是headerView.前者就一个;后者根据session决定个数 headerView的 ...
- 实现UITableView循环利用
tableViewUITableView循环利用 前言 大家都知道UITableView,最经典在于循环利用,这里我自己模仿UITableView循环利用,写了一套自己的TableView实现方案,希 ...
- Unity3d NGUI的使用(九)(UIScrollView制作滑动列表)
UIScrollView制作滑动列表,可横向,竖直展示一些列表在固定可视范围内 UIScrollVIew只是一个可滑动的UI组件 如果需要制作复杂的可视区域UI需要配合使用UIPanel与UIGrid ...
- 解决cell循环利用造成的重复勾选
@interface ProfessionViewController (){ NSMutableArray *_professionArray;//cell模型数组 NSMutableArray * ...
随机推荐
- js switch 函数类型 序列化 转义
switch(name){ case '1': age = 123; break; case '2': age = 456; break; default : age = 777; } 函数 func ...
- Oracle Split字符串
为了让 PL/SQL 函数返回数据的多个行,必须通过返回一个 REF CURSOR 或一个数据集合来完成.REF CURSOR 的这种情况局限于可以从查询中选择的数据,而整个集合在可以返回前,必须进行 ...
- ECR是什么意思
有效客户反应简称为ECR(efficient consumer response).它是1992年从美国的食品杂货业发展起来的一种供应链管理战略.这是一种分销商与供应商为消除系统中不必要的成本和费用并 ...
- Ubuntu 14.10 下安装Spark
Spark 是一种与 Hadoop 相似的开源集群计算环境,不过最近非常火.下面介绍安装步骤. 1 安装scala 1.1 我选用的是2.11.4,下载地址http://www.scala-lang. ...
- Espresso 开源了
Google Testing Blog上发布了一篇博客,Espresso 开源了 http://googletesting.blogspot.com/2013/10/espresso-for-andr ...
- gmake缺失错误
原文:http://blog.csdn.net/syh_486_007/article/details/53862831 编译nachos程序的时候发现了这样一个错误gmake: command no ...
- redis集群服务启动
1 启动redis服务器 redis-server.exe redis.windows.conf 需要配置config节点的bind ip 2 启动redis集群 开启redis.xx.conf 服务 ...
- studio之mac快捷键
2. SouceTree忽略文件: .gitignore文件编辑: 忽略指定文件:直接写文件名 忽略文件夹:直接写文件夹路径,例:target或者target/ -> 忽略target下的所有 ...
- 第15课 右值引用(2)_std::move和移动语义
1. std::move (1)std::move的原型 template<typename T> typename remove_reference<T>::type& ...
- MariaDB MaxScale
1. down https://mariadb.com/downloads/#mariadb_platform-mariadb_maxscale (1) install sudo yum locali ...