[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 * ...
随机推荐
- Configure Virtual Serial Port Driver (vspd)注册表
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VSBC7\Ports\COM3COM4] “Port1”=”COM3” “Port2”=” ...
- 美剧黑名单的插曲《Jolene》
网易上有Slowdown 版本.<Jolene>Dolly Parton
- 【亲测】<g++/gcc>CentOS下g++: command not found问题的解决(c++环境安装)
CentOS下g++: command not found问题的解决 2017年02月27日 18:09:06 阅读数:5174 标签: centosgcc 更多 个人分类: 问题分析 版权声明: ...
- js 格式化时间日期函数小结
下面是脚本之家为大家整理的一些格式化时间日期的函数代码,需要的朋友可以参考下. 代码如下: Date.prototype.format = function(format){ var o = { &q ...
- Python Selenium set Chrome Preference Download Location.
def set_chrome_pref(self): chromeOptions = webdriver.ChromeOptions() prefs = {"download.default ...
- 测试教程网.unittest教程.5. 实例: 找出所有是弱密码的用户
From: http://www.testclass.net/pyunit/test_example_3/ 背景 当我们的测试数据是下面这些的时候,我们的用例是有问题的. [ {"name& ...
- [蓝桥杯]ALGO-8.算法训练_操作格子
题目描述: 问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3种操作类型: .修改一个格子的权值, .求连续一段格子权值和, .求连续一段格子的最大值. 对于每个2.3操作输出 ...
- sql培训
1.select--查询语句 select * from table; select cols from table; select cols from table where condition; ...
- 非ECS阿里云安装插件,给阿里云云监控平台
linux的init学习: https://blog.csdn.net/kunkliu/article/details/80942279 阿里云官方文档: https://help.aliyun.co ...
- Spring+MyBatis实现数据库读写分离方案
推荐第四种:https://github.com/shawntime/shawn-rwdb 方案1 通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactor ...