首页 > 代码库 > [UGUI]ListLayoutGroup--可重用的滚动列表

[UGUI]ListLayoutGroup--可重用的滚动列表

为了不生成太多的GameObject,当滚动的时候,需要将出框的item重复利用起来。这个网上已经有了很多例子。我为了项目使用方便,在GridLayoutGroup基础上修改了一下,配合ScrollRect使用

技术分享

首先在传入数据的时候,需要知道要显示多少数据,为了拖动时看不到突然消失的item,会多显示一个(往前滑动的时候和往后滑动的时候会放在最前面或最后面):

    private float GetScrollRectSize()
    {
        var rectSize = m_scrollRect.GetComponent<RectTransform> ().rect.size;
        return m_Constraint = IsVertical ? rectSize.y : rect.y;
    }
    private float GetCellSize()
    {
        return m_Constraint = IsVertical ? cellSize.y + spacing.y : cellSize.x + spacing.x;
    }
    public void SetData<P, D>(ICollection<D> dataList, System.Action<int, P, D> setContentHandler)
        where P : MonoBehaviour
    {
        ........
        displayListCount = Mathf.CeilToInt(GetScrollRectSize () / GetCellSize()) + 1;
        .......
    }

 

在滚动的时候,首先需要知道最左上角的数据index:

    public int GetStartIndex()
    {
        if (m_dataList == null || m_dataList.Count == 0)
            return 0;
        float anchorPosition = IsVertical ? rectTransform.anchoredPosition.y : rectTransform.anchoredPosition.x;
        if(!IsVertical)
            anchorPosition *= -1;
        anchorPosition -=  GetCellSize() * 0.5f - GetPadding();
        return (int)(anchorPosition / GetCellSize()) * constraintCount;
    }

 

为了不产生其他开销,Item的实际顺序不会改动,这就需要在已有的顺序中对其在数据中的实际顺序做映射,举个例子,如果一屏可以显示四个数据,那么对应不同的startIndex,四个item的映射关系如下:

Start Index Actual Index
0 0 1 2 3
1 4 1 2 3
2 4 5 1 2

 

 

 

 

如下公式可得到Actual Index(对于无限滚动的列表,start index可能小于0):

    private  int GetActualIndex(int startIndex, int index)
    {
        var count = GetChildCount;
        return ((startIndex + (startIndex >= 0 ? (count - index - 1) : -index)) / count * count + index);
    }

 

滚动时,可根据Actual Index设置Item的位置,并对比滚动前的Actual Index是否改变,决定是否更新数据:

    private void SetCellsAlongAxis (int axis)
    {
        ......
        for(int i = 0; i < m_childCount; ++i) 
        {
            var child = m_childList[i];
                        ...
            if (IsVertical) {
                positionX = Mathf.Abs(actualIndex) % cellsPerMainAxis;
                positionY = actualIndex / cellsPerMainAxis;
            } else {
                positionX = actualIndex / cellsPerMainAxis;
                positionY = Mathf.Abs(actualIndex) % cellsPerMainAxis;
            }

            if (cornerX == 1)
                positionX = actualCellCountX - 1 - positionX;
            if (cornerY == 1)
                positionY = actualCellCountY - 1 - positionY;
            
......
if(actualIndex != previousIndex) { SetItemContent(acutalIndex, child, m_dataList[i]); } SetChildAlongAxis (child, 0, startOffset.x + (cellSize [0] + spacing [0]) * positionX, cellSize [0]); SetChildAlongAxis (child, 1, startOffset.y + (cellSize [1] + spacing [1]) * positionY, cellSize [1]); }
    }

 

[UGUI]ListLayoutGroup--可重用的滚动列表