Unity中ScrollView/ScrollRect的优化,实现自动布局和可复用Item的滑动列表

主要步骤

1.根据滑动列表的布局类型确定Content的Anchors和Pivot、滑动方式为水平滑动或者垂直滑动这里我们以从上到下的垂直滑动为例。

m_ScrollRect.horizontal = arrangementType == ArrangementType.Horizontal;
m_ScrollRect.vertical = !(arrangementType == ArrangementType.Horizontal);
m_isHorizontal = m_ScrollRect.horizontal;

 case ArrangementPos.Down:
            {
                    v2 = new Vector2(0, 1);
                    if (m_isHorizontal)
                    {
                        v2 = new Vector2(0.5f, 0.5f);
                        Debug.LogError("布局错误"+ ArrangementPos.Down + "调整为Center");
                    }
                    m_ScrollViewContent.anchorMax = v2;
                    m_ScrollViewContent.anchorMin = v2;
                    m_ScrollViewContent.pivot = v2;
                    break;
            }

2.根据需要添加的item数量和高确定Content的高。

private void SetContentSize() 
    {
        if(m_isHorizontal) 
        {
            Vector2 v2 = m_ScrollViewContent.rect.size;
            v2.x = (m_CellSize.x + HorizontalSpace) * (Mathf.CeilToInt((float)_dataCount / rowCount));
            m_ScrollViewContent.sizeDelta = v2;
        }
        else 
        {
            Vector2 v2 = m_ScrollViewContent.rect.size;
            v2.y = (m_CellSize.y + VerticalSpace) * (Mathf.CeilToInt((float)_dataCount / colCount));
            m_ScrollViewContent.sizeDelta = v2;
        }
    }

3.根据ScrollRect的高即遮罩区域大小和item的高确定当前可视区域内可容纳的行数

 private void SetViewCount() 
    {
        if (m_isHorizontal)
        {
            Vector2 v2 = m_UIScrollViewRect.rect.size;
            _viewCount = Mathf.CeilToInt(v2.x / (m_CellSize.x + HorizontalSpace)) + 1;
        }
        else
        {
            Vector2 v2 = m_UIScrollViewRect.rect.size;
            _viewCount = Mathf.CeilToInt(v2.y / (m_CellSize.y + VerticalSpace)) + 1;
        }
    }

4.获取Content与ScrollRect的相对坐标的偏移量,确定当前滑动列表滑动了几行。比如当偏移了一个Item的高我们就当作Content滑动了一行。

 public int GetCurLineIndex() 
   {
     switch (arrangementPos) 
     {
      	case ArrangementPos.Down:
            {
             	int line = Mathf.FloorToInt(m_ScrollViewContent.anchoredPosition.y / (m_CellSize.y + VerticalSpace));
                if (line < 0) line = 0;
                return line;
            }
          
     }
   }

5.我们给Item定义一个属性值Index。记录它在Content中的位置。

比如Index=0相当于这个Item的位于Content的最左上角。

6.根据4.中我们获取的当前Content偏移了多少行和3.中可视范围内最多容纳的行确定当前可视范围内Index的起始值和结束值。

比如当初始状态时起始Index值为0,结束值为初始值0+最大容纳的行乘上我们定义的每行的Item的数量。每次Content发生偏移时这里Index的初始值和结束值都随之变化。然后就是生成Item的步骤(见代码)

SetRectItem(GetCurLineIndex());
 private void SetRectItem(int curline)
    {
        int StartIndex=0;
        int EndIndex=0;
        if (m_isHorizontal)
        {
            StartIndex = curline * rowCount;
            EndIndex = StartIndex + _viewCount * rowCount;
            if (EndIndex > _dataCount) 
            {
                EndIndex = _dataCount;
            }
        }
        else
        {
            StartIndex = curline * colCount;
            EndIndex = StartIndex + _viewCount * colCount;
            if (EndIndex > _dataCount)
            {
                EndIndex = _dataCount;
            }
        }
        //复用Item步骤
        List<UIScrollViewItem> removeItem = new List<UIScrollViewItem>();
        if (ItemList.Count != 0) {
            for (int i = 0; i < ItemList.Count; i++)
            {
                if (ItemList[i].Index> EndIndex|| ItemList[i].Index < StartIndex)
                {
                //不在StartIndex和EndIndex范围内的Item将被回收保存在UnUseItemQueue队列中
                    ItemList[i].transform.localScale = Vector3.zero;
                    UnUseItemQueue.Enqueue(ItemList[i]);
                    removeItem.Add(ItemList[i]);
                }else
                {
                	//更新你的Item
                }
            }
        }
        for (int i = 0; i < removeItem.Count; i++)
        {
            ItemList.Remove(removeItem[i]);
        }
        for (int i = StartIndex; i < EndIndex; i++)
        {
            if(i >= _dataCount)continue;
            if (isExistIndex(i)) continue;//是否存在相同Index的item
            CreateItem(i);//创建Item
        }

 private void CreateItem(int index) 
    {
        UIScrollViewItem item;
        if (UnUseItemQueue.Count != 0) 
        {
        	//如果回收队列中存在item就从中出队而不实例化新的item
            item = UnUseItemQueue.Dequeue();
        }
        else 
        {
            GameObject go= AddChild(m_Cell, m_ScrollViewContent);
            item = go.GetComponent<UIScrollViewItem>();
            item.scrollView = this;
        }
        //设置新的Item的Index并保存在可见区域的Item的列表ItemList中
        if (!item.gameObject.activeInHierarchy) item.gameObject.SetActive(true);
        item.transform.localScale = Vector3.one;
        item.Index = index;
        ItemList.Add(item);
        //更新你的Item
    }

7.给ScrollRect的onValueChange添加监听滑动时不断执行4.步骤和6.步骤

m_ScrollRect.onValueChanged.AddListener(onValueChange);
 private void onValueChange(Vector2 v2) 
    {
        if (m_isHorizontal)
        {
            Debug.Log(GetCurLineIndex());
            SetRectItem(GetCurLineIndex());
        }
        else
        {
            Debug.Log(GetCurLineIndex());
            SetRectItem(GetCurLineIndex());
        }
    }

8.根据Item的Index值给每个Item设置相对坐标

public class UIScrollViewItem : MonoBehaviour
    {
        public UIScrollView scrollView;
        public int Index 
        {
            get 
            { 
                return index; 
            }
            set 
            {
                index = value;
                name = index.ToString();
                SetItemPos(index);
            }
        }
        private int index;
        private void SetItemPos(int index)
        {
            Vector3 v3 = scrollView.GetItemPosByIndex(index);
            transform.localPosition = v3;
        }
    }
public Vector3 GetItemPosByIndex(int index) 
    {
        int CurRowOrCol = 0;
        int CurRowOrColIndex = 0;
        Vector3 Pos = new Vector3();
        if (m_isHorizontal) 
        {
           
        }
        else
        {
            if (arrangementPos== ArrangementPos.Down) 
            {
                CurRowOrCol = index / colCount;
                CurRowOrColIndex = index % colCount;
                Pos.x = CurRowOrColIndex * (m_CellSize.x+HorizontalSpace) + m_CellSize.x/2;
                Pos.y = -(CurRowOrCol * (m_CellSize.y+VerticalSpace)+ (m_CellSize.y + VerticalSpace) / 2);
            }
        }
        return  Pos;
    }

最终效果

在这里插入图片描述


版权声明:本文为weixin_44068264原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。