From 6eb915c129fc90c6f4c82ae097dd6ffad5239efc Mon Sep 17 00:00:00 2001 From: chai Date: Mon, 25 Jan 2021 14:28:30 +0800 Subject: +scripts --- .../Scripts/UI/LoopScrollView/LoopItemData.cs | 12 + .../Scripts/UI/LoopScrollView/LoopItemData.cs.meta | 8 + .../Scripts/UI/LoopScrollView/LoopItemObject.cs | 47 ++ .../UI/LoopScrollView/LoopItemObject.cs.meta | 8 + .../Scripts/UI/LoopScrollView/LoopScrollView.cs | 585 +++++++++++++++++++++ .../UI/LoopScrollView/LoopScrollView.cs.meta | 8 + 6 files changed, 668 insertions(+) create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs.meta create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs.meta create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs create mode 100644 Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs.meta (limited to 'Client/Assets/Scripts/UI/LoopScrollView') diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs new file mode 100644 index 00000000..f809ea78 --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs @@ -0,0 +1,12 @@ +using UnityEngine; +using System.Collections; +using XUtliPoolLib; + +/// +/// 与item对关联的数据类,具体的item的数据类一定继承它 +/// +//public class LoopItemData : ILoopItemData +//{ +// // *** + +//} diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs.meta b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs.meta new file mode 100644 index 00000000..4094a3ff --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemData.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c0d6527af18bd6643be0ed8270bd2e6d +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs new file mode 100644 index 00000000..dd3610e2 --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs @@ -0,0 +1,47 @@ +using UnityEngine; +using System.Collections; +using XUtliPoolLib; + +/// +/// item对像的封装类LoopItemObject,不要求具体的item类来继承它。 +/// 但我们要示具体的item对像一定要包含UIWidget组件。 +/// +[System.Serializable] +public class LoopItemObject:ILoopItemObject +{ + /// + /// The widget. + /// + public UIWidget widget; + + /// + /// 本item,在实际整个scrollview中的索引位置, + /// 即对就数据,在数据列表中的索引 + /// + public int _dataIndex = -1; + + public int dataIndex + { + get { return _dataIndex; } + set { _dataIndex = value; } + } + + public bool isVisible() + { + LoopScrollView sc = NGUITools.FindInParents(widget.gameObject); + if (sc != null) return sc.IsVisible(this); + return false; + } + + public GameObject GetObj() + { + return widget != null ? widget.gameObject : null; + } + + + public void SetHeight(int height) + { + widget.height = height; + } + +} diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs.meta b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs.meta new file mode 100644 index 00000000..61c8a763 --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopItemObject.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea09ef10b830b5a418a68277930e5d7c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs b/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs new file mode 100644 index 00000000..e6785aa2 --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs @@ -0,0 +1,585 @@ +using UnityEngine; +using System.Collections.Generic; +using XUtliPoolLib; +using System.Collections; + +/// +/// 这个类主要做了一件事,就是优化了,NGUI UIScrollView 在数据量很多都时候, +/// 创建过多都GameObject对象,造成资源浪费. +/// +public class LoopScrollView : MonoBehaviour, ILoopScrollView +{ + + public enum ArrangeDirection + { + Left_to_Right, + Right_to_Left, + Up_to_Down, + Down_to_Up, + } + /// + /// items的排列方式 + /// + public ArrangeDirection arrangeDirection = ArrangeDirection.Up_to_Down; + + /// + /// 列表单项模板 + /// + public GameObject itemPrefab; + + /// + /// The items list. + /// + public List itemsList; + + /// + /// The datas list. + /// + public List datasList; + public UIScrollView scrollView; + public GameObject itemParent; + + /// + /// itemsList的第一个元素 + /// + LoopItemObject firstItem; + + /// + /// itemsList的最后一个元素 + /// + LoopItemObject lastItem; + + /// + /// 第一item的起始位置 + /// + public Vector3 itemStartPos = Vector3.zero; + + public DelegateHandler OnItemInit; + + /// + /// 菜单项间隙 + /// + public float gapDis = 0f; + + private Vector3 m_TopLeft, m_BtmRight; + + private System.Action onDragFinish; + + /// + /// 预制可见个数 会影响边缘的回弹spring + /// + public int m_maxViewCnt = 2; + + bool mTop = true; + + // 对象池 + Queue itemLoop = new Queue(); + + void Awake() + { + if (itemPrefab == null || scrollView == null || itemParent == null) + { + Debug.LogError("LoopScrollView.Awake not set value in inspector!"); + } + if (m_maxViewCnt < 2) + { + Debug.LogError("Make sure your view cnt more than 2!"); + } + + if (arrangeDirection == ArrangeDirection.Up_to_Down || + arrangeDirection == ArrangeDirection.Down_to_Up) + { + scrollView.movement = UIScrollView.Movement.Vertical; + } + else + { + scrollView.movement = UIScrollView.Movement.Horizontal; + } + scrollView.onDragFinished = OnDragFinish; + } + + void Start() + { + UIPanel panel = scrollView.panel; + Vector4 v4 = scrollView.panel.baseClipRegion; + Vector3 v3 = scrollView.transform.localPosition; + v3 = new Vector3(v3.x + panel.clipOffset.x + v4.x, v3.y + panel.clipOffset.y + v4.y, v3.z); + + Vector3 topleft = new Vector3(v3.x - v4.z / 2, v3.y + v4.w / 2, v3.z); + Vector3 btmright = new Vector3(v3.x + v4.z / 2, v3.y - v4.w / 2, v3.z); + + m_TopLeft = scrollView.transform.parent.localToWorldMatrix.MultiplyPoint(topleft); + m_BtmRight = scrollView.transform.parent.localToWorldMatrix.MultiplyPoint(btmright); + //XDebug.singleton.AddGreenLog("topleft =" + topleft + "btmright" + btmright, "m_TopLeft=" + m_TopLeft + "...." + "m_BtmRight" + m_BtmRight); + } + + private void OnDragFinish() + { + if (lastItem != null && IsScrollLast()) + { + //XDebug.singleton.AddGreenLog("xxxxxxxxxxxxxxxxxxxxxxxxxx"); + if (onDragFinish != null) onDragFinish(); + } + } + + void FixedUpdate() + { + Validate(); + if (frameIndex <= 4) + { + frameIndex = frameIndex << 1; + if (frameIndex == 1 << 2) + { + YiledScroll(); //第二帧调用 + } + } + } + + + private int frameIndex = 1 << 10; + /// + /// Init the specified datas. + /// + /// Datas. + public void Init(List datas, DelegateHandler onItemInitCallback, System.Action dragFinish, int pivot = 0, bool forceRefreshPerTime = false) + { + mTop = pivot == 0; + onDragFinish = dragFinish; + + if (forceRefreshPerTime) + { + RefeshPerTime(datas, onItemInitCallback, pivot); + } + else + { + if (IsEqual(datas)) + { + // Debug.Log("equal, do noting!"); + } + else if (IsAddNew(datas)) + { + AddItem(datas[datas.Count - 1]); + } + else if (UpdateNewOne(datas)) + { + UpdateItem(datas[datas.Count - 1]); + } + else + { + RefeshPerTime(datas, onItemInitCallback, pivot); + } + } + frameIndex = 1; + } + + private void RefeshPerTime(List datas,DelegateHandler onItemInitCallback,int pivot) + { + mTop = pivot == 0; + this.OnItemInit = onItemInitCallback; + Resetloop(); + datasList = datas; + Validate(); + ResetScroll(); + int cnt = Mathf.Min(m_maxViewCnt, datasList.Count); + for (int i = 0; i < cnt; i++) Validate(); + } + + void YiledScroll() + { + ResetScroll(); + CheckBTM(); + } + + + private void Resetloop() + { + if (itemsList != null) + { + for (int i = 0; i < itemsList.Count; i++) + { + PutItemToLoop(itemsList[i]); + } + itemsList.Clear(); + } + if (datasList != null) datasList.Clear(); + } + + private bool activable { get { return gameobject.activeSelf; } } + + public void SetPivot(bool istop){ } + + + public GameObject GetTpl() + { + return itemPrefab; + } + + private bool IsAllInvisible() + { + bool all_invisible = true; + for (int i = 0; i < itemsList.Count; i++) + { + if (IsVisible(itemsList[i])) + { + all_invisible = false; + break; + } + } + return all_invisible; + } + + /// + /// 检验items的两端是否要补上或删除 + /// + private void Validate() + { + if (datasList == null || datasList.Count == 0) return; + // 如果itemsList还不存在 + if (itemsList == null || itemsList.Count == 0) + { + itemsList = new List(); + LoopItemObject item = GetItemFromLoop(); + if (mTop) InitItem(item, 0, datasList[0]); //默认起始0 + else InitItem(item, datasList.Count - 1, datasList[datasList.Count - 1]); + firstItem = lastItem = item; + itemsList.Add(item); + ResetScroll(); + } + if (IsAllInvisible()) return; + + // 先判断前端是否要增减 + if (IsVisible(firstItem)) + { + // 判断要不要在它的前面补充一个item + if (firstItem.dataIndex > 0) + { + LoopItemObject item = GetItemFromLoop(); + int index = firstItem.dataIndex - 1; + AddToFront(firstItem, item, index, datasList[index]); + firstItem = item; + itemsList.Insert(0, item); + } + } + else + { + // 判断要不要将它移除 + // 条件:自身是不可见的; 且它后一个item也是不可见的(或被被裁剪过半的).隐含条件itemsList.Count>=2. + if (itemsList.Count > m_maxViewCnt && !IsVisible(itemsList[0]) && !IsVisible(itemsList[1])) + { + itemsList.Remove(firstItem); + PutItemToLoop(firstItem); + firstItem = itemsList[0]; + } + } + + // 再判断后端是否要增减 + if (IsVisible(lastItem)) + { + // 判断要不要在它的后面补充一个item + if (lastItem.dataIndex < datasList.Count - 1) + { + LoopItemObject item = GetItemFromLoop(); + int index = lastItem.dataIndex + 1; + AddToBack(lastItem, item, index, datasList[index]); + lastItem = item; + itemsList.Add(item); + } + } + else + { + // 判断要不要将它移除 + // 条件:自身是不可见的;且它前一个item也是不可见的(或被被裁剪过半的).隐含条件itemsList.Count>=2. + if (itemsList.Count > m_maxViewCnt + && !IsVisible(itemsList[itemsList.Count - 1]) + && !IsVisible(itemsList[itemsList.Count - 2])) + { + itemsList.Remove(lastItem); + PutItemToLoop(lastItem); + lastItem = itemsList[itemsList.Count - 1]; + } + } + } + + private bool isPivotLast { get { return lastItem != null && datasList != null && datasList.Count > 1 && lastItem.dataIndex == datasList.Count - 1; } } + + private bool IsEqual(List datas) + { + bool equal = true; + if (datas != null && datasList != null && datas.Count == datasList.Count) + { + for (int i = 0; i < datasList.Count; i++) + { + if (datasList[i].LoopID != datas[i].LoopID) + { + equal = false; + break; + } + } + } + else + { + equal = false; + } + return equal; + } + + // 在列表最后更新一个 + private bool UpdateNewOne(List datas) + { + bool isnew = true; + if (datas != null && isPivotLast && datas.Count == datasList.Count ) + { + int iret = Mathf.Min(4, datasList.Count - 1); //只比较前四个是为了快速,比较新增和刷新整个list效果一样 + for (int i = 0; i < iret; i++) + { + if (datasList[i + 1].LoopID != datas[i].LoopID) + { + isnew = false; + break; + } + } + } + else + { + isnew = false; + } + return isnew; + } + + // 在列表最后新增一个 + private bool IsAddNew(List datas) + { + bool isnew = true; + if (datas != null && isPivotLast && datas.Count - datasList.Count == 1) + { + int iret = Mathf.Min(4, datasList.Count); //只比较前四个是为了快速,比较新增和刷新整个list效果一样 + for (int i = 0; i < iret; i++) + { + if (datasList[i].LoopID != datas[i].LoopID) + { + isnew = false; + break; + } + } + } + else + { + isnew = false; + } + return isnew; + } + + // 此方法主要是为了避免新加item而整体进行刷新 + public void AddItem(LoopItemData data) + { + datasList.Add(data); + LoopItemObject item = GetItemFromLoop(); + int index = datasList.Count - 1; + AddToBack(lastItem, item, index, datasList[index]); + lastItem = item; + itemsList.Add(item); + } + + public void UpdateItem(LoopItemData data) + { + if (datasList.Count > 0) datasList.RemoveAt(0); + if (itemsList.Contains(firstItem)) itemsList.Remove(firstItem); + PutItemToLoop(firstItem); + if (itemsList.Count > 0) firstItem = itemsList[0]; + AddItem(data); + } + + public bool IsVisible(LoopItemObject obj) + { + if (obj == null || obj.widget == null || obj.widget.transform == null) return false; + return IsVisible(obj.widget.transform); + } + + private bool IsVisible(Transform tran) + { + bool x = tran.position.x >= m_TopLeft.x && tran.position.x <= m_BtmRight.x; + bool y = tran.position.y >= m_BtmRight.y && tran.position.y <= m_TopLeft.y; + return x && y; + } + + public GameObject gameobject + { + get { return this.gameObject; } + } + + + public bool IsScrollLast() + { + if (lastItem != null && lastItem.widget != null) + { + return IsVisible(lastItem); + } + else + { + return true; + } + } + + + [ContextMenu("reset pos")] + public void ResetScroll() + { + scrollView.ResetPosition(mTop ? 0f : 1f); + } + + public void SetClipSize(Vector2 size) + { + Vector4 reg = scrollView.panel.baseClipRegion; + reg.z = size.x; + reg.w = size.y; + scrollView.panel.baseClipRegion = reg; + } + + public void SetDepth(int depth) + { + if (scrollView.panel != null) scrollView.panel.depth = depth; + } + + public GameObject GetFirstItem() + { + return firstItem.GetObj(); + } + + public GameObject GetLastItem() + { + return lastItem.GetObj(); + } + + /// + /// 构造一个 item 对象 + /// + LoopItemObject CreateItem() + { + GameObject go = NGUITools.AddChild(itemParent, itemPrefab); + UIWidget widget = go.GetComponent(); + LoopItemObject item = new LoopItemObject(); + item.widget = widget; + if(!go.activeSelf) go.SetActive(true); + return item; + } + + /// + /// 用数据列表来初始化scrollview + /// + void InitItem(LoopItemObject item, int dataIndex, LoopItemData data) + { + item.dataIndex = dataIndex; + if (OnItemInit != null) + { + OnItemInit(item as ILoopItemObject, data); + } + item.widget.transform.localPosition = itemStartPos; + } + + /// + /// 在itemsList前面补上一个item + /// + private void AddToFront(LoopItemObject priorItem, LoopItemObject newItem, int newIndex, LoopItemData newData) + { + if (!CheckItem(priorItem) || !CheckItem(newItem)) return; + InitItem(newItem, newIndex, newData); + // 计算新item的位置 + if (scrollView.movement == UIScrollView.Movement.Vertical) + { + float offsetY = priorItem.widget.height * 0.5f + gapDis + newItem.widget.height * 0.5f; + if (arrangeDirection == ArrangeDirection.Down_to_Up) offsetY *= -1f; + newItem.widget.transform.localPosition = priorItem.widget.cachedTransform.localPosition + new Vector3(0f, offsetY, 0f); + } + else + { + float offsetX = priorItem.widget.width * 0.5f + gapDis + newItem.widget.width * 0.5f; + if (arrangeDirection == ArrangeDirection.Right_to_Left) offsetX *= -1f; + newItem.widget.transform.localPosition = priorItem.widget.cachedTransform.localPosition - new Vector3(offsetX, 0f, 0f); + } + } + + /// + /// 在itemsList后面补上一个item + /// + private void AddToBack(LoopItemObject backItem, LoopItemObject newItem, int newIndex, LoopItemData newData) + { + if (!CheckItem(backItem) || !CheckItem(newItem)) return; + InitItem(newItem, newIndex, newData); + // 计算新item的位置 + if (scrollView.movement == UIScrollView.Movement.Vertical) + { + float offsetY = backItem.widget.height * 0.5f + gapDis + newItem.widget.height * 0.5f; + if (arrangeDirection == ArrangeDirection.Down_to_Up) offsetY *= -1f; + newItem.widget.transform.localPosition = backItem.widget.cachedTransform.localPosition - new Vector3(0f, offsetY, 0f); + } + else + { + float offsetX = backItem.widget.width * 0.5f + gapDis + newItem.widget.width * 0.5f; + if (arrangeDirection == ArrangeDirection.Right_to_Left) offsetX *= -1f; + newItem.widget.transform.localPosition = backItem.widget.cachedTransform.localPosition + new Vector3(offsetX, 0f, 0f); + } + } + + + private void CheckBTM() + { + if (mTop && lastItem != null + && lastItem.widget != null + && arrangeDirection == ArrangeDirection.Up_to_Down) + { + if (m_BtmRight.y > lastItem.widget.transform.position.y) + { + mTop = false; + ResetScroll(); + } + } + } + + + #region 对象池性能相关 + /// + /// 从对象池中取行一个item + /// + /// The item from loop. + LoopItemObject GetItemFromLoop() + { + LoopItemObject item = null; + while (itemLoop.Count > 0 && !CheckItem(item)) + { + item = itemLoop.Dequeue(); + } + if (item == null) + { + item = CreateItem(); + } + if (CheckItem(item) && !item.widget.gameObject.activeSelf) item.widget.gameObject.SetActive(true); + return item; + } + + private bool CheckItem(LoopItemObject item) + { + return item != null && item.widget != null && item.widget.gameObject != null; + } + + /// + /// 将要移除的item放入对象池中 + /// --这个里我保证这个对象池中存在的对象不超过3个 + /// + /// Item. + void PutItemToLoop(LoopItemObject item) + { + if (!CheckItem(item)) return; + if (itemLoop.Count >= 3) + { + DestroyImmediate(item.widget.gameObject); + return; + } + item.dataIndex = -1; + item.widget.gameObject.SetActive(false); + itemLoop.Enqueue(item); + } + #endregion + +} + + diff --git a/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs.meta b/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs.meta new file mode 100644 index 00000000..e5cfe7ea --- /dev/null +++ b/Client/Assets/Scripts/UI/LoopScrollView/LoopScrollView.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9232c4b66d37744e91e6f9258a14a22 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: -- cgit v1.1-26-g67d0