这个主要是用了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了.