NGUI ScrollView 最彻底优化方案 加载大量(百万级)Item不卡的()
2016-01-31 19:05
986 查看
逻辑描诉:
将实际的Item,与对应填充的数据分开。
例如:List<item> 与 List<data>
item这个class中设置填充函数,填充数据的同时还要根据数据的逻辑位置调整自己的渲染位置。
在ScrollView中加载填满当前View的item数量,为了保证用户拖拽的时候不会显示空白的地方就在多加载n个item
以纵向拖拽为例:
进行item重排是优化的核心,这里提供两套方案:
1、当用户拖拽的距离超过单元格的高度的时候,就要拿数据重新填充所有的List<item> 。
2、用户拖拽的距离超过单元格的高度的时候,下滑的时候把底部的item,拼接到上部。上滑的时候把上部的item,拼接到上部。
方案1:
最初状态
---------
item0
item1
itme2
---------
item3
拉动一个格子后
---------
item1
item2
item3
---------
item4
方案2:
最初状态
---------
item0
item1
itme2
---------
item3
item4
item5
向下拉动 3个单元格后
---------
item3
item4
itme5
---------
item6
item7(原先的item1移动到这里重写填充)
item8(原先的item0移动到这里重写填充)
再向上拉动 1个单元格
item0(上次的item8移动到这里重写填充)
item1(上次的item7移动到这里重写填充)
---------
item2
item3
itme4
---------
item5
再向下拉动1个单元格
---------
item3
item4
itme5
---------
item6
item7(上次的item0移动到这里重写填充)
item8(上次的item8移动到这里重写填充)
核心代码方案1如下:(代码比较直观,但是如果grid item有选中状态的时候,处理起来比较蛋疼)
往Gird中加Item的函数
核心代码方案2如下:(代码多出20多行,重排的代码至少多用了20~30行,各种数值)
Item的基类函数
将实际的Item,与对应填充的数据分开。
例如:List<item> 与 List<data>
item这个class中设置填充函数,填充数据的同时还要根据数据的逻辑位置调整自己的渲染位置。
在ScrollView中加载填满当前View的item数量,为了保证用户拖拽的时候不会显示空白的地方就在多加载n个item
以纵向拖拽为例:
进行item重排是优化的核心,这里提供两套方案:
1、当用户拖拽的距离超过单元格的高度的时候,就要拿数据重新填充所有的List<item> 。
2、用户拖拽的距离超过单元格的高度的时候,下滑的时候把底部的item,拼接到上部。上滑的时候把上部的item,拼接到上部。
方案1:
最初状态
---------
item0
item1
itme2
---------
item3
拉动一个格子后
---------
item1
item2
item3
---------
item4
方案2:
最初状态
---------
item0
item1
itme2
---------
item3
item4
item5
向下拉动 3个单元格后
---------
item3
item4
itme5
---------
item6
item7(原先的item1移动到这里重写填充)
item8(原先的item0移动到这里重写填充)
再向上拉动 1个单元格
item0(上次的item8移动到这里重写填充)
item1(上次的item7移动到这里重写填充)
---------
item2
item3
itme4
---------
item5
再向下拉动1个单元格
---------
item3
item4
itme5
---------
item6
item7(上次的item0移动到这里重写填充)
item8(上次的item8移动到这里重写填充)
核心代码方案1如下:(代码比较直观,但是如果grid item有选中状态的时候,处理起来比较蛋疼)
往Gird中加Item的函数
public class Tool{ public static void CreateScrollView<T>( UIGrid grid, GameObject templateItem, IList datas, List<T> scrollItems) where T : ScrollViewItem { // 删除UI项目 grid.transform.DestroyChildren(); UIScrollView scrollView = grid.transform.parent.GetComponent<UIScrollView>(); if (scrollItems == null) { scrollItems = new List<T>(); } else { scrollItems.Clear(); } int childCount = datas.Count; int fillCount = 0; //当前scrollView被填满的格子数 int lastIndex = 0; //上次显示出来的第一个格子,在grid数据中的index Vector3 lastPos = Vector3.zero; UIPanel panel = scrollView.GetComponent<UIPanel>(); if (scrollView.movement == UIScrollView.Movement.Vertical) { fillCount = Mathf.RoundToInt(panel.height / grid.cellHeight); } else if (scrollView.movement == UIScrollView.Movement.Horizontal) { fillCount = Mathf.RoundToInt(panel.width / grid.cellWidth); } // 面板没被占满拖拽回滚 if (!scrollView.disableDragIfFits) { if (childCount <= fillCount) { lastPos = panel.transform.localPosition; scrollView.onMomentumMove = () => { }; scrollView.onMomentumMove = () => { SpringPanel.Begin(panel.gameObject, lastPos, 13f).strength = 8f; }; } } // 如果item数量大于填满显示面板的数量做优化 if (childCount > fillCount + 1) { childCount = fillCount + 1; // 拖拽刷新面板 panel.onClipMove = (uiPanel) => { Vector3 delata = lastPos - panel.transform.localPosition; float distance = delata.y != 0 ? delata.y : delata.x; // 满的时候向上滑不管它 if (distance > 0) return; distance = -distance; //当前显示出来的第一个格子,在grid数据中的index int index = Mathf.FloorToInt(distance / grid.cellHeight); // 拖拽不满一个单元格 if (index == lastIndex) return; // 拉到底了 if (index + childCount > datas.Count) return; lastIndex = index; // 重刷 for (int i = index; i < childCount + index; i++) { scrollItems[i - index].FillItem(datas, i); } }; } // 添加能填满UI数量的button for (int i = 0; i < childCount; i++) { GameObject go = NGUITools.AddChild(grid.gameObject, templateItem); go.SetActive(true); T item = go.AddComponent<T>(); item.grid = grid; item.FindItem(); item.FillItem(datas, i); scrollItems.Add(item); } lastPos = panel.transform.localPosition; grid.Reposition(); } }
核心代码方案2如下:(代码多出20多行,重排的代码至少多用了20~30行,各种数值)
static public void CreateScrollView<T>(UIGrid grid, GameObject templateItem, IList datas, List<T> scrollItems) where T : ScrollViewBaseItem { // 删除UI项目 grid.transform.DestroyChildren(); UIScrollView scrollView = grid.transform.parent.GetComponent<UIScrollView>(); if (scrollItems == null) { scrollItems = new List<T>(); } else { scrollItems.Clear(); } int dataCount = datas.Count;//数据的数量 int fillCount = 0; //当前scrollView被填满的格子数 int cacheNum = 3; //多出来的缓存格子 Vector3 lastPos = Vector3.zero; UIPanel panel = scrollView.GetComponent<UIPanel>(); UIScrollView.Movement moveType = scrollView.movement; if (moveType == UIScrollView.Movement.Vertical) { fillCount = Mathf.RoundToInt(panel.height / grid.cellHeight); } else if (moveType == UIScrollView.Movement.Horizontal) { fillCount = Mathf.RoundToInt(panel.width / grid.cellWidth); } // 面板没被占满拖拽回滚 if (!scrollView.disableDragIfFits) { if (dataCount <= fillCount) { lastPos = panel.transform.localPosition; scrollView.onMomentumMove = () => { }; scrollView.onMomentumMove = () => { SpringPanel.Begin(panel.gameObject, lastPos, 13f).strength = 8f; }; } } // 如果item数量大于填满显示面板的数量做优化 if (dataCount > fillCount + cacheNum) { dataCount = fillCount + cacheNum; int lastIndex = 0; //上次显示出来的第一个格子,在grid数据中的index int maxIndex = dataCount - 1; int minIndex = 0; int forwardCacheNum = 0;//用于缓存向指定方向滑动,预加载的格子数 // 拖拽刷新面板 panel.onClipMove = (uiPanel) => { Vector3 delata = lastPos - panel.transform.localPosition; float distance = -1; int index;//当前显示出来的第一个格子,在grid数据中的index distance = delata.y != 0 ? delata.y : delata.x; // 满的时候向上滑不管它 if (distance > 0) return; distance = -distance; if (moveType == UIScrollView.Movement.Horizontal) { index = Mathf.FloorToInt(distance / grid.cellWidth); } else { index = Mathf.FloorToInt(distance / grid.cellHeight); } // 拖拽不满一个单元格 if (index == lastIndex) return; // 拉到底了 if (index + dataCount > datas.Count) return; // 重刷 int offset = Math.Abs(index - lastIndex); // 判断要把最上(左)的item移动到最下(右),还是相反 if (lastIndex < index) { //如果有上一次的缓存数量,就清掉 if(forwardCacheNum > 0){ while (forwardCacheNum > 0) { //上(左)移动到下(右) int curIndex = maxIndex + 1; T item = scrollItems[0]; scrollItems.Remove(item); scrollItems.Add(item); item.FillItem(datas, curIndex); minIndex++; maxIndex++; forwardCacheNum--; } } for (int i = 1; i <= offset; i++) { //上(左)移动到下(右) int curIndex = maxIndex + 1; T item = scrollItems[0]; scrollItems.Remove(item); scrollItems.Add(item); item.FillItem(datas, curIndex); minIndex++; maxIndex++; } } else { forwardCacheNum = forwardCacheNum - offset; //缓存数量 while ((forwardCacheNum < cacheNum - 1 && index >= cacheNum - 1) || (forwardCacheNum < 0 && index < cacheNum -1)) { // 下(右)移动到上(左) int curIndex = minIndex - 1; T item = scrollItems[scrollItems.Count - 1]; scrollItems.Remove(item); scrollItems.Insert(0, item); item.FillItem(datas, curIndex); minIndex--; maxIndex--; forwardCacheNum++; } } lastIndex = index; }; //如果这个函数BUG了,请把下面解开下面代码的封印,强行不BUG //scrollView.onMomentumMove = () => { }; //scrollView.onMomentumMove = () => { // for (int i = lastIndex; i < childCount + lastIndex; i++) { // scrollItems[i - lastIndex].FillItem(datas, i); // } //}; } // 添加能填满UI数量的button for (int i = 0; i < dataCount; i++) { GameObject go = NGUITools.AddChild(grid.gameObject, templateItem); go.SetActive(true); T item = go.AddComponent<T>(); item.grid = grid; item.fillCount = fillCount; scrollItems.Add(item); item.FindItem(); item.FillItem(datas, i); } lastPos = panel.transform.localPosition; grid.Reposition(); }
Item的基类函数
public class ScrollViewItem : MonoBehaviour { private IList _datas; private int _index; private UIScrollView.Movement _moveType; private UIScrollView _scrollView; public IList datas { get { return _datas; } } public int index { get { return _index; } } protected UIGrid _grid; public UIGrid grid { set { _grid = value; _scrollView = _grid.transform.parent.GetComponent<UIScrollView>(); _moveType = _scrollView.movement; } get { return _grid; } } public virtual void FindItem() { } public virtual void FillItem(IList datas, int index) { _datas = datas; _index = index; if (_moveType == UIScrollView.Movement.Horizontal) { transform.localPosition = new Vector3(-_grid.cellWidth * index, 0, 0); } else if (_moveType == UIScrollView.Movement.Vertical) { transform.localPosition = new Vector3(0, -_grid.cellHeight * index, 0); } } }
相关文章推荐
- Bluez调试工具hcitool与gattool的使用实例
- iOS使用XZMRefresh实现UITableView或UICollectionView横向刷新
- B. Guess the Permutation
- UINavigationController练习
- 修改Request 中的数据
- RabbitMQ学习笔记
- Intergraph CADWorx (Plant, P&ID, Equipment, IP, SpecEditor) 2016 v16.0-ISO 1DVD
- java.sql.SQLException: General error, message from server: "Field 'ID' doesn't have a default value
- java.net.SocketException: Can't assign requested address
- busybox filesystem matrix-gui-2.0 undefined function json_encode()
- UItextView回收键盘的几种方式
- LeetCode Unique Binary Search Trees
- android-Building a Notification,Preserving Navigation when Starting an Activity
- Codeforces 67C Sequence of Balls 编辑距离 dp
- BZOJ-1206 虚拟内存 Hash+离散化+Priority_Queue
- BZOJ-1206 虚拟内存 Hash+离散化+Priority_Queue
- iOS UITextField
- iOS8下的UIAlertContoller初探
- UE-9260使用说明1
- 联合国大楼UVA1605(Building for UN)