zoukankan      html  css  js  c++  java
  • [UGUI]ListLayoutGroup--可重用的滚动列表

    一、相同cell size的可重用列表:

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

    首先在传入数据的时候,需要知道要显示多少数据,为了拖动时看不到突然消失的item,会多显示一个:

        private float GetScrollRectSize()
        {
            var rectSize = m_scrollRect.rectTransform.rect.size;
            return IsVertical ? rectSize.y : rect.y;
        }
        private float GetCellSize()
        {
            return 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 2 3

    如下公式可得到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 < childCount; ++i) 
            {
                var child = 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]); }
        }

    二 不同cell size的可重用列表

    在一的基础上稍微做一下修改

    为了滚动条大小正确,需要提前知道计算出data对应的cell size

    protected List<Vector2> m_sizeList = new List<Vector2>();
    protected List<float> m_positionList = new List<float>();
    protected float m_minSize = float.MaxValue;//同屏item的最大数量由这个决定
            public void ReCalcItemSize(bool rebuildSizeList = false)
            {
                if (m_getSizeHandler == null)//外部传入,计算data对应的cell size
                    return;
                if(rebuildSizeList)
                    m_sizeList.Clear ();
                m_positionList.Clear ();
                m_minSize = float.MaxValue;
                m_otherMaxSize = 0f;
                float totalSize = 0f;
                m_positionList.Add (0);
                for (int i = 0; i < m_dataList.Count; ++i)
                {
                    var size = Vector2.zero;
    
                    if (rebuildSizeList)
                    {
                        size = m_getSizeHandler (i, m_dataList [i]);
                    } else
                    {
                        size = m_sizeList [i];
                    }
                    var cur = (isVertical ? size.y : size.x);
                    m_minSize = Mathf.Min (cur, m_minSize);
                    m_otherMaxSize = Mathf.Max ((isVertical ? size.x : size.y), m_otherMaxSize);
                    if(rebuildSizeList)
                        m_sizeList.Add (size);
                    totalSize += cur + spacing;
                    m_positionList.Add (totalSize);
                }
    
                m_displayListCount = Mathf.CeilToInt(GetScrollRectSize() / (m_minSize + spacing));
                ...
            }

    接着需要重写方法,让ScrollRect得到正确的滚动范围:

        public override void CalculateLayoutInputHorizontal()
        {
            CalcAlongAxis(0, isVertical);
        }
    
        public override void CalculateLayoutInputVertical()
        {
            CalcAlongAxis(1, isVertical);
        }
            protected void CalcAlongAxis(int axis, bool calcVertical)
            {
                if (m_ScrollRect == null)
                    return;
                float combinedPadding = (axis == 0 ? padding.horizontal : padding.vertical);
    
                float totalMin = combinedPadding;
    
                bool alongOtherAxis = (calcVertical ^ (axis == 1));
                if (alongOtherAxis)
                    totalMin += m_otherMaxSize;
                
                if (!alongOtherAxis && m_positionList.Count > 0)
                {
                    totalMin += m_positionList[m_positionList.Count - 1] - spacing;
                }
                SetLayoutInputForAxis(totalMin, totalMin, totalMin, axis);
            }    

    最后计算StartIndex也要做一下修改:

            protected int GetStartIndex()
            {
                if (m_dataList == null || m_dataList.Count == 0)
                    return 0;
                var curPosition = (isVertical ? rectTransform.anchoredPosition.y : rectTransform.anchoredPosition.x);
                if (isVertical)
                    curPosition -= padding.top;
                else
                    curPosition = -curPosition - padding.left;
    
                int index = 0;
                for (int i = 0; i < m_positionList.Count; ++i)
                {
                    if (m_positionList [i] > curPosition)
                        break;
                    index = i;
                }
                return index;
            }

    比较繁琐,主要是需要提前计算好所有data对应的cell size,这个是不能省的。

  • 相关阅读:
    learning scala view collection
    scala
    learning scala dependency injection
    learning scala implicit class
    learning scala type alise
    learning scala PartialFunction
    learning scala Function Recursive Tail Call
    learning scala Function Composition andThen
    System.Threading.Interlocked.CompareChange使用
    System.Threading.Monitor的使用
  • 原文地址:https://www.cnblogs.com/drashnane/p/6391435.html
Copyright © 2011-2022 走看看