您的位置:首页 > 产品设计 > UI/UE

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的函数

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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: