using System; using System.Collections.Generic; using Fort23.Core; using Fort23.UTool; using UnityEngine; using UnityEngine.UI; public class ScrollList : MonoBehaviour, IScrollListContent { public enum LayoutType { Left, Center, } // public Camera Camera; public ScrollRect ScrollRect; public Vector2 ScrollRectSizedata; public Vector2Int Page = new Vector2Int(1, 1); public float bottom; public RectTransform wdiget; public Vector2 sizeData = new Vector2(); public List hindWidget = new List(); public List showWidget = new List(); protected int currIndex; // public Vector2 offSize; public bool isCustomizeHeight; public Vector2 posOff; public bool isDelay; public long delayTime; public bool isAdaptationWidth; private bool _isStartDelay; private bool hasAddedOffset = false; protected IScrollListContent m_scrollListContent; // private void Awake() // { // Init(this, 100); // } protected RectTransform myRectTransform; private float addY; protected int m_startIndex; private int _maxSize; private Vector3 onePos; private CTask isAwaitInitFinis; private bool _isBreak; private Vector2 _vel; public LayoutType layoutType = LayoutType.Left; public float overflowDistance; private void Awake() { if (myRectTransform == null) { myRectTransform = gameObject.GetComponent(); } onePos = myRectTransform.localPosition; } public void Break() { _isBreak = true; } public void Clear() { currIndex = 0; IScorllListWidget[] listWidgets = showWidget.ToArray(); for (int i = 0; i < listWidgets.Length; i++) { HindIWidget(listWidgets[i]); } showWidget.Clear(); ScrollRect.content.localPosition = onePos; ScrollRect.StopMovement(); } /// /// 初始化 /// /// /// /// public async CTask Init(IScrollListContent scrollListContent, int maxSize, int startIndex = 0) { if (startIndex < 0) { startIndex = 0; } if (ScrollRect == null) { ScrollRect = transform.GetComponentInParent(); } RectTransform root = ScrollRect.GetComponent(); addY = 0; _isBreak = true; using (await CoroutineLockComponent.Instance.Wait(this.GetInstanceID().ToString())) { if (myRectTransform == null) { myRectTransform = gameObject.GetComponent(); } Clear(); _maxSize = maxSize; if (isAdaptationWidth) { ScrollRect.SetLayoutHorizontal(); await TimerComponent.Instance.WaitAsync(50); float fx = ((root.rect.size.x - posOff.x) / sizeData.x); int c = (int)fx; if (fx % 1 >= 0.9f) { c += 1; } Page.y = c; } if (layoutType == LayoutType.Center) { await TimerComponent.Instance.WaitAsync(30); float w = root.rect.width / 2; posOff = new Vector2(w, posOff.y); } await TimerComponent.Instance.WaitAsync(10); Vector2 lasetPos = myRectTransform.anchoredPosition; if (ScrollRect.horizontal) { myRectTransform.sizeDelta = new Vector2(sizeData.x * startIndex, myRectTransform.sizeDelta.y); myRectTransform.anchoredPosition = new Vector2(sizeData.x * startIndex * -1, myRectTransform.anchoredPosition.y); lasetPos = myRectTransform.anchoredPosition; m_startIndex = 0; currIndex = startIndex; } else if (ScrollRect.vertical) { // m_startIndex = startIndex; currIndex = startIndex; // Vector2 targetSizeDelta = CalculateTargetSizeDelta(startIndex); Vector2 targetSizeDelta = CalculateBottomSizeDelta(startIndex); targetSizeDelta += new Vector2(0, bottom); lasetPos = CalculateTargetPosition(startIndex); myRectTransform.sizeDelta = new Vector2(sizeData.x, targetSizeDelta.y); } _isBreak = false; m_scrollListContent = scrollListContent; CTaskAwaitBuffer cTaskAwaitBuffer = new CTaskAwaitBuffer(); CTask ctask = Cread(currIndex, false); cTaskAwaitBuffer.AddTask(ctask); currIndex++; if (isDelay) { _isStartDelay = true; } await cTaskAwaitBuffer.WaitAll(); ScrollRect.SetLayoutVertical(); myRectTransform.anchoredPosition = lasetPos; ScrollRect.StopMovement(); await onValueChanged(Vector2.zero, true); ScrollRect.onValueChanged.RemoveListener(onValueChanged2); ScrollRect.onValueChanged.AddListener(onValueChanged2); _isStartDelay = false; } } public void RemoveWidget(IScorllListWidget listWidget) { bool isHide = false; for (int i = 0; i < showWidget.Count; i++) { if (showWidget[i] == listWidget || isHide) { isHide = true; HindIWidget(showWidget[i]); i--; } } if (isHide) { if (showWidget.Count <= 0) { currIndex = 0; CTask ctask = Cread(currIndex, false); currIndex++; } onValueChanged(Vector2.zero, false); } } protected async CTask Cread(int index, bool isUp) { int showIndex = index - m_startIndex; if (showWidget.Count > 100) { return null; } IScorllListWidget widget = await GetWidget(index); if (widget == null) { return null; } showWidget.Add(widget); int xoff = 0; int yoff = 0; if (ScrollRect.horizontal) { xoff = showIndex / Page.x; yoff = showIndex % Page.x; } else if (ScrollRect.vertical) { if (showIndex >= 0 || isCustomizeHeight) { yoff = showIndex / Page.y; xoff = showIndex % Page.y; } else { { xoff = showIndex % (Page.y + 1); yoff = showIndex / (Page.y + 1); } if (showIndex < 0) { xoff = Page.y - Math.Abs(xoff); yoff -= 1; } } } widget.Transform.gameObject.name = index.ToString(); widget.Transform.SetParent(transform); widget.Transform.localScale = Vector3.one; if (!isCustomizeHeight) { widget.Transform.anchoredPosition = new Vector3(xoff * sizeData.x + posOff.x, -yoff * sizeData.y - addY - posOff.y); } else { float y = 0; if (isUp) { y = -5000000; } else { y = 5000000; } IScorllListWidget yWidget = null; for (int i = 0; i < showWidget.Count; i++) { IScorllListWidget scorllListWidget = showWidget[i]; int c = scorllListWidget.index - m_startIndex; int lastY = c / Page.y; if (isUp) { if (lastY - yoff == 1) { float currY = scorllListWidget.Transform.localPosition.y; if (y < currY) { yWidget = scorllListWidget; y = currY; } } } else { if (yoff - lastY == 1) { float currY = scorllListWidget.Transform.localPosition.y; if (y > currY) { yWidget = scorllListWidget; y = currY; } } } } if (yWidget == null) { widget.Transform.localPosition = new Vector3(xoff * sizeData.x, -yoff * sizeData.y); } else { if (isUp) { Vector3 pos = new Vector3(0, y + widget.GetSize().y + sizeData.y); widget.Transform.localPosition = pos; } else { Vector3 pos = new Vector3(0, y - yWidget.GetSize().y - sizeData.y); widget.Transform.localPosition = pos; } } } if (ScrollRect.horizontal) { if (widget.Transform.localPosition.x > 0) { float x = widget.Transform.localPosition.x + sizeData.x; if (x < myRectTransform.sizeDelta.x) { x = myRectTransform.sizeDelta.x; } else { myRectTransform.sizeDelta = new Vector2(x + overflowDistance, myRectTransform.sizeDelta.y); } } // else // { // m_startIndex--; // float x = Math.Abs(widget.Transform.localPosition.x); // myRectTransform.sizeDelta += new Vector2(x, // 0); // MoveX(x); // } } else { float y = 0; if (isCustomizeHeight) { y = Math.Abs(widget.Transform.localPosition.y) + widget.GetSize().y; } else { y = Math.Abs(widget.Transform.localPosition.y) + sizeData.y; } if (widget.Transform.localPosition.y > 0) { if (isCustomizeHeight) { float addY = widget.GetSize().y + sizeData.y; myRectTransform.sizeDelta += new Vector2(0, addY); if (index >= _maxSize && !hasAddedOffset) { myRectTransform.sizeDelta += new Vector2(0, bottom); hasAddedOffset = true; } MoveY(addY); } else { myRectTransform.sizeDelta += new Vector2(0, sizeData.y); if (index >= _maxSize && !hasAddedOffset) { myRectTransform.sizeDelta += new Vector2(0, bottom); hasAddedOffset = true; } MoveY(sizeData.y); } } else { if (y > myRectTransform.sizeDelta.y) { myRectTransform.sizeDelta = new Vector2(myRectTransform.sizeDelta.x, y); } if (index >= _maxSize && !hasAddedOffset) { myRectTransform.sizeDelta += new Vector2(0, bottom); hasAddedOffset = true; } } } widget.Transform.anchorMax = Vector2.up; widget.Transform.anchorMin = Vector2.up; widget.Transform.pivot = Vector2.up; return widget; } protected void MoveY(float y) { ScrollRect.StopMovement(); addY += y; ScrollRect.content.localPosition += new Vector3(0, y, 0); for (int i = 0; i < showWidget.Count; i++) { IScorllListWidget scorllListWidget = showWidget[i]; Vector3 pos = scorllListWidget.Transform.localPosition; scorllListWidget.Transform.localPosition = new Vector3(pos.x, pos.y - y, pos.z); } } protected void MoveX(float x) { ScrollRect.StopMovement(); // addY += y; myRectTransform.anchoredPosition -= new Vector2(x, 0); // ScrollRect.content.localPosition += new Vector3(x, 0, 0); for (int i = 0; i < showWidget.Count; i++) { IScorllListWidget scorllListWidget = showWidget[i]; Vector3 pos = scorllListWidget.Transform.localPosition; scorllListWidget.Transform.localPosition = new Vector3(pos.x + x, pos.y, pos.z); } } protected void HindWidget() { if (showWidget.Count < 2) { return; } for (int i = 0; i < showWidget.Count; i++) { bool isShow = false; bool isHind = false; GetIsShow(showWidget[i], ref isShow, ref isHind); if (!isHind) //已经移除到了隐藏区域 { HindIWidget(showWidget[i]); i--; if (showWidget.Count < 2) { return; } } } } public async void onValueChanged2(Vector2 pos) { onValueChanged(pos, false); } public async CTask onValueChanged(Vector2 pos, bool isInit) { if (_isBreak) { return; } if (!isInit && (_isBreak || _isStartDelay)) { return; } // _vel = ScrollRect.velocity; using (await CoroutineLockComponent.Instance.Wait("123")) { if (_isBreak) { return; } hasAddedOffset = false; // ScrollRect.StopMovement(); HindWidget(); IScorllListWidget minWidget = null; IScorllListWidget maxWdiget = null; for (int i = 0; i < showWidget.Count; i++) { if (minWidget == null) { minWidget = showWidget[i]; maxWdiget = showWidget[i]; } else { if (minWidget.index > showWidget[i].index) { minWidget = showWidget[i]; } if (maxWdiget.index < showWidget[i].index) { maxWdiget = showWidget[i]; } } } if (minWidget == null || maxWdiget == null) { //检查是否需要生成第一个 return; } bool isShow = false; bool isHind = false; GetIsShow(minWidget, ref isShow, ref isHind); bool isShow3 = false; bool isHind3 = false; GetIsShow(maxWdiget, ref isShow3, ref isHind3); if (isShow) //最小的在显示区域,需要生成下面的 { IScorllListWidget wdiget = await Cread(minWidget.index - 1, true); if (wdiget != null) { bool isShow2 = true; bool isHind2 = false; while (isShow2 && wdiget != null) { if (_isBreak) { return; } GetIsShow(wdiget, ref isShow2, ref isHind2); if (isShow2) { if (_isStartDelay) { await TimerComponent.Instance.WaitAsync(delayTime); } if (_isBreak) { return; } wdiget = await Cread(wdiget.index - 1, true); } } } } if (isShow3) //最小的在显示区域,需要生成下面的 { IScorllListWidget wdiget = await Cread(maxWdiget.index + 1, false); if (wdiget != null) { bool isShow2 = true; bool isHind2 = false; while (isShow2 && wdiget != null) { if (_isBreak) { return; } GetIsShow(wdiget, ref isShow2, ref isHind2); if (isShow2) { if (_isStartDelay) { await TimerComponent.Instance.WaitAsync(delayTime); } if (_isBreak) { return; } wdiget = await Cread(wdiget.index + 1, false); } } } } // ScrollRect.velocity = _vel; } // myRectTransform.sizeDelta=new Vector2(maxWdiget.index*) // Debug.Log(pos); } public void GetIsShow(IScorllListWidget widget, ref bool isShow, ref bool isHind) { RectTransform root = ScrollRect.GetComponent(); Vector2 posint = root.worldToLocalMatrix.MultiplyPoint3x4(widget.Transform.position); Vector2 size = widget.GetSize(); isShow = false; isHind = false; Vector2 pos = Vector2.zero; float minx = pos.x - (root.rect.size.x * root.pivot.x); float miny = pos.y - (root.rect.size.y * root.pivot.y); float maxx = pos.x + (root.rect.size.x * (1 - root.pivot.x)); float maxy = pos.y + (root.rect.size.y * (1 - root.pivot.y)); // UIManager.Instance.Canvas // Debug.Log(pos); Vector2 rootPos = posint; List widgetBox = GetBox(rootPos.x, rootPos.y - size.y, rootPos.x + size.x, rootPos.y); for (int i = 0; i < widgetBox.Count; i++) { Vector2 p = widgetBox[i]; if (ScrollRect.vertical) { if (p.y > miny && p.y < maxy) { isShow = true; } if (p.y > miny - size.y - 100 && p.y < maxy + size.y + 100) { isHind = true; } } else { if (p.x > minx && p.x < maxx) { isShow = true; } if (p.x > minx - size.x - 100 && p.x < maxx + size.x + 100) { isHind = true; } } // if (p.x > minx && p.x < maxx && p.y > miny && p.y < maxy) // { // isShow = true; // } // if (p.x > minx - size.x - 100 && p.x < maxx + size.x + 100 && p.y > miny - size.y - 100 && // p.y < maxy + size.y + 100) // { // isHind = true; // } } } protected List GetBox(float minx, float miny, float maxx, float maxy) { List s = new List(); s.Add(new Vector2(minx, miny)); s.Add(new Vector2(minx, maxy)); s.Add(new Vector2(maxx, maxy)); s.Add(new Vector2(maxx, miny)); return s; } public async CTask GetWidget(int index) { IScorllListWidget listWidget = await m_scrollListContent.GetIScorllListWidget(index, transform.GetComponent()); if (listWidget == null) { return null; } listWidget.Transform.anchorMin = new Vector2(0, 1); listWidget.Transform.anchorMax = new Vector2(0, 1); listWidget.Transform.pivot = new Vector2(0, 1); listWidget.Transform.localScale = Vector3.one; listWidget.index = index; return listWidget; } public async CTask GetIScorllListWidget(int index, RectTransform root) { if (hindWidget.Count > 0) { IScorllListWidget listWidget = hindWidget[0]; listWidget.Transform.gameObject.SetActive(true); hindWidget.RemoveAt(0); Debug.Log(index + "___" + listWidget.index); listWidget.index = index; return listWidget; } return null; } public void HindIWidget(IScorllListWidget widget) { showWidget.Remove(widget); m_scrollListContent.HindIScorllListWidget(widget); } public void HindIScorllListWidget(IScorllListWidget widget) { widget.Transform.gameObject.SetActive(false); Debug.Log("hindWidget___" + widget.index); hindWidget.Add(widget); } /// /// 计算目标索引对应的位置 /// /// 目标索引 /// 目标位置 private Vector2 CalculateTargetPosition(int targetIndex) { Vector2 targetPos = onePos; // 从初始位置开始计算 if (ScrollRect.horizontal) { // 水平滚动:计算 X 轴位置 int xOffset = (targetIndex - m_startIndex) / Page.x; targetPos.x -= xOffset * sizeData.x + posOff.x; } else if (ScrollRect.vertical) { // 垂直滚动:计算 Y 轴位置 int yOffset = (targetIndex - m_startIndex) / Page.y; if (!isCustomizeHeight) { targetPos.y = yOffset * sizeData.y + posOff.y; } else { // 如果是自定义高度,假设高度固定,实际需根据 GetSize() 动态调整 targetPos.y -= yOffset * sizeData.y + posOff.y; } } return targetPos; } /// /// 计算目标索引对应的 content sizeDelta /// /// 目标索引 /// 目标 sizeDelta private Vector2 CalculateTargetSizeDelta(int targetIndex) { Vector2 targetSizeDelta = myRectTransform.sizeDelta; if (ScrollRect.horizontal) { // 水平滚动:根据目标索引计算总宽度 int totalColumns = (targetIndex + Page.y - 1) / Page.y; // 向上取整 targetSizeDelta.x = totalColumns * sizeData.x + overflowDistance; targetSizeDelta.y = Page.y * sizeData.y; // 高度基于每列的控件数 } else if (ScrollRect.vertical) { // 垂直滚动:根据目标索引计算总高度 int totalRows = (targetIndex + Page.y - 1) / Page.y; // 向上取整 if (!isCustomizeHeight) { targetSizeDelta.y = totalRows * sizeData.y + overflowDistance; targetSizeDelta.x = Page.y * sizeData.x; // 宽度基于每行的控件数 } else { // 如果是自定义高度,简单假设高度固定,实际需根据已有控件动态计算 targetSizeDelta.y = totalRows * sizeData.y + overflowDistance; targetSizeDelta.x = Page.y * sizeData.x; } } return targetSizeDelta; } private Vector2 CalculateBottomSizeDelta(int targetIndex) { Vector2 targetSizeDelta = myRectTransform.sizeDelta; RectTransform root = ScrollRect.GetComponent(); float viewportHeight; if (ScrollRectSizedata.y == 0) { viewportHeight = root.rect.height; } else { viewportHeight = ScrollRectSizedata.y; } // float viewportHeight = root.rect.height; if (ScrollRect.vertical) { // 计算可视行数 float itemHeight = isCustomizeHeight ? sizeData.y : sizeData.y; // 简化,实际需动态获取 int visibleRows = Mathf.CeilToInt(viewportHeight / itemHeight); // 向上取整 // 计算最低端索引 int bottomIndex = Mathf.Min(targetIndex + visibleRows * Page.y, _maxSize - 1); int totalRows = (bottomIndex + Page.y - 1) / Page.y; // 从 0 到 bottomIndex 的行数 if (!isCustomizeHeight) { targetSizeDelta.y = totalRows * sizeData.y + overflowDistance; } else { // 动态高度简化处理 targetSizeDelta.y = totalRows * sizeData.y + overflowDistance; // 实际应为: // float totalHeight = 0f; // for (int i = 0; i <= bottomIndex; i += Page.y) // totalHeight += (i < showWidget.Count) ? showWidget[i].GetSize().y : sizeData.y; // targetSizeDelta.y = totalHeight + overflowDistance; } targetSizeDelta.x = Page.y * sizeData.x; // Debug.Log( // $"最低端 sizeDelta - visibleRows: {visibleRows}, bottomIndex: {bottomIndex}, totalRows: {totalRows}, sizeDelta.y: {targetSizeDelta.y}"); } else if (ScrollRect.horizontal) { int visibleColumns = Mathf.CeilToInt(root.rect.width / sizeData.x); int rightIndex = Mathf.Min(targetIndex + visibleColumns * Page.x, _maxSize - 1); int totalColumns = (rightIndex + Page.y - 1) / Page.y; targetSizeDelta.x = totalColumns * sizeData.x + overflowDistance; targetSizeDelta.y = Page.y * sizeData.y; } return targetSizeDelta; } }