2025-11-03 10:59:33 +08:00

691 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace BF
{
public class BFScrollRectCommon : BFScrollRectBase
{
public BFCell bfCell;
// public float spacing;
public int topRecoveryOffset;
public int downRecoveryOffset;
public BFUIDirection direction;
public bool reverse = false;
public bool centerWhenNotFull = false; // 新增参数,控制是否在不满一行时居中
Stack<BFCell> cellPool = new Stack<BFCell>();
List<BFCell> activeCells = new List<BFCell>();
float cellSize;
int instantiateCount = 0;
int totalCount = 0;
int tmpTotalCount = 0;
public float cellWidth = 0.0f;
public float CellWidth
{
get { return cellWidth;}
set {
cellWidth = value;
UpdateAllCellPosition();
}
}
public float cellHeight = 0.0f;
public float CellHeight
{
get { return cellHeight;}
set {
cellHeight = value;
UpdateAllCellPosition();
}
}
private int minIdx = 0;
private int maxIdx = 0;
private int tmpMinIdx = 0;
private int tmpMaxIdx = 0;
private int maxLine = 0;
private Vector2 curAnchoredPosition = new Vector2(0.0f, 0.0f);
public int perLineNum = 1;
void Awake()
{
if (bfCell.gameObject.activeSelf)
{
bfCell.gameObject.SetActive(false);
}
if (direction == BFUIDirection.Vertical)
{
if (reverse)
{
ContentTrans.anchorMax = new Vector2(0.0f, 0.0f);
ContentTrans.anchorMin = new Vector2(0.0f, 0.0f);
ContentTrans.pivot = new Vector2(0.0f, 0.0f);
ContentTrans.anchoredPosition = Vector2.zero;
}
else
{
ContentTrans.anchorMax = new Vector2(0.0f, 1.0f);
ContentTrans.anchorMin = new Vector2(0.0f, 1.0f);
ContentTrans.pivot = new Vector2(0.0f, 1.0f);
ContentTrans.anchoredPosition = Vector2.zero;
}
}
else
{
if (reverse)
{
ContentTrans.anchorMax = new Vector2(1.0f, 1.0f);
ContentTrans.anchorMin = new Vector2(1.0f, 1.0f);
ContentTrans.pivot = new Vector2(1.0f, 1.0f);
ContentTrans.anchoredPosition = new Vector2(1.0f, 1.0f);
}
else
{
ContentTrans.anchorMax = new Vector2(0.0f, 0.0f);
ContentTrans.anchorMin = new Vector2(0.0f, 0.0f);
ContentTrans.pivot = new Vector2(0.0f, 0.0f);
ContentTrans.anchoredPosition = Vector2.zero;
}
}
}
public override void RefillCells(int totalCount, int targetIndex)
{
SetTotalCount(totalCount);
if (activeCells.Count != 0)
{
while (activeCells.Count != 0)
{
Pool(activeCells[activeCells.Count - 1]);
}
UnityScrollRect.StopMovement();
}
minIdx = 0;
maxIdx = 0;
if (targetIndex > 0)
{
MoveToIndex(targetIndex);
}
TryFullFill();
AnchoredPositionChange();
}
public override void SetTotalCount(int totalCount)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "totalCount = " + totalCount.ToString());
this.totalCount = totalCount;
if (totalCount < activeCells.Count)
{
for (int i = activeCells.Count - 1; i > totalCount - 1; i--)
{
Pool(activeCells[i]);
}
}
maxLine = Mathf.CeilToInt(totalCount*1.0f/perLineNum);
if (direction == BFUIDirection.Vertical)
{
float maxHeight = Mathf.Max(UnityScrollRect.viewport.rect.height, maxLine*cellHeight + topRecoveryOffset + downRecoveryOffset);
ContentTrans.sizeDelta = new Vector2(ContentTrans.sizeDelta.x, maxHeight);
// cellWidth = (int)UnityScrollRect.viewport.rect.width;
if (reverse)
{
float posY = UnityScrollRect.content.anchoredPosition.y;
float minPosY = Mathf.Floor(UnityScrollRect.viewport.rect.height) - maxHeight;
if (posY < minPosY)
{
UnityScrollRect.content.anchoredPosition = new Vector2(UnityScrollRect.content.anchoredPosition.x, minPosY);
}
}
else
{
float posY = UnityScrollRect.content.anchoredPosition.y;
float maxPosY = maxHeight - Mathf.Floor(UnityScrollRect.viewport.rect.height);
if (posY > maxPosY)
{
UnityScrollRect.content.anchoredPosition = new Vector2(UnityScrollRect.content.anchoredPosition.x, maxPosY);
}
}
}
else
{
float maxWidth = Mathf.Max(UnityScrollRect.viewport.rect.width, maxLine*cellWidth + topRecoveryOffset + downRecoveryOffset);
ContentTrans.sizeDelta = new Vector2(maxWidth, ContentTrans.sizeDelta.y);
// cellHeight = (int)UnityScrollRect.viewport.rect.height;
if (reverse)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "UnityScrollRect.content.anchoredPosition.x = " + UnityScrollRect.content.anchoredPosition.x);
float posX = UnityScrollRect.content.anchoredPosition.x;
float maxPosX = maxWidth - Mathf.Floor(UnityScrollRect.viewport.rect.width);
if (posX > maxPosX)
{
UnityScrollRect.content.anchoredPosition = new Vector2(maxPosX, UnityScrollRect.content.anchoredPosition.y);
}
}
else
{
float posX = UnityScrollRect.content.anchoredPosition.x;
float minPosX = Mathf.Floor(UnityScrollRect.viewport.rect.width) - maxWidth;
if (posX < minPosX)
{
UnityScrollRect.content.anchoredPosition = new Vector2(minPosX, UnityScrollRect.content.anchoredPosition.y);
}
}
}
// minIdx = Mathf.Max(1, minIdx);
// maxIdx = Mathf.Min(this.totalCount, maxIdx);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", " ContentTrans.sizeDelta.x = " + ContentTrans.sizeDelta.x);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", " ContentTrans.sizeDelta.y = " + ContentTrans.sizeDelta.y);
UpdateAllCellPosition();
}
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 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 ClearCells()
{
while (activeCells.Count != 0)
{
Pool(activeCells[activeCells.Count - 1]);
}
if (direction == BFUIDirection.Vertical)
{
ContentTrans.anchoredPosition = Vector2.zero;
}
else
{
if (reverse)
{
ContentTrans.anchoredPosition = new Vector2(1.0f, 1.0f);
}
else
{
ContentTrans.anchoredPosition = Vector2.zero;
}
}
minIdx = 0;
maxIdx = 0;
}
public override void AnchoredPositionChange()
{
if (Mathf.Abs(curAnchoredPosition.x - UnityScrollRect.content.anchoredPosition.x) >= 1 || Mathf.Abs(curAnchoredPosition.y - UnityScrollRect.content.anchoredPosition.y) >= 1)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "diffX = " + (curAnchoredPosition.x - UnityScrollRect.content.anchoredPosition.x));
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "diffY = " + (curAnchoredPosition.y - UnityScrollRect.content.anchoredPosition.y));
curAnchoredPosition.x = UnityScrollRect.content.anchoredPosition.x;
curAnchoredPosition.y = UnityScrollRect.content.anchoredPosition.y;
OnAnchoredPositionAction?.Invoke(UnityScrollRect.content.anchoredPosition.x, UnityScrollRect.content.anchoredPosition.y);
}
}
protected override void TryFullFill()
{
CheckBorder();
}
void calculateIndex()
{
if (direction == BFUIDirection.Vertical)
{
float posY = UnityScrollRect.content.anchoredPosition.y;
if (reverse)
{
// float posX = totalMaxWidth - (Mathf.Floor(UnityScrollRect.viewport.rect.width) - UnityScrollRect.content.anchoredPosition.x);
tmpMinIdx = Mathf.CeilToInt(-(posY + topRecoveryOffset)/cellHeight);
tmpMaxIdx = Mathf.CeilToInt((Mathf.Floor(UnityScrollRect.viewport.rect.height) - (posY + topRecoveryOffset))/cellHeight);
}
else
{
tmpMinIdx = Mathf.CeilToInt((posY - topRecoveryOffset)/cellHeight);
tmpMaxIdx = Mathf.CeilToInt((posY - topRecoveryOffset + Mathf.Floor(UnityScrollRect.viewport.rect.height))/cellHeight);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "posY = " + posY);
}
}
else
{
float posX = UnityScrollRect.content.anchoredPosition.x;
if (reverse)
{
// float posX = totalMaxWidth - (Mathf.Floor(UnityScrollRect.viewport.rect.width) - UnityScrollRect.content.anchoredPosition.x);
tmpMinIdx = Mathf.CeilToInt((posX - topRecoveryOffset)/cellWidth);
tmpMaxIdx = Mathf.CeilToInt((Mathf.Floor(UnityScrollRect.viewport.rect.width) + (posX - topRecoveryOffset))/cellWidth);
}
else
{
tmpMinIdx = Mathf.CeilToInt(-(posX + topRecoveryOffset)/cellWidth);
tmpMaxIdx = Mathf.CeilToInt((Mathf.Floor(UnityScrollRect.viewport.rect.width) - (posX + topRecoveryOffset))/cellWidth);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "posX = " + posX);
}
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "posX = " + posX);
}
tmpMinIdx = Mathf.Max(1, tmpMinIdx);
tmpMaxIdx = Mathf.Min(maxLine, tmpMaxIdx);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "tmpMinIdx = " + tmpMinIdx + " tmpMaxIdx = " + tmpMaxIdx);
}
bool CheckBorder()
{
calculateIndex();
if (minIdx == 0)
{
minIdx = tmpMinIdx;
maxIdx = tmpMaxIdx;
tmpTotalCount = totalCount;
int minIdxMult = (minIdx - 1)*perLineNum + 1;
int maxIdxMult = maxIdx*perLineNum;
maxIdxMult = Mathf.Min(maxIdxMult, totalCount);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "===== 1 minIdxMult = " + minIdxMult + " maxIdxMult = " + maxIdxMult);
for (int i = minIdxMult; i <= maxIdxMult; i++)
{
TryStartAdd(i, false);
}
AnchoredPositionChange();
return false;
}
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "===== 1 tmpMinIdx = " + tmpMinIdx + " tmpMaxIdx = " + tmpMaxIdx);
if (tmpMinIdx == minIdx && tmpMaxIdx == maxIdx && tmpTotalCount == totalCount)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "===== 3 minIdx = " + minIdx + " maxIdx = " + maxIdx);
AnchoredPositionChange();
return false;
}
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "===== 2 tmpMinIdx = " + tmpMinIdx + " tmpMaxIdx = " + tmpMaxIdx);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "minIdx = " + minIdx + " maxIdx = " + maxIdx);
int minIdxMult2 = (tmpMinIdx - 1)*perLineNum + 1;
int maxIdxMult2 = tmpMaxIdx*perLineNum;
maxIdxMult2 = Mathf.Min(maxIdxMult2, totalCount);
for (int ii = activeCells.Count - 1; ii >= 0; ii--)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "================================1 minIdx = " + i);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", activeCells[ii].dataIndex.ToString());
if (activeCells[ii] && (activeCells[ii].dataIndex < minIdxMult2 || activeCells[ii].dataIndex > maxIdxMult2))
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "activeCells[ii].dataIndex = " + activeCells[ii].dataIndex);
Pool(activeCells[ii]);
}
}
int minIdxMult1 = (tmpMinIdx - 1)*perLineNum + 1;
int maxIdxMult1 = tmpMaxIdx*perLineNum;
maxIdxMult1 = Mathf.Min(maxIdxMult1, totalCount);
for (int i = minIdxMult1; i <= maxIdxMult1; i++)
{
bool notFind = true;
for (int ii = 0; ii < activeCells.Count; ii++)
{
if (activeCells[ii] && activeCells[ii].dataIndex == i)
{
notFind = false;
break;
}
}
if(notFind)
{
if (minIdx > tmpMinIdx)
{
TryStartAdd(i, true);
}
else
{
TryStartAdd(i, false);
}
}
}
// if (minIdx > tmpMinIdx)
// {
// float minIdxMult1 = (tmpMinIdx - 1)*perLineNum + 1;
// float minIdxMult2 = (minIdx - 1)*perLineNum;
// for (float i = minIdxMult1; i <= minIdxMult2; i++)
// {
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "minIdx i = " + i);
// TryStartAdd(i, true);
// }
// }
// if (tmpMaxIdx > maxIdx || tmpTotalCount != totalCount)
// {
// float maxIdxMult1 = maxIdx*perLineNum + 1;
// float maxIdxMult2 = tmpMaxIdx*perLineNum;
// maxIdxMult2 = Mathf.Min(maxIdxMult2, totalCount);
// // BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "maxIdxMult2 = " + maxIdxMult2);
// for (float i = maxIdxMult1; i <= maxIdxMult2; i++)
// {
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "maxIdx i = " + i);
// TryStartAdd(i, false);
// }
// }
minIdx = tmpMinIdx;
maxIdx = tmpMaxIdx;
tmpTotalCount = totalCount;
AnchoredPositionChange();
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "minIdx = " + minIdx + " maxIdx = " + maxIdx + " maxCellNum = " + (int)Mathf.Ceil(UnityScrollRect.viewport.rect.width/cellWidth));
return false;
}
private float GetPositionXByIndex(int index)
{
float posX = 0.0f;
if (direction == BFUIDirection.Vertical)
{
if (centerWhenNotFull && totalCount < perLineNum)
{
float totalWidth = totalCount * cellWidth;
float startPos = (UnityScrollRect.viewport.rect.width - totalWidth) / 2;
float idx = (index - 1.0f) % perLineNum + 1.0f;
posX = startPos + (idx - 0.5f) * cellWidth;
}
else
{
if (reverse)
{
float cellWidth1 = UnityScrollRect.viewport.rect.width / perLineNum;
float idx = (index - 1.0f) % perLineNum + 1.0f;
posX = (idx - 0.5f) * cellWidth1;
}
else
{
float cellWidth1 = UnityScrollRect.viewport.rect.width / perLineNum;
float idx = (index - 1.0f) % perLineNum + 1.0f;
posX = (idx - 0.5f) * cellWidth1;
}
}
}
else
{
if (reverse)
{
float idxX = Mathf.Ceil(index*1.0f/perLineNum);
posX = -(idxX - 0.5f)*cellWidth - topRecoveryOffset;
}
else
{
float idxX = Mathf.Ceil(index*1.0f/perLineNum);
posX = (idxX - 0.5f)*cellWidth + topRecoveryOffset;
}
}
return posX;
}
private float GetPositionYByIndex(int index)
{
float posY = 0.0f;
if (direction == BFUIDirection.Vertical)
{
if (reverse)
{
float idxY = Mathf.Ceil(index*1.0f/perLineNum);
posY = (idxY - 0.5f)*cellHeight + topRecoveryOffset;
}
else
{
float idxY = Mathf.Ceil(index*1.0f/perLineNum);
posY = -(idxY - 0.5f)*cellHeight - topRecoveryOffset;
}
}
else
{
if (centerWhenNotFull && totalCount < perLineNum)
{
// 在ContentTrans高度范围内居中
float totalHeight = totalCount * cellHeight;
float contentOffset = (ContentTrans.rect.height - totalHeight) / 2;
// 元素在ContentTrans内垂直居中
float idx = (index - 1.0f) % perLineNum + 1.0f;
if (reverse)
{
posY = -(contentOffset + (idx - 0.5f) * cellHeight);
}
else
{
posY = contentOffset + (idx - 0.5f) * cellHeight;
}
// 如果ContentTrans比viewport矮调整全局居中
if (ContentTrans.rect.height < UnityScrollRect.viewport.rect.height)
{
float viewportOffset = (UnityScrollRect.viewport.rect.height - ContentTrans.rect.height) / 2;
if (reverse)
{
posY -= viewportOffset; // 抵消视口偏移
}
else
{
posY += viewportOffset; // 抵消视口偏移
}
}
}
else
{
if (reverse)
{
float cellHeight1 = UnityScrollRect.viewport.rect.height / perLineNum;
float offset = UnityScrollRect.viewport.rect.height / perLineNum / 2.0f;
float idx = (index - 1.0f) % perLineNum + 1.0f;
posY = -(offset + (idx - 1.0f) * cellHeight1);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "index = " + index + " posY = " + posY + " offset = " + offset);
}
else
{
float cellHeight1 = UnityScrollRect.viewport.rect.height / perLineNum;
float offset = UnityScrollRect.viewport.rect.height - UnityScrollRect.viewport.rect.height / perLineNum / 2.0f;
float idx = (index - 1.0f) % perLineNum + 1.0f;
posY = offset - (idx - 1.0f) * cellHeight1;
}
}
}
return posY;
}
void TryStartAdd(int index, bool addMin)
{
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "index = " + index);
if (totalCount == 0)
{
return;
}
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "cellWidth = " + cellWidth + " index - 0.5 = " + (index - 0.5));
float posX = GetPositionXByIndex(index);
float posY = GetPositionYByIndex(index);
// BFLog.LogDebug(BFLog.DEBUG_GAME_LAUNCH, "red", "posX = " + posX + " posY = " + posY + " index = " + index);
var cell = GetCell();
cell.name = StringConst.GetScrollRectCellName(index - 1);
cell.dataIndex = index;
if (reverse)
{
if (direction == BFUIDirection.Vertical)
{
cell.CachedRectTransform.anchorMin = new Vector2(0.0f, 0.0f);
cell.CachedRectTransform.anchorMax = new Vector2(0.0f, 0.0f);
}
else
{
cell.CachedRectTransform.anchorMin = new Vector2(1.0f, 1.0f);
cell.CachedRectTransform.anchorMax = new Vector2(1.0f, 1.0f);
}
}
else
{
if (direction == BFUIDirection.Vertical)
{
cell.CachedRectTransform.anchorMin = new Vector2(0.0f, 1.0f);
cell.CachedRectTransform.anchorMax = new Vector2(0.0f, 1.0f);
}
else
{
cell.CachedRectTransform.anchorMin = new Vector2(0.0f, 0.0f);
cell.CachedRectTransform.anchorMax = new Vector2(0.0f, 0.0f);
}
}
cell.CachedRectTransform.pivot = new Vector2(0.5f, 0.5f);
cell.CachedRectTransform.anchoredPosition = new Vector2(posX, posY);
if (addMin)
{
activeCells.Insert(0, cell);
}
else
{
activeCells.Add(cell);
}
cell.gameObject.SetActive(true);
RefreshAction?.Invoke(cell.dataIndex, cell.objIndex);
}
BFCell GetCell()
{
if (cellPool.Count > 0)
{
return cellPool.Pop();
}
else
{
var t = Instantiate(bfCell.CachedRectTransform);
t.SetParent(ContentTrans);
t.localScale = bfCell.CachedRectTransform.localScale;
t.anchoredPosition3D = Vector3.zero;
instantiateCount++;
var newCell = t.GetComponent<BFCell>();
newCell.objIndex = instantiateCount;
OnInstantiateCell?.Invoke(newCell.gameObject);
return newCell;
}
}
void Pool(BFCell cell)
{
activeCells.Remove(cell);
cell.gameObject.SetActive(false);
cellPool.Push(cell);
}
public override void MoveToIndex(int index)
{
if (index > totalCount)
{
return;
}
if (direction == BFUIDirection.Vertical)
{
float moveDistance = cellHeight * Mathf.Floor((index - 1)*1.0f/perLineNum);
float moveHeight = Mathf.Min(moveDistance, ContentTrans.rect.size.y - ViewPortTrans.rect.size.y);
if(this.reverse)
{
ContentTrans.anchoredPosition = new Vector2(ContentTrans.anchoredPosition.x, -moveHeight);
}
else
{
ContentTrans.anchoredPosition = new Vector2(ContentTrans.anchoredPosition.x, moveHeight);
}
}
else
{
float moveDistance = cellWidth * Mathf.Floor((index - 1)*1.0f/perLineNum);
float moveWidth = Mathf.Min(moveDistance, ContentTrans.rect.size.x - ViewPortTrans.rect.size.x);
if(this.reverse)
{
ContentTrans.anchoredPosition = new Vector2(moveWidth, ContentTrans.anchoredPosition.y);
}
else
{
ContentTrans.anchoredPosition = new Vector2(-moveWidth, ContentTrans.anchoredPosition.y);
}
}
}
public override void RemoveCell(int index)
{
if (totalCount <= 0)
{
return;
}
var activeCount = activeCells.Count;
if (activeCells[activeCount - 1].dataIndex == totalCount && activeCells[0].dataIndex > index)
{
foreach (var cell in activeCells)
{
cell.dataIndex -= 1;
}
}
SetTotalCount(totalCount - 1);
TryFullFill();
RefreshAll();
}
public void StopMovement()
{
UnityScrollRect.StopMovement();
}
[ContextMenu("UpdateAllCellPosition")]
public void UpdateAllCellPosition()
{
for (int i = 0; i < activeCells.Count; i++)
{
float posX = GetPositionXByIndex(activeCells[i].dataIndex);
float posY = GetPositionYByIndex(activeCells[i].dataIndex);
activeCells[i].CachedRectTransform.anchoredPosition = new Vector2(posX, posY);
}
}
public void SetCellWidth(float width)
{
cellWidth = width;
UpdateAllCellPosition();
}
public void SetCellHeight(float height)
{
cellHeight = height;
UpdateAllCellPosition();
}
public void SetPerLineNum(int num)
{
perLineNum = num;
UpdateAllCellPosition();
}
public void SetTopRecoveryOffset(int num)
{
topRecoveryOffset = num;
UpdateAllCellPosition();
}
public void SetDownRecoveryOffset(int num)
{
downRecoveryOffset = num;
UpdateAllCellPosition();
}
public int GetTopRecoveryOffset()
{
return topRecoveryOffset;
}
public int GetDownRecoveryOffset()
{
return downRecoveryOffset;
}
}
}