UGUI實現迴圈列表
阿新 • • 發佈:2018-12-21
主要思路是通過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 }