[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 * ...
随机推荐
- R3注入的四种方式
DLL注入 1.首先要获取想要注入的进程句柄(OpenProcess) 2.从要注入的进程的地址空间中分配一段内存(VirtualAllocEx) 3.往分配的内存位置写入要注入的DLL名称(Writ ...
- php上传导入文件 nginx-502错误
4. php程序执行时间过长而超时,检查nginx和fastcgi中各种timeout设置.(nginx 中的 fastcgi_connect_timeout 300;fastcgi_send_ti ...
- 关于AXI4-Stream to Video Out 和 Video Timing Controller IP核学习
关于AXI4-Stream to Video Out 和 Video Timing Controller IP核学习 1.AXI4‐Stream to Video Out Top‐Level Sign ...
- StyleCop(C#代码检测工具)
StyleCop(C#代码检测工具) 一.StyleCop是微软的一个开源的静态代码分析工具,检查c#代码一致性和编码风格. 二.下载地址 http://stylecop.codeplex.c ...
- PerformEraseBackground 擦除背景(ThemeServices)
PerformEraseBackground 擦除背景的简单方法(外带ThemeServices例子) 在查这个函数的时候,顺便看到了有趣的代码. 怎么使用 Themes . unit Unit2; ...
- 解决iScroll横向滚动区域无法拉动页面的问题
近期项目中使用iScroll遇到一个问题,在设定wrapper为横向滚动时,如果你手指放在该区域,将无法拉动页面,也就是说该区域取消了默认事件.这个体验是实在是无法接受,特别是页面中有多个横向滚动区域 ...
- C#中数据库事务、存储过程基本用法
SQL 事务 public bool UpdateQsRegisterSql(List<string> ids, int newQueueId, string newQueueName) ...
- 黄聪:超实用的PHPExcel[导入][导出]实现方法总结
首先需要去官网https://github.com/PHPOffice/PHPExcel/下载PHPExcel,下载后只需要Classes目录下的文件即可. 1.PHPExcel导出方法实现过程 /* ...
- hierarchical_mutex函数问题(C++ Concurrent in Action)
C++ Concurrent in Action(英文版)书上(No.52-No.53)写的hierarchical_mutex函数,只适合结合std::lock_guard使用,直接使用如果不考虑顺 ...
- [UE4]虚幻UE4 .uproject文件无关联 右键菜单少了
前一段时间因为一些事,重装系统 然后重新安装UE4跟VS ,突然发现...竟然之前的UE4原先的项目找不到了,然后用UE4打开就提示 “该文件没有与之关联的程序来执行该操作,请先安装一个程序... ...