简介:
对于数据量较大的列表, 载入时需要创建等同于数据个数的GameObject, 非常消耗性能.
本列表组件只生成Math.min(列表容器高度/条目高度 +1, 数据个数) 个GameObject, 并在列表滚动时, 重排列条目位置, 并重新对每个条目SetData(待优化).
适用范围:
包含大批量数据的单一条目列表, 所有条目高度一致
缺点:
方法WrapContent中未对SetData的调用做剪枝, 导致过多对子项SendMessage方法的调用, 当有打印语句时会有明显卡顿
代码如下:
using UnityEngine; using System.Collections; using System.Collections.Generic; public class SongeWrap : MonoBehaviour { public GameObject ItemRenderer; void Awake () { } void Start() { pScroll.restrictWithinPanel = true; firstFlag = addFlag("FirstFlag"); lastFlag = addFlag("LastFlag"); pPanel.onClipMove = panel => WrapContent(); ; GetComponentInParent<UIWidget>().onChange += onContainerItemChange; WrapContent(); } void Update () { testFunc(); } #region 数据 List<object> dataProvider = new List<object>(); public void AddData(object data) { dataProvider.Add(data); onDataChange(); } public void RemoveData(object data) { if (dataProvider.Contains(data)) dataProvider.Remove(data); onDataChange(); } public void RemoveDataAt(int index) { if(index >= 0 && index < dataProvider.Count) { dataProvider.RemoveAt(index); onDataChange(); } } public void ClearData() { dataProvider.Clear(); onDataChange(); } void onDataChange() { refreshItemNum(); } public object GetDataAt(int index) { return dataProvider[index]; } #endregion #region Item个数管理 int maxItemNum = 0;//按照列表大小, 最大所需条目数 void onContainerItemChange() { float containerSize = scrollDir == UIScrollView.Movement.Horizontal ? pPanel.width : pPanel.height; maxItemNum = (int)(containerSize / ItemSize) + 1; //Debug.Log("容器/条目大小变化, containerSize: " + containerSize + ", ItemSize:" + ItemSize); //Debug.Log("最大所需条目个数: " + maxItemNum); refreshItemNum(); } List<Transform> GetChildList() { List<Transform> childList = new List<Transform>(); for(int i = 0; i< transform.childCount; i++) { Transform childT = transform.GetChild(i); if (!(childT == firstFlag.transform || childT == lastFlag.transform)) childList.Add(childT); } return childList; } void refreshItemNum() { int itemNum = Mathf.Min(dataProvider.Count, maxItemNum); //Debug.Log("刷新条目数: " + itemNum + " " + dataProvider.Count + " " + maxItemNum); List<Transform> childList = GetChildList(); while(childList.Count > itemNum) { Transform removeItem = childList[0]; removeItem.parent = null; removeItem.gameObject.SetActive(false); childList.Remove(removeItem); Destroy(removeItem.gameObject); } while(childList.Count < itemNum) { GameObject addItem = Instantiate(ItemRenderer); addItem.transform.parent = transform; addItem.transform.localScale = Vector3.one; addItem.transform.localPosition = Vector3.zero; childList.Add(addItem.transform); } WrapContent(); } #endregion #region 布局 UIScrollView.Movement scrollDir { get { return pScroll.movement; } } UIScrollView pScroll { get { return NGUITools.FindInParents<UIScrollView>(gameObject); } } UIPanel pPanel { get { return pScroll.GetComponent<UIPanel>(); } } private float itemSize = 100; public float ItemSize { get { return itemSize; } set { float oldItemSize = itemSize; itemSize = value; if (itemSize != oldItemSize) { firstFlag.GetComponent<UISprite>().width = (int)(itemSize); lastFlag.GetComponent<UISprite>().height = (int)(itemSize); onContainerItemChange(); } } } GameObject firstFlag; GameObject lastFlag; GameObject addFlag(string goName) { GameObject newGO = new GameObject(); newGO.name = goName; UISprite sp = newGO.AddComponent<UISprite>(); sp.width = (int)(itemSize); sp.height = (int)(itemSize); newGO.transform.parent = transform; newGO.transform.localScale = Vector3.one; newGO.transform.localPosition = Vector3.zero; return newGO; } void sortChild(int startIndex) { List<Transform> childList = GetChildList(); firstFlag.transform.localPosition = Vector3.zero; lastFlag.transform.localPosition = Vector3.zero; SongeUtil.EnumVector dir = scrollDir == UIScrollView.Movement.Vertical ? SongeUtil.EnumVector.y : SongeUtil.EnumVector.x; firstFlag.SetLocalPos(0f, dir); lastFlag.SetLocalPos(-ItemSize * (dataProvider.Count == 0 ? 0 : dataProvider.Count - 1), dir); if (scrollDir == UIScrollView.Movement.Vertical) childList.Sort(UIGrid.SortVertical); else childList.Sort(UIGrid.SortHorizontal); for (int i = 0; i < childList.Count; i++) { childList[i].SetLocalPos((-startIndex - i) * itemSize, dir); Debug.Log("SetData, startIndex/i: " + startIndex + "/" + i + "\r\n" + "RealIndex/dataProvider.count: " + (startIndex + i) + "/" + dataProvider.Count); childList[i].gameObject.SendMessage("SetIndex", startIndex + i); //由于scrollerPanel返回的位置信息可能出界, 所以获取的index必须做范围判定 if ((startIndex + i) < dataProvider.Count && (startIndex + i) >= 0) childList[i].gameObject.SendMessage("SetData", dataProvider[startIndex + i]); } } int curIndex = -1; public void WrapContent() { int oldIndex = curIndex; List<Transform> childList = GetChildList(); Vector3[] corners = pPanel.worldCorners; for (int i = 0; i < 4; ++i) { Vector3 v = corners[i]; v = transform.InverseTransformPoint(v); corners[i] = v; } //当前位置 float curPosition = scrollDir == UIScrollView.Movement.Horizontal ? corners[2].x - ItemSize / 2f - pPanel.clipSoftness.x : corners[2].y - ItemSize / 2f - pPanel.clipSoftness.y; float rawCurPos = curPosition; //总长度 float totalLength = scrollDir == UIScrollView.Movement.Horizontal ? corners[2].x - corners[0].x : corners[2].y - corners[0].y; float softness = scrollDir == UIScrollView.Movement.Horizontal ? pPanel.clipSoftness.x : pPanel.clipSoftness.y; if (childList.Count * ItemSize < totalLength) { curPosition = Mathf.Clamp(curPosition, 0, totalLength - ItemSize - softness * 2f); curIndex = Mathf.FloorToInt(curPosition / ItemSize); sortChild(0); } else { curPosition = -curPosition; curPosition = Mathf.Max(curPosition, 0f); curIndex = Mathf.FloorToInt(curPosition / ItemSize); sortChild(curIndex); } //Debug.Log("cur(raw)/total: " + (int)curPosition + "(" + (int)rawCurPos + ")/" + totalLength + "\r\n" // + oldIndex + " -> " + curIndex + (curIndex != oldIndex ? " Warp!" : "")); } #endregion #region 测试 public bool TestModel = false; void testFunc() { if (!TestModel) return; if (Input.GetKeyDown(KeyCode.Alpha3)) TestAddData(); else if (Input.GetKeyDown(KeyCode.Alpha4)) TestRemoveData(); } [ContextMenu("TestAddData 测试添加数据")] void TestAddData() { AddData(Random.Range(0, 99).ToString()); } [ContextMenu("TestRemoveData 测试删除数据")] void TestRemoveData() { RemoveDataAt(0); } #endregion }
其中 ItemRenderer上需要挂载一个脚本接受数据, 该脚本需要有SetIndex(int index)和SetData(object data)两个方法
using UnityEngine; using System.Collections; public class TestItemRenderer : MonoBehaviour { int index = -1; void SetIndex(int _index) { index = _index; } public object data; void SetData(object _data) { if(data == _data) { //Debug.Log("重复数据: "+_data); } else { data = _data; string str = data is string ? data.ToString() : "非字符串类型: "+data; GetComponentInChildren<UILabel>().text = index+"."+str; } } }
时间: 2024-10-24 21:39:44