[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 * ...
随机推荐
- Cordova+Angularjs+Ionic 混合开发入门讲解
作为一名学习Android开发的学生,对于移动开发的发展趋势颇为关注,大家都知道,现在原生的移动开发在企业上基本很少使用了,大部分企业为了降低成本,选择了webapp,hybrid(混合开发)这两种模 ...
- MySQL分析数据运行状态利器【SHOW PROCESSLIST】
这个博文,将只是简单的记录一下,我们的数据库操作和使用中,加索引加不上去,分析的过程,其实比较简单,就是看有没有连接进程还在操作表.有的话,将其停掉(不影响业务的场景下). 今天的主角是: SHOW ...
- jQuery 筛选器 链式编程操作
$('#i1').next() 下一个标签$('#i1').nextAll() 兄弟标签中,所有下一个标签$('#i1').nextUntil('#ii1') 兄弟标签中,从下一个标签到id为ii1的 ...
- Linux系统构成和基本操作
Linux的优势 Linux的目录结构 Linux目录与文件管理 列出目录内容 创建新目录(文件夹) 创建文件 复制文件或目录 删除文件或目录 移动目录或文件 查看文件属性 文件属性含义 读权限-4 ...
- PHP 中如何创建和修改数组?
PHP中使用array来创建一个数组:array( key=>value , key=>value …… )用方括号的语法来修改数组:$arr[] = value 例如:$arr = ar ...
- ALGO-149_蓝桥杯_算法训练_5-2求指数
AC代码: #include <stdio.h> int main(void) { int n,m,sum; int i,j; scanf("%d %d",&n ...
- googletest--Test Fixture
前面博客中我们在单元测试之前会做一些变量初始化等工作,而同一个testcase的不同test之间往往会有一些初始化工作是相同的.我们不想做多余的重复的工作,当然同时也不想设置全局变量. 这个时候我们可 ...
- jquery zTree异步搜索的例子--搜全部节点
参考博客: https://segmentfault.com/a/1190000004657854 https://blog.csdn.net/houpengfei111/article/detail ...
- react中的数据请求
在react中,没有提供专门的请求数据的模块,所以我们可以采用第三方请求数据的模块进行数据请求,常见的有axios和fetch-jsonp,下面来总结这两种方式的用法. axios 查看文档 http ...
- Linux CentOS 下关闭防火墙
永久关闭(重启后生效) 开启: chkconfig iptables on 关闭: chkconfig iptables off 临时关闭(重启后失效) 开启: service iptables st ...