using System; using System.Collections.Generic; using UnityEngine; namespace BF { public class BFScrollRectMultCell : BFScrollRectBase { public BFCell[] bfCells; public float spacing; public float padding; public float topRecoveryOffset; public float downRecoveryOffset; public int preLineCount = 4; public BFUIDirection direction; List activeCells = new List(); Dictionary> cellPool = new Dictionary>(); float lineSize; float cellSize; int instantiateCount = 0; int totalCount = 0; int topFillCount; int bottomFillCount; Func GetCellIndex; void Awake() { lineSize = direction == BFUIDirection.Vertical ? bfCells[0].Height + spacing : bfCells[0].Width + spacing; cellSize = direction == BFUIDirection.Vertical ? bfCells[0].Width + padding : bfCells[0].Height + padding; int i = 1; foreach (var cell in bfCells) { if (cell.gameObject.activeSelf) { cell.gameObject.SetActive(false); } cellPool.Add(i, new Stack()); i++; } } int GetTotalLines(int totalCount) { if (totalCount % preLineCount == 0) { return totalCount / preLineCount; } return totalCount / preLineCount + 1; } public void SetGetCellIndexFunc(Func getCellIndex) { GetCellIndex = getCellIndex; } public override void RefillCells(int totalCount, int targetIndex) { this.totalCount = totalCount; if (activeCells.Count != 0) { while (activeCells.Count != 0) { Pool(activeCells[0]); } UnityScrollRect.StopMovement(); } ContentTrans.anchoredPosition = Vector2.zero; if (direction == BFUIDirection.Vertical) { ContentTrans.sizeDelta = new Vector2(ContentTrans.sizeDelta.x, lineSize * GetTotalLines(totalCount) - spacing); } else { ContentTrans.sizeDelta = new Vector2(lineSize * GetTotalLines(totalCount) - spacing, ContentTrans.sizeDelta.y); } TryFullFill(); } public override void SetTotalCount(int totalCount) { this.totalCount = totalCount; if (totalCount < activeCells.Count) { for (int i = activeCells.Count - 1; i > totalCount - 1; i--) { Pool(activeCells[i]); if(bottomFillCount > 0) { bottomFillCount--; } else { if(totalCount <= preLineCount) { topFillCount--; } else { bottomFillCount = preLineCount - 1; } } } } if (direction == BFUIDirection.Vertical) { ContentTrans.sizeDelta = new Vector2(ContentTrans.sizeDelta.x, lineSize * GetTotalLines(totalCount) - spacing); } else { ContentTrans.sizeDelta = new Vector2(lineSize * totalCount - spacing, ContentTrans.sizeDelta.y); } } public override void SetSelected(int index) { selectedIndex = index; int count = activeCells.Count; for (int i = 0; i < count; i++) { var cell = activeCells[i]; Selected?.Invoke(cell.dataIndex == index, cell.objIndex); } } public override void RefreshAll() { int count = activeCells.Count; for (int i = 0; i < count; i++) { var cell = activeCells[i]; RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); } } public override void ClearCells() { while (activeCells.Count != 0) { Pool(activeCells[0]); } ContentTrans.anchoredPosition = Vector2.zero; } public override void AnchoredPositionChange() { } protected override void TryFullFill() { int i = 0; while (CheckBorder() && i < totalCount) { i++; } } bool CheckBorder() { if (activeCells.Count == 0) { TryStartAdd(); return false; } var cell = activeCells[0]; var delta = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x + ContentTrans.anchoredPosition.x; if (delta < 0 + topRecoveryOffset && TryAddUp()) { return true; } if (topFillCount < preLineCount && TryAddUp()) { return true; } cell = activeCells[activeCells.Count - 1]; delta = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y - lineSize + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x - lineSize + ContentTrans.anchoredPosition.x; if (delta > -(ViewPortTrans.sizeDelta.y + downRecoveryOffset) && TryAddDown()) { return true; } if (bottomFillCount < preLineCount && TryAddDown()) { return true; } if (activeCells.Count <= preLineCount) { return false; } if (NeedCull(activeCells[0])) { Pool(activeCells[0]); if(topFillCount == 0) { topFillCount = preLineCount - 1; } else { topFillCount--; } return true; } if (NeedCull(activeCells[activeCells.Count - 1])) { Pool(activeCells[activeCells.Count - 1]); if(bottomFillCount == 0) { bottomFillCount = preLineCount - 1; } else { bottomFillCount--; } return true; } return false; } void TryStartAdd() { if (totalCount == 0) { return; } var cell = GetCell(1); topFillCount = 1; cell.CachedRectTransform.anchoredPosition = Vector2.zero; activeCells.Add(cell); cell.gameObject.SetActive(true); RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); } bool TryAddUp() { var topIndex = activeCells[0].dataIndex; if (topIndex == 1 && (topFillCount == preLineCount || activeCells.Count == totalCount)) { return false; } BFCell cell; if (topFillCount == preLineCount) { topFillCount = 0; cell = GetCell(topIndex - preLineCount); } else { cell = GetCell(topIndex + topFillCount); } var x = direction == BFUIDirection.Vertical ? topFillCount * cellSize : -GetLineIndex(cell.dataIndex) * lineSize; var y = direction == BFUIDirection.Vertical ? -GetLineIndex(cell.dataIndex) * lineSize : topFillCount * cellSize; cell.CachedRectTransform.anchoredPosition = new Vector2(x, y); cell.gameObject.SetActive(true); topFillCount++; if (topFillCount == activeCells.Count) { activeCells.Add(cell); } else { activeCells.Insert(topFillCount - 1, cell); } RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); return true; } bool TryAddDown() { var downIndex = activeCells[activeCells.Count - 1].dataIndex; if (downIndex == totalCount) { return false; } if (bottomFillCount == preLineCount) { bottomFillCount = 0; } var cell = GetCell(downIndex + 1); var x = direction == BFUIDirection.Vertical ? bottomFillCount * cellSize : -GetLineIndex(cell.dataIndex) * lineSize; var y = direction == BFUIDirection.Vertical ? -GetLineIndex(cell.dataIndex) * lineSize : bottomFillCount * cellSize; cell.CachedRectTransform.anchoredPosition = new Vector2(x, y); activeCells.Add(cell); cell.gameObject.SetActive(true); bottomFillCount++; RefreshAction?.Invoke(cell.dataIndex, cell.objIndex); return true; } int GetLineIndex(int index) { if (index % preLineCount == 0) { return index / preLineCount - 1; } return index / preLineCount; } BFCell GetCell(int dataIndex) { var index = GetCellIndex(dataIndex); if (cellPool[index].Count > 0) { return cellPool[index].Pop(); } else { var t = Instantiate(bfCells[index].CachedRectTransform); t.SetParent(ContentTrans); t.localScale = Vector3.one; t.anchoredPosition3D = Vector3.zero; instantiateCount++; var newCell = t.GetComponent(); newCell.objIndex = instantiateCount; OnInstantiateCell?.Invoke(newCell.gameObject); newCell.dataIndex = dataIndex; return newCell; } } void Pool(BFCell cell) { cell.gameObject.SetActive(false); activeCells.Remove(cell); var index = GetCellIndex(cell.dataIndex); cellPool[index].Push(cell); } bool NeedCull(BFCell cell) { if (activeCells.IndexOf(cell) == 0) { var d1 = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y - lineSize + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x - lineSize + ContentTrans.anchoredPosition.x; if (d1 > 0.01 + topRecoveryOffset) { return true; } } var d2 = direction == BFUIDirection.Vertical ? cell.AnchoredPosition.y + ContentTrans.anchoredPosition.y : cell.AnchoredPosition.x + ContentTrans.anchoredPosition.x; var d3 = direction == BFUIDirection.Vertical ? -ViewPortTrans.sizeDelta.y : -ViewPortTrans.sizeDelta.x; if (d2 < d3 - (0.01 + downRecoveryOffset)) { return true; } if (cell.dataIndex > totalCount) { return true; } return false; } } }