NGUI ScrollView 最彻底优化方案 加载大量(百万级)Item不卡的()

逻辑描诉:


将实际的Item,与对应填充的数据分开。

例如:List 与 List

item这个class中设置填充函数,填充数据的同时还要根据数据的逻辑位置调整自己的渲染位置。


在ScrollView中加载填满当前View的item数量,为了保证用户拖拽的时候不会显示空白的地方就在多加载n个item

以纵向拖拽为例:


进行item重排是优化的核心,这里提供两套方案:

1、当用户拖拽的距离超过单元格的高度的时候,就要拿数据重新填充所有的List

2、用户拖拽的距离超过单元格的高度的时候,下滑的时候把底部的item,拼接到上部。上滑的时候把上部的item,拼接到上部。


方案1:

最初状态

---------

item0 

item1

itme2

---------

item3

NGUI ScrollView 最彻底优化方案 加载大量(百万级)Item不卡的()_第1张图片

















拉动一个格子后

---------

item1

item2

item3

---------

item4


NGUI ScrollView 最彻底优化方案 加载大量(百万级)Item不卡的()_第2张图片


方案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(
	        UIGrid grid, GameObject templateItem, IList datas, List scrollItems)
                where T : ScrollViewItem {
		// 删除UI项目
		grid.transform.DestroyChildren();
		UIScrollView scrollView = grid.transform.parent.GetComponent();

		if (scrollItems == null) { scrollItems = new List(); }
		else { scrollItems.Clear(); }


		int childCount = datas.Count;
		int fillCount = 0; //当前scrollView被填满的格子数
		int lastIndex = 0; //上次显示出来的第一个格子,在grid数据中的index
		Vector3 lastPos = Vector3.zero;
		UIPanel panel = scrollView.GetComponent();
		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();
			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(UIGrid grid, GameObject templateItem, IList datas, List scrollItems) where T : ScrollViewBaseItem {
		// 删除UI项目
		grid.transform.DestroyChildren();
		UIScrollView scrollView = grid.transform.parent.GetComponent();

		if (scrollItems == null) { scrollItems = new List(); }
		else { scrollItems.Clear(); }

		int dataCount = datas.Count;//数据的数量
		int fillCount = 0; //当前scrollView被填满的格子数
		int cacheNum = 3; //多出来的缓存格子
		Vector3 lastPos = Vector3.zero;
		UIPanel panel = scrollView.GetComponent();
		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();
			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();
			_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);
		}
	}
}



你可能感兴趣的:(Unity3d)