烦烦烦
新建一个scrollview 如上图,有一个简单预制体Item拿来测试用
新建一个脚本TableView.cs挂载在ScrollView节点上
TableView.cs内容
/// <summary>
/// 子对象 一般是预制体
/// </summary>
public GameObject cellItem;
/// <summary>
/// 面板总尺寸
/// </summary>
private Vector2 totalViewSize;
/// <summary>
/// 可见区域尺寸
/// </summary>
private Vector2 visibleViewSize;
/// <summary>
/// 子项尺寸
/// </summary>
private Vector2 cellSize;
/// <summary>
/// 子项间隔
/// </summary>
private float cellInterval;
/// <summary>
/// 可滑动的距离
/// </summary>
private float totalScrollDistance;
/// <summary>
/// 子项数量
/// </summary>
private int totalCellCount;
/// <summary>
/// 开始结束下标
/// </summary>
private int startIndex;
private int endIndex;
/// <summary>
/// 内容有已滑动距离
/// </summary>
private Vector2 contentOffset;
/// <summary>
/// 可见子项集合
/// </summary>
private Dictionary<int, GameObject> cells;
/// <summary>
/// 可重用子项集合
/// </summary>
private List<GameObject> reUseCells;
private ViewDirection currentDir;
/// <summary>
/// ScrollRect 组件
/// </summary>
private UnityEngine.UI.ScrollRect scrollRect;
/// <summary>
/// 遮罩的rectTransform
/// </summary>
private RectTransform viewportRectTransform;
/// <summary>
/// 内容的rectTransform
/// </summary>
private RectTransform contentRectTransform;
public delegate void OnItemRender(int index, Transform trans);
/// <summary>
/// 外部回调用
/// </summary>
public OnItemRender onItemRender;
public enum ViewDirection
{
Horizontal,
Vertical
}
初始话组件
private void InitComponent()
{
scrollRect = gameObject.GetComponent<UnityEngine.UI.ScrollRect>();
viewportRectTransform = scrollRect.viewport;
contentRectTransform = scrollRect.content;
scrollRect.onValueChanged.AddListener(OnScrollValueChanged);
}
初始化一些数据
/// <summary>
/// 初始化数据
/// </summary>
private void InitVars()
{
cells = new Dictionary<int, GameObject>();
reUseCells = new List<GameObject>();
contentOffset = new Vector2(0, 0);
totalCellCount = 8; // 总的子项数量
cellInterval = 20; // 子项的间隔
visibleViewSize = viewportRectTransform.rect.size;
cellSize = cellItem.GetComponent<RectTransform>().rect.size;
if (scrollRect.horizontal)
{
currentDir = ViewDirection.Horizontal;
totalViewSize = new Vector2(cellSize.x * totalCellCount + cellInterval * (totalCellCount - 1), contentRectTransform.sizeDelta.y);
totalScrollDistance = totalViewSize.x - visibleViewSize.x;
} else
{
currentDir = ViewDirection.Vertical;
totalViewSize = new Vector2(contentRectTransform.sizeDelta.x, cellSize.y * totalCellCount + cellInterval * (totalCellCount - 1));
totalScrollDistance = totalViewSize.y - visibleViewSize.y;
}
}
private void InitView()
{
int count = 0;
if (currentDir == ViewDirection.Horizontal)
{
// 所有锚点设置成一样的
contentRectTransform.anchorMin = new Vector2(0, 0.5f);
contentRectTransform.anchorMax = contentRectTransform.anchorMin;
contentRectTransform.pivot = contentRectTransform.anchorMin;
if (visibleViewSize.x > totalViewSize.x)
{
count = Mathf.FloorToInt((totalViewSize.x + cellInterval) / (cellSize.y + cellInterval));
} else
{
count = Mathf.CeilToInt((visibleViewSize.x + cellInterval) / (cellSize.y + cellInterval));
}
} else
{
contentRectTransform.anchorMin = new Vector2(0.5f, 1);
contentRectTransform.anchorMax = contentRectTransform.anchorMin;
contentRectTransform.pivot = contentRectTransform.anchorMin;
if (visibleViewSize.y > totalViewSize.y)
{
count = Mathf.FloorToInt((totalViewSize.y + cellInterval) / (cellSize.y + cellInterval));
}
else
{
// count * cellSize + (count - 1) * cellInterval = count * (cellSize + cellInterval) - cellInterval >= visibleViewSize
// => count >= (visibleViewSize - cellInterval) / (cellSize + cellInterval)
count = Mathf.CeilToInt((visibleViewSize.y + cellInterval) / (cellSize.y + cellInterval));
}
}
// 设置内容面板尺寸
contentRectTransform.sizeDelta = totalViewSize;
contentRectTransform.anchoredPosition = Vector2.zero;
for (int i = 0; i < count; ++i)
{
OnCellCreateAtIndex(i);
}
}
public void SetOnLoadCellCallback(OnItemRender func)
{
onItemRender = func;
}
/// <summary>
/// 直接跳到最上面或者最左边
/// </summary>
public void JumpToTop()
{
scrollRect.StopMovement();
if (currentDir == ViewDirection.Horizontal)
{
contentRectTransform.anchoredPosition = new Vector2(0, contentRectTransform.anchoredPosition.y);
} else
{
contentRectTransform.anchoredPosition = new Vector2(contentRectTransform.anchoredPosition.x, 0);
}
}
public void JumpToBottom()
{
if (totalScrollDistance > 0)
{
scrollRect.StopMovement();
if (currentDir == ViewDirection.Horizontal)
{
contentRectTransform.anchoredPosition = new Vector2(-totalScrollDistance, contentRectTransform.anchoredPosition.y);
}
else
{
contentRectTransform.anchoredPosition = new Vector2(contentRectTransform.anchoredPosition.x, totalScrollDistance);
}
}
}
private void OnCellCreateAtIndex(int index)
{
GameObject cell = null;
if (reUseCells.Count > 0)
{
// 有可以重用的
cell = reUseCells[0];
reUseCells.RemoveAt(0);
} else
{
// 没有的话就从预制体创建
cell = GameObject.Instantiate(cellItem);
}
cell.transform.SetParent(contentRectTransform);
cell.transform.localScale = Vector3.one;
RectTransform cellRectTrans = cell.GetComponent<RectTransform>();
if (currentDir == ViewDirection.Horizontal)
{
cellRectTrans.anchorMin = new Vector2(0, 0.5f);
cellRectTrans.anchorMax = cellRectTrans.anchorMin;
cellRectTrans.pivot = cellRectTrans.anchorMin;
float posX = index * cellSize.x + index * cellInterval;
cellRectTrans.anchoredPosition = new Vector2(posX, 0);
} else
{
cellRectTrans.anchorMin = new Vector2(0.5f, 1);
cellRectTrans.anchorMax = cellRectTrans.anchorMin;
cellRectTrans.pivot = cellRectTrans.anchorMin;
float posY = index * cellSize.y + index * cellInterval;
cellRectTrans.anchoredPosition = new Vector2(0, -posY);
}
cell.name = index.ToString();
cell.SetActive(true);
cells.Add(index, cell);
// 回调
onItemRender(index, cell.transform);
}
private void OnScrollValueChanged(Vector2 offset)
{
// offset 的x,y都是0-1之间的数 分别代表横向滑出的宽度百分比 纵向。。。
OnCellScrolling(offset);
}
private void OnCellScrolling(Vector2 offset)
{
if ((currentDir == ViewDirection.Horizontal && totalViewSize.x <= visibleViewSize.x) ||
(currentDir == ViewDirection.Vertical && totalViewSize.y <= visibleViewSize.y))
{
return;
}
offset.x = Mathf.Max(Mathf.Min(offset.x, 1), 0);
offset.y = Mathf.Max(Mathf.Min(offset.y, 1), 0);
contentOffset.x = totalScrollDistance * offset.x;
contentOffset.y = totalScrollDistance * (1 - offset.y);
CalCellIndex();
}
private void CalCellIndex()
{
float startOffset = 0f;
float endOffset = 0f;
if (currentDir == ViewDirection.Horizontal)
{
startOffset = contentOffset.x; // 当前可见区域起始x坐标
endOffset = contentOffset.x + visibleViewSize.x;
startIndex = Mathf.CeilToInt((startOffset + cellInterval) / (cellSize.x + cellInterval)) - 1;
startIndex = Mathf.Max(0, startIndex);
endIndex = Mathf.CeilToInt((endOffset + cellInterval) / (cellSize.x + cellInterval)) - 1;
endIndex = Mathf.Min(endIndex, totalCellCount - 1);
} else
{
startOffset = contentOffset.y;
endOffset = contentOffset.y + visibleViewSize.y;
startIndex = Mathf.CeilToInt((startOffset + cellInterval) / (cellSize.y + cellInterval)) - 1;
startIndex = Mathf.Max(0, startIndex);
endIndex = Mathf.CeilToInt((endOffset + cellInterval) / (cellSize.y + cellInterval)) - 1;
endIndex = Mathf.Min(endIndex, totalCellCount - 1);
}
UpdateCells();
}
private void UpdateCells()
{
List<int> delList = new List<int>();
foreach(KeyValuePair<int, GameObject> pair in cells)
{
if (pair.Key < startIndex || pair.Key > endIndex)
{
delList.Add(pair.Key);
reUseCells.Add(pair.Value);
}
}
foreach(int index in delList)
{
cells.Remove(index);
}
for (int i = startIndex; i <= endIndex; ++i)
{
if (!cells.ContainsKey(i))
{
OnCellCreateAtIndex(i);
}
}
}
版权声明:本文为jijun7885417原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。