需要注意的有下面几点:

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]滑动列表优化(循环利用)的更多相关文章

  1. iOS边练边学--UITableView性能优化之三种方式循环利用

    一.cell的循环利用方式1: /** * 什么时候调用:每当有一个cell进入视野范围内就会调用 */ - (UITableViewCell *)tableView:(UITableView *)t ...

  2. UIWrapContent(NGUI长列表优化利器)

    NGUI长列表优化利器 优化原理 NGUI3.7.x以上版本 有个新组件 UIWrapContent ,当我们的列表内容很多时,可以进行优化.它不是一次生成全部的child,而是只有固定数量的chil ...

  3. iOS开发UI篇—无限轮播(循环利用)

    iOS开发UI篇—无限轮播(循环利用) 一.无限轮播  1.简单说明 在开发中常需要对广告或者是一些图片进行自动的轮播,也就是所谓的无限滚动. 在开发的时候,我们通常的做法是使用一个UIScrollV ...

  4. 使用泛型简单封装NGUI的ScrollView实现滑动列表

    懒,是老毛病了,周末跑了半马,跑完也是一通累,好久没锻炼了..也是懒的,有时都懒的写博客..最近看到项目中各种滑动列表框,本着要懒出水平来的原则,决定花点时间简单处理下(暂时未做列表太多时的优化):1 ...

  5. App自动化之dom结构和元素定位方式(包含滑动列表定位)

    900×383 38 KB 先来看几个名词和解释: dom: Document Object Model 文档对象模型 dom应用: 最早应用于html和js的交互.界面的结构化描述, 常见的格式为h ...

  6. iOS开发小技巧--TableView中headerView的循环利用,以及自定义的headerView

    一.首先要搞清楚,tableView中有两种headerView,一个是tableHeaderView,另一个是headerView.前者就一个;后者根据session决定个数 headerView的 ...

  7. 实现UITableView循环利用

    tableViewUITableView循环利用 前言 大家都知道UITableView,最经典在于循环利用,这里我自己模仿UITableView循环利用,写了一套自己的TableView实现方案,希 ...

  8. Unity3d NGUI的使用(九)(UIScrollView制作滑动列表)

    UIScrollView制作滑动列表,可横向,竖直展示一些列表在固定可视范围内 UIScrollVIew只是一个可滑动的UI组件 如果需要制作复杂的可视区域UI需要配合使用UIPanel与UIGrid ...

  9. 解决cell循环利用造成的重复勾选

    @interface ProfessionViewController (){ NSMutableArray *_professionArray;//cell模型数组 NSMutableArray * ...

随机推荐

  1. 简单脱壳教程笔记(2)---手脱UPX壳(1)

    本笔记是针对ximo早期发的脱壳基础视频教程,整理的笔记. ximo早期发的脱壳基础视频教程 下载地址如下: http://down.52pojie.cn/%E5%90%BE%E7%88%B1%E7% ...

  2. SQLServer数据库自增长标识列的更新修改操作

    SQLServer数据库自增长标识列的更新修改操作方法在日常的sql server开发中,经常会用到Identity类型的标识列作为一个表结构的自增长编号.比如文章编号.记录序号等等.自增长的标识列的 ...

  3. 关于MySql悲观锁与乐观锁

    悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...

  4. ALGO-14_蓝桥杯_算法训练_回文数

    问题描述 若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数. 例如:给定一个10进制数56,将56加65(即把56从右向左读),得到121是一个回文数. 又如:对于10进制数 ...

  5. C/C++程序CPU问题分析

    转载地址:http://www.10tiao.com/html/473/201606/2651473094/1.html   程序的CPU问题是另外一类典型的程序性能问题,很多开发人员都受到过程序CP ...

  6. 廖雪峰Java2面向对象编程-6Java核心类-3包装类型

    Java的数据类型: 基本类型:int boolean float 引用类型:所有class类型 为一个基本类型int赋值为null,会提示"incompatible types" ...

  7. [UE4]Text Block文字字体偏移

    这样看起来就像是真正的垂直居中对齐了.

  8. [UE4]多线程开关,开启的解决方案

    像这样直接获取值就会被警告. 解决方法:定义一个变量speed,然后在“Blueprint Update Animation”事件中赋值给这个变量. 这样就不会被警告了. 另外一种解决方法:就是关掉多 ...

  9. CGo中传递多维数组给C函数

    转自:http://www.cnblogs.com/cobbliu/p/5035358.html package main /* #include <stdio.h> #include & ...

  10. Qt Opengl

    目前在Qt5中做Opengl的学习时候,发现gluPerspective函数没有定义. 1: gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, ...