這個主要是用了Grid Layout Group組件
核心代碼有三部分,第一部分是判斷當前要展示的下標,第二部分是位置的更新,第三部分是數據的更新.
先說第一部分得到要展示的下標,也就是說得到要展示第幾個到第幾個數據
public void GetInViewIndexRange(Rect rect, out int from, out int to)
{
Vector2 lt = new Vector2(rect.xMin - PaddingLeft, rect.yMin - PaddingTop);
Vector2 rb = new Vector2(rect.xMax - PaddingLeft, rect.yMax - PaddingTop);
float w = CellSizeX + SpacingX;
float h = CellSizeY + SpacingY;
int col = (int) (lt.x / w);
int row = (int) (lt.y / h);
from = GetIndexOfRowCol(row, col);
col = (int) (rb.x / w);
row = (int) (rb.y / h);
to = GetIndexOfRowCol(row, col);
}
private int GetIndexOfRowCol(int row, int col)
{
if (Constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
if (col >= ConstraintCount)
{
col = ConstraintCount - 1;
}
return row * ConstraintCount + col;
}
if (Constraint == GridLayoutGroup.Constraint.FixedRowCount)
{
if (row >= ConstraintCount)
{
row = ConstraintCount - 1;
}
return col * ConstraintCount + row;
}
return 0;
}
這部分代碼還蠻好理解的,只要只要這些參數是幹嘛的就好了,首先第一個參數是傳入了一個Rect也就是一個矩形,這個矩形怎麼得到的下面再說,然後lt是用矩形的最小x和最小y減去左上的偏移量,這兩個偏移量是Grid Layout Group組件的,爲了幫助大家理解我在下面放了組件的圖,方法中用到的參數請一一對號入座.
https://docs.unity3d.com/Manual/script-GridLayoutGroup.html組件的官方文檔,參數是幹嘛用的詳情就看文檔吧,我就不一一說明
然後rb是用矩形的最大x和最大y減去左上的偏移量,爲什麼減去左上是因爲咱們這個錨點要設置爲左上角,中心點(Pivot)設置成01.
w和h就是物體的寬和高,用x/長,y/高,這樣來計算出當前的行列.
再看一下GetIndexOfRowCol方法,首先判斷下是固定行還是固定列,咱們就拿固定列來說,用之前算的col也就是當前列數與自己設定的固定列數比較,如果大於等於的話就設置爲固定列數-1(這是因爲下標是從0開始,我是這麼理解的),最後再用當前行數*固定列數+計算過後的當前列數,就是當前的下標(第一次用是計算起始下標,第二次是計算結尾下標,用於後面的數據更新和判斷是否要增加或減少Item).
這樣第一部分就完成了.對了講一下Rect是怎麼獲得的
var rt = _content.parent.GetComponent<RectTransform>();
var rect = rt.rect;
_viewHeight = rect.height;
_viewWidth = rect.width;
var pos = content.anchoredPosition;
_viewRect = new Rect(-pos.x, pos.y, _viewWidth, _viewHeight);
content是Scroll View組建的Content,取得是它的錨點位置,加上它父物體的大小(遮罩的大小)
public void OnUpdateView
{
var pos = _content.anchoredPosition;
_viewRect = new Rect(-pos.x, pos.y, _viewWidth, _viewHeight);
UpdateView();
}
然後在滑動的監聽事件中實時更新錨點位置,通過新的錨點位置去更新視圖
第二個重點是位置的更新,之前說過這個是仿照着Grid Layout Group組件寫的,位置判斷也會用到組件的數據
private void DoLayoutForFixedColumnCount(List<RectTransform> rts, int fromIndex)
{
for (int i = 0; i < rts.Count; ++i)
{
int index = fromIndex + i;
rts[i].anchoredPosition = GetPositionOfIndexForFixedColumn(index);
}
}
private void DoLayoutForFixedRowCount(List<RectTransform> rts, int fromIndex)
{
for (int i = 0; i < rts.Count; ++i)
{
int index = fromIndex + i;
rts[i].anchoredPosition = GetPositionOfIndexForFixedRow(index);
}
}
private Vector2 GetPositionOfIndexForFixedColumn(int index)
{
int col = index % ConstraintCount;
int row = index / ConstraintCount;
float lx = PaddingLeft + col * (CellSizeX + SpacingX);
float ly = PaddingTop + row * (CellSizeY + SpacingY);
return new Vector2(lx, -ly);
}
private Vector2 GetPositionOfIndexForFixedRow(int index)
{
int row = index % ConstraintCount;
int col = index / ConstraintCount;
float lx = PaddingLeft + col * (CellSizeX + SpacingX);
float ly = PaddingTop + row * (CellSizeY + SpacingY);
return new Vector2(lx, -ly);
}
也是分爲橫豎,還是拿固定列當例子rts列表是當前實例的Item,fromIndex是當前開始的下標.用index%固定列數,能求出是在第幾列,用index/固定列數求出當前是在第幾行,再用求出的行列去乘上Item的寬高就能得到當前的位置了.
Note:Item的錨點和中心點也是在左上
附加一個計算Content大小的方法
public void SetItemCount(int count)
{
if (Constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
int columns = ConstraintCount;
int rows = (count + ConstraintCount - 1) / ConstraintCount;
Width = PaddingLeft + CellSizeX * columns + SpacingX * (columns - 1) + PaddingRight;
Height = PaddingTop + CellSizeY * rows + SpacingY * (rows - 1) + PaddingBottom;
}
else if (Constraint == GridLayoutGroup.Constraint.FixedRowCount)
{
int rows = ConstraintCount;
int columns = (count + ConstraintCount - 1) / ConstraintCount;
Width = PaddingLeft + CellSizeX * columns + SpacingX * (columns - 1) + PaddingRight;
Height = PaddingTop + CellSizeY * rows + SpacingY * (rows - 1) + PaddingBottom;
}
}
參數count是要展示的數據的個數
最後就是數據的更新了
private void UpdateItemDatas()
{
var index = _fromIndex;
foreach (var item in _items)
{
if (_dataDirty || item.GetIndex() != index)
{
item.SetIndex(index);
item.SetData(_datas[index]);
}
++index;
}
_dataDirty = false;
}
從第一步計算的開始下標開始給Item列表賦值就ok了.