1. 程式人生 > >UGUI實現迴圈列表

UGUI實現迴圈列表

主要思路是通過ScrollView藉助實現的,這個主要以只能垂直拉動為例。 為了方便理解,我們可以先建立一個ScrollView,然後新建幾個Image放在Scrollview的Content物體之下,自己隨意拖動,將其擺好,新建出來的場景圖如下圖所示。 在這裡插入圖片描述 當我向下拖動時,如下圖所示,Content 的anchoredPosition.y會發生變化,而這個值與拖動長度有關,一個Image我設定的大小是寬高分別是60X60,我剛剛差不多拖動了1個Image還要多一些的距離,現在值Conten的Y值增加到100左右,因此可以看出,我可利用Content的Y值,從而大概知道自己向上拉動了幾個Image。如下圖所示。 在這裡插入圖片描述

當我們拖動時,ScrollView的OnVauleChanged事件就會被觸發。大體思路是當我們每生成一個Image時,我們就給他賦予一個Index值來記錄它。每當玩家拖動滑動條,觸發OnVauleChange事件時,我們計算當前應該的Index值,從而來重新整理介面。具體程式碼如下圖所示。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MyLoopScrollvewMgr : MonoBehaviour {

    private  ScrollRect myScrollView;
    private RectTransform myContent;

    public GameObject CellPerfab;

    /// <summary>
    /// 當前場景中生成的Cell物件列表
    /// </summary>
    public   List<GameObject> CellItemList = new List<GameObject>();
    /// <summary>
    /// Cell與分配給他的Index對應表
    /// </summary>
    public   Dictionary<GameObject, int> CellIndexDic = new Dictionary<GameObject, int>();
    [Header ("引數設定")]
    public  RectOffset MyGridPadding;
    public  Vector2 MyGridSpacing;
    public  Vector2 MyGridCellSize;
    /// <summary>
    /// 建立在場景中的Item數量個數引數
    /// </summary>
    private int creatCount=15;
    /// <summary>
    /// 當前資料的Item總數
    /// </summary>
    private int dataCount=68;
    /// <summary>
    ///非法的起始索引
    /// </summary>
    private const int invalidStartIndex = -1;
    /// <summary>
    /// 當前Content的Y值下第一個開始的Index值
    /// </summary>
    private int StartIndex;
    /// <summary>
    /// 一行Cell的個數
    /// </summary>
    public int rowCellNum=3;
    /// <summary>
    /// 上方快取行數
    /// </summary>
    private int OffsetTopLineNum = 2;
    /// <summary>
    /// 下方快取行數
    /// </summary>
    private int OffsetBottomLineNum=2;
    void Start () {
        Init();

    }
	
	void Init()
    {
        CellItemList.Clear();
        CellIndexDic.Clear();

        myScrollView = GetComponent<ScrollRect>();
        myContent = myScrollView.content;      
        MyGridCellSize = CellPerfab.GetComponent<RectTransform>().sizeDelta;


        #region Content引數初始化
        rowCellNum = (int)(myScrollView  .GetComponent<RectTransform>().sizeDelta.x / MyGridCellSize.x);
        SetContentAnchors();
        myContent .sizeDelta = SetContentSize();
        myContent.anchoredPosition = Vector2.zero;
        #endregion


        StartIndex = 0;
        for (int i = 0; i < creatCount ; i++)
        {
            CreatCell(i);
        }

        myScrollView.onValueChanged.RemoveAllListeners();
        myScrollView.onValueChanged.AddListener(OnValueChanged);

    }

    private void OnValueChanged(Vector2 arg0)
    {
        int CurrentIndex = GetCurrentIndex();       
        if (CurrentIndex !=StartIndex &&CurrentIndex >invalidStartIndex )
        {
            StartIndex = CurrentIndex;
            //為了將需要快取的也先生出來,以免玩家滑動太快,顯示不過來。
            //如果不考慮快取,min應該為StartIndex,max應該為StartIndex+creatCount
            int min = Mathf.Max(StartIndex - OffsetTopLineNum * rowCellNum, 0);
            int max = Mathf.Min( StartIndex + creatCount + OffsetBottomLineNum * rowCellNum,dataCount );
            for (int i = CellItemList.Count-1; i >=0 ; i--)
            {
                GameObject go = CellItemList[i];
                int index = CellIndexDic[go];                                       
                if(index <min ||index >max )
                {
                    ReturnCell(go);
                }
            }

            float maxCreat = Mathf.Min(StartIndex + creatCount, dataCount);
            bool isExit = false;
                      
            for (int i = min; i < max; i++)
            {
                isExit = false;
                for (int j = 0; j < CellItemList.Count; j++)
                {
                    GameObject cell = CellItemList[j];
                    if (CellIndexDic[cell] == i)
                    {
                        isExit = true;
                        break;
                    }
                }
                if (isExit)
                {
                    continue;
                }
                CreatCell(i);
            }
            #region 
           /* for (int i = StartIndex ; i < maxCreat; i++)
            {
                isExit = false;
                for (int j = 0; j < CellItemList .Count ; j++)
                {
                    GameObject cell = CellItemList[j];
                    if(CellIndexDic [cell]==i )
                    {
                        isExit = true;
                        break;
                    }
                }
                if(isExit )
                {
                    continue;
                }
                CreatCell(i);
            }*/
            #endregion

        }
    }
    private int  GetCurrentIndex()
    {
        int index = 0;
        if(myScrollView .vertical )
        {
            index =Mathf .FloorToInt ( myContent.anchoredPosition .y / (MyGridCellSize.y + MyGridSpacing .y));
        }
        return index*rowCellNum ;
    }
    private void CreatCell(int cellIndex)
    {
        GameObject cell = GetItemInPool();
        cell.SetActive(true);
        cell.transform.name = cellIndex.ToString();
        cell.transform.Find("Image/ID").gameObject.GetComponent<Text>().text = cellIndex.ToString();
        cell.transform.parent = myContent;
        RectTransform rect = cell .GetComponent<RectTransform>();               
        rect.pivot = new Vector2(0, 1);
        //垂直拉動以左上為錨點
        rect.anchorMin = new Vector2(0, 1);
        rect.anchorMax = new Vector2(0, 1);
        cell.transform.localPosition  = GetCellPosition(cellIndex);
        CellItemList.Add(cell);
        CellIndexDic.Add(cell, cellIndex);
    }

    private Vector3   GetCellPosition(int cellIndex)
    {
        Vector3 tempvec = Vector3 .zero;
        if(myScrollView .vertical && myScrollView.horizontal == false)
        {
            int row =cellIndex / rowCellNum;
            int column = cellIndex% rowCellNum;
            tempvec.x = MyGridCellSize.x * column + MyGridSpacing.x * (column)+MyGridPadding .left ;
            tempvec.y =-( MyGridCellSize.y * row + MyGridSpacing.y * (row ) + MyGridPadding.top);
        }
        return tempvec;
    }
    private void ReturnCell(GameObject  cell)
    {
        cell.SetActive(false);
        CellItemList.Remove(cell);
           
        CellIndexDic[cell] = invalidStartIndex;
        ReturnItemToPool(cell);
        CellIndexDic.Remove(cell);
    }
    #region Content相關設定方法
    private Vector2  SetContentSize()
    {
        Vector2 tempContentSize = Vector2.zero;
        if (myScrollView == null) return tempContentSize; 
        if (myScrollView.vertical && myScrollView.horizontal == false)
        {
            int row =Mathf .CeilToInt ((float)dataCount / rowCellNum);
            tempContentSize.x = myContent.sizeDelta.x;
            tempContentSize.y = (MyGridCellSize.y *(row) + MyGridSpacing .y *(row )+MyGridPadding.top );
        }
          
        return tempContentSize;
    }
   private void SetContentAnchors()
    {
        if (myScrollView == null) return;
        //僅僅考慮當前只能垂直拉動或水平拉動情況
        if(myScrollView .vertical&&myScrollView .horizontal ==false  )
        {
            //垂直拉動以上方為錨點
            myContent.anchorMin = new Vector2(0, 1);
            myContent.anchorMax = new Vector2(1, 1);
        }else if((!myScrollView.vertical) && myScrollView.horizontal)
        {
            //水平拉動以左邊為錨點
            myContent.anchorMin = new Vector2(0, 0);
            myContent.anchorMax = new Vector2(0, 1);
        }
    }
    #endregion
    #region 簡單物件池

    private Stack<GameObject> itemPools = new Stack<GameObject>();
    private int poolsMaxCount = 40;
    public GameObject GetItemInPool()
    {
        GameObject item = null;
        if(itemPools.Count >0)
        {
            item = itemPools.Pop();
        }
        if(item ==null )
        {
            item = Instantiate(CellPerfab);
        }
        return item;
    }

    public void ReturnItemToPool(GameObject  item)
    {
        DestroyObject(item);
        //if (item == null) return;
        //if (itemPools.Count > poolsMaxCount)
        //{
        //    DestroyObject(item);
        //}
        //else
        //{
        //    itemPools.Push(item);
        //}
    }
    #endregion
}