逻辑描诉:
将实际的Item,与对应填充的数据分开。
例如:List
item这个class中设置填充函数,填充数据的同时还要根据数据的逻辑位置调整自己的渲染位置。
在ScrollView中加载填满当前View的item数量,为了保证用户拖拽的时候不会显示空白的地方就在多加载n个item
以纵向拖拽为例:
进行item重排是优化的核心,这里提供两套方案:
1、当用户拖拽的距离超过单元格的高度的时候,就要拿数据重新填充所有的List
2、用户拖拽的距离超过单元格的高度的时候,下滑的时候把底部的item,拼接到上部。上滑的时候把上部的item,拼接到上部。
方案1:
最初状态
---------
item0
item1
itme2
---------
item3
拉动一个格子后
---------
item1
item2
item3
---------
item4
方案2:
最初状态
---------
item0
item1
itme2
---------
item3
item4item5
向下拉动 3个单元格后
---------
item3
item4
itme5
---------
item6
item7(原先的item1移动到这里重写填充)item8(原先的item0移动到这里重写填充)
item0(上次的item8移动到这里重写填充)
item1(上次的item7移动到这里重写填充)
---------
item2
item3
itme4
---------
item5
---------
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();
}
}
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();
}
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);
}
}
}