using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; /// /// 滚动列表优化 /// [DisallowMultipleComponent] public class UIWarpContent : MonoBehaviour { /// /// 元素初始化 /// /// 游戏对象 /// 数据索引 public delegate void OnInitializeItem(GameObject go, int dataIndex); /// /// 元素初始化 /// public OnInitializeItem onInitializeItem; /// /// 排列 /// public enum Arrangement { /// /// 横 /// Horizontal, /// /// 竖 /// Vertical, } /// /// 排列 /// public Arrangement arrangement = Arrangement.Horizontal; /// /// Maximum children per line. /// If the arrangement is horizontal, this denotes the number of columns. /// If the arrangement is vertical, this stands for the number of rows. /// [Range(1, 50)] public int maxPerLine = 1; /// /// The width of each of the cells. /// public float cellWidth = 200f; /// /// The height of each of the cells. /// public float cellHeight = 200f; /// /// The Width Space of each of the cells. /// [Range(0, 50)] public float cellWidthSpace = 0f; /// /// The Height Space of each of the cells. /// [Range(0, 50)] public float cellHeightSpace = 0f; /// /// 窗口能容纳的 行数 或 列数 /// [Range(0, 30)] public int viewCount = 5; /// /// scroll view 对象 /// public ScrollRect scrollRect = null; /// /// content 对象 /// public RectTransform content = null; /// /// 元素预制物体 /// public GameObject goItemPrefab = null; /// /// 数据的数量 /// private int mDataCount = 0; /// /// 当前所处的行数 /// private int curScrollPerLineIndex = -1; /// /// 正在使用中的元素 /// private List listItem = new List(); /// /// 未在使用中的元素 /// private Queue unUseItem = new Queue(); /// /// 初始化 /// private void Awake() { } /// /// 初始化 /// /// 数据的数量 public void Init(int dataCount) { if (scrollRect == null || content == null || goItemPrefab == null) { Debug.LogError("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab); return; } // 不显示元素预制物体 goItemPrefab.SetActive(false); scrollRect.onValueChanged.RemoveAllListeners(); scrollRect.onValueChanged.AddListener(onValueChanged); // 如果数据数量为 0 ,没有初始化的必要 if (dataCount <= 0) { return; } // 设置数据的数量,并计算 content 的尺寸 setDataCount(dataCount); unUseItem.Clear(); listItem.Clear(); setUpdateRectItem(0); } /// /// 数据改变了,比如切换分类功能,全部,前排,中排,后排,元素要重新获取数据 /// /// 数据的数量 /// 是否返回初始位置 public void DataChange(int dataCount, bool bResetPos) { if (scrollRect == null || content == null || goItemPrefab == null) { Debug.LogError("异常:请检测<" + gameObject.name + ">对象上UIWarpContent对应ScrollRect、Content、GoItemPrefab 是否存在值...." + scrollRect + " _" + content + "_" + goItemPrefab); return; } // 重新设置数量,重新计算 content 的尺寸 setDataCount(dataCount); // 因为数据改变了,元素要重新获取数据 for (int i = listItem.Count - 1; i >= 0; i--) { UIWarpContentItem item = listItem[i]; item.Index = -1; listItem.Remove(item); unUseItem.Enqueue(item); } if (bResetPos) { content.anchoredPosition = Vector3.zero; } // 计算当前所处的行数,刷新 int _curScrollPerLineIndex = getCurScrollPerLineIndex(); setUpdateRectItem(_curScrollPerLineIndex); } /// /// 设置数据的数量,并重新计算 content 的尺寸 /// /// 数据的数量 private void setDataCount(int count) { if (mDataCount == count) { return; } mDataCount = count; // 计算 content 的尺寸 setUpdateContentSize(); } /// /// scroll view 拖动时触发 /// /// 拖动数值 private void onValueChanged(Vector2 vt2) { int _curScrollPerLineIndex = getCurScrollPerLineIndex(); if (_curScrollPerLineIndex == curScrollPerLineIndex) { return; } setUpdateRectItem(_curScrollPerLineIndex); } /// /// 刷新元素 /// /// 当前所处的行数 private void setUpdateRectItem(int scrollPerLineIndex) { if (scrollPerLineIndex < 0) { return; } curScrollPerLineIndex = scrollPerLineIndex; int startDataIndex = curScrollPerLineIndex * maxPerLine; int endDataIndex = (curScrollPerLineIndex + viewCount) * maxPerLine; // 移除 for (int i = listItem.Count - 1; i >= 0; i--) { UIWarpContentItem item = listItem[i]; int index = item.Index; if (index < startDataIndex || index >= endDataIndex) { item.Index = -1; listItem.Remove(item); unUseItem.Enqueue(item); } } // 显示 for (int dataIndex = startDataIndex; dataIndex < endDataIndex; dataIndex++) { if (dataIndex >= mDataCount) { continue; } if (isExistDataByDataIndex(dataIndex)) { continue; } createItem(dataIndex); } } /// /// 添加当前数据索引数据 /// /// 数据索引 public void AddItem(int dataIndex) { if (dataIndex < 0 || dataIndex > mDataCount) { return; } // 检测是否需添加gameObject bool isNeedAdd = false; for (int i = listItem.Count - 1; i >= 0; i--) { UIWarpContentItem item = listItem[i]; if (item.Index >= (mDataCount - 1)) { isNeedAdd = true; break; } } setDataCount(mDataCount + 1); if (isNeedAdd) { for (int i = 0; i < listItem.Count; i++) { UIWarpContentItem item = listItem[i]; int oldIndex = item.Index; if (oldIndex >= dataIndex) { item.Index = oldIndex + 1; } item = null; } setUpdateRectItem(getCurScrollPerLineIndex()); } else { // 重新刷新数据 for (int i = 0; i < listItem.Count; i++) { UIWarpContentItem item = listItem[i]; int oldIndex = item.Index; if (oldIndex >= dataIndex) { item.Index = oldIndex; } item = null; } } } /// /// 删除当前数据索引下数据 /// /// 数据索引 public void DelItem(int dataIndex) { if (dataIndex < 0 || dataIndex >= mDataCount) { return; } ////删除item逻辑三种情况 ////1.只更新数据,不销毁gameObject,也不移除gameobject ////2.更新数据,且移除gameObject,不销毁gameObject ////3.更新数据,销毁gameObject bool isNeedDestroyGameObject = (listItem.Count >= mDataCount); setDataCount(mDataCount - 1); for (int i = listItem.Count - 1; i >= 0; i--) { UIWarpContentItem item = listItem[i]; int oldIndex = item.Index; if (oldIndex == dataIndex) { listItem.Remove(item); if (isNeedDestroyGameObject) { GameObject.Destroy(item.gameObject); } else { item.Index = -1; unUseItem.Enqueue(item); } } if (oldIndex > dataIndex) { item.Index = oldIndex - 1; } } setUpdateRectItem(getCurScrollPerLineIndex()); } /// /// 根据数据索引计算在 content 下的 local position /// /// 数据索引 /// 位置 public Vector3 getLocalPositionByIndex(int index) { if (index < 0) { return new Vector3(-2000.0f, 2000.0f, 0.0f); } float x = 0f; float y = 0f; float z = 0f; switch (arrangement) { // 水平方向 case Arrangement.Horizontal: { x = (index / maxPerLine) * (cellWidth + cellWidthSpace); y = -(index % maxPerLine) * (cellHeight + cellHeightSpace); } break; // 垂着方向 case Arrangement.Vertical: { x = (index % maxPerLine) * (cellWidth + cellWidthSpace); y = -(index / maxPerLine) * (cellHeight + cellHeightSpace); } break; default: { // 不需要处理 } break; } return new Vector3(x, y, z); } /// /// 创建元素 /// /// 数据索引 private void createItem(int dataIndex) { UIWarpContentItem item = null; if (unUseItem.Count > 0) { item = unUseItem.Dequeue(); } else { GameObject temp = addChild(goItemPrefab, content); item = temp.GetComponent(); if (item == null) { AssemblyHelper.Instance.BindScript("UIWarpContentItem", temp); item = temp.GetComponent(); } } item.WarpContent = this; item.Index = dataIndex; listItem.Add(item); } /// /// 当前数据是否存在 List 中 /// /// 数据索引 /// 是否已存在 private bool isExistDataByDataIndex(int dataIndex) { if (listItem == null || listItem.Count <= 0) { return false; } for (int i = 0; i < listItem.Count; i++) { if (listItem[i].Index == dataIndex) { return true; } } return false; } /// /// 根据Content偏移,计算当前开始显示所在数据列表中的行或列 /// /// 行数 或 列数 private int getCurScrollPerLineIndex() { int result = 0; switch (arrangement) { // 水平方向 case Arrangement.Horizontal: { result = Mathf.FloorToInt(-(content.anchoredPosition.x) / (cellWidth + cellWidthSpace)); } break; // 垂着方向 case Arrangement.Vertical: { result = Mathf.FloorToInt((content.anchoredPosition.y) / (cellHeight + cellHeightSpace)); } break; default: { // 不需要处理 } break; } if (result < 0) { result = 0; } return result; } /// /// 计算 content 的尺寸 /// private void setUpdateContentSize() { // 行数,向上取整 int lineCount = Mathf.CeilToInt((float)mDataCount / maxPerLine); switch (arrangement) { case Arrangement.Horizontal: { content.sizeDelta = new Vector2((cellWidth * lineCount) + (cellWidthSpace * (lineCount - 1)), content.sizeDelta.y); } break; case Arrangement.Vertical: { content.sizeDelta = new Vector2(content.sizeDelta.x, (cellHeight * lineCount) + (cellHeightSpace * (lineCount - 1))); } break; default: { // 不需要处理 } break; } } /// /// 实例化预设对象 、添加实例化对象到指定的子对象下 /// /// 元素预制物体 /// 父节点 /// 新创建的元素对象 private GameObject addChild(GameObject goPrefab, Transform parent) { if (goPrefab == null || parent == null) { Debug.LogError("异常。UIWarpContent.cs addChild(goPrefab = null || parent = null)"); return null; } GameObject goChild = GameObject.Instantiate(goPrefab) as GameObject; goChild.transform.SetParent(parent, false); goChild.SetActive(true); return goChild; } /// /// 销毁 /// private void OnDestroy() { scrollRect = null; content = null; goItemPrefab = null; onInitializeItem = null; listItem.Clear(); unUseItem.Clear(); listItem = null; unUseItem = null; } public void SetContentToEnd() { float x = 0, y = 0; RectTransform temp = scrollRect.GetComponent(); if (scrollRect.content.sizeDelta.x > temp.sizeDelta.x) { x = scrollRect.content.sizeDelta.x - temp.sizeDelta.x; } if (scrollRect.content.sizeDelta.y > temp.sizeDelta.y) { y = scrollRect.content.sizeDelta.y - temp.sizeDelta.y; } switch (arrangement) { case Arrangement.Horizontal: { y = 0; } break; case Arrangement.Vertical: { x = 0; } break; } Debug.LogError("end::" + x + " " + y); scrollRect.content.anchoredPosition = new Vector2(x, y); } }