UGUI無限滾動

這個主要是用了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了.

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章