無限滾動UI面板
阿新 • • 發佈:2019-01-11
using UnityEngine; using System.Collections; using System.Collections.Generic; public class Mall { public Sprite sprite;//貼圖 public string ModelName = "";//模型名字 public string GoodsMessage = "";//物品介紹 //toto } public class UILoopListEzample : MonoBehaviour { public int ItemAmount; public UILoopList uiList; // Use this for initialization void Start() { uiList.onSelectedEvent = OnSelectedEventHandler; List<int> listData = new List<int>(); for (int i = 0; i < ItemAmount; i++) { listData.Add(i); } uiList.Data(listData); } private void OnSelectedEventHandler(UILoopItem item) { Debug.LogError("選擇的單元資料為:" + item.GetData().ToString()); } } ------------------------------------======================------------------------------- using UnityEngine; using System.Collections; using UnityEngine.UI; public class UILoopItem : MonoBehaviour { [System.NonSerialized] public int itemIndex;//當前資料第幾個 [System.NonSerialized] public GameObject itemObject; private object data; private Mall Mall_; public void UpdateItem(int index, GameObject item)//(object mall,int index, GameObject item) { itemIndex = index; itemObject = item; } public virtual void Data(object data) { this.data = data; transform.Find("Text").GetComponent<Text>().text = "資料" + data.ToString(); } public virtual object GetData() { return data; } public virtual void SetSelected(bool selected) { } } ----------------------------------========================------------------------------- using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class UILoopList : MonoBehaviour { /// <summary> /// 滾動的方向 /// </summary> enum Direction { Horizontal,//水平方向 Vertical//垂直方向 } /// <summary> /// 離上邊的距離 /// </summary> [SerializeField] private float topPadding = 0; /// <summary> /// 離下邊的距離 /// </summary> [SerializeField] private float bottomPadding = 0; /// <summary> ///time框 /// </summary> [SerializeField] private RectTransform m_Cell; [SerializeField] private Vector2 m_Page = new Vector2(1, 1);//必須設定;幾行幾列 [SerializeField] Direction direction = Direction.Horizontal; [SerializeField, Range(4, 10)] private int m_BufferNo;//Item的數量 /// <summary> /// Item的間隔 /// </summary> [SerializeField] private float cellGapX = 0f; [SerializeField] private float cellGapY = 0f; public delegate void OnSelectedEvent(UILoopItem item); /// <summary> /// 選擇事件 但元件上一定要有Button元件 設定要在Data()設定資料前 /// </summary> public OnSelectedEvent onSelectedEvent;// private List<RectTransform> m_InstantiateItems = new List<RectTransform>(); private List<RectTransform> m_oldItems = new List<RectTransform>(); /// <summary> /// Time的實際數量就是數字 /// </summary> private IList m_Datas; /// <summary> /// 調整Item的間隔 /// </summary> public Vector2 CellRect { get { return m_Cell != null ? new Vector2(cellGapX + m_Cell.sizeDelta.x, cellGapY + m_Cell.sizeDelta.y) : new Vector2(100, 100); } } /// <summary> /// 調整Item的間隔返回的是X 還是 Y /// </summary> public float CellScale { get { return direction == Direction.Horizontal ? CellRect.x : CellRect.y; } } private float m_PrevPos = 0; // 返回的是Content距離錨點的距離 x或y public float DirectionPos { get { return direction == Direction.Horizontal ? m_Rect.anchoredPosition.x : m_Rect.anchoredPosition.y; } } private int m_CurrentIndex;//頁面的第一行(列)在整個conten中的位置 private Vector2 m_InstantiateSize = Vector2.zero; public Vector2 InstantiateSize { get { if (m_InstantiateSize == Vector2.zero) { float rows, cols; if (direction == Direction.Horizontal) { rows = m_Page.x; cols = m_Page.y + (float)m_BufferNo; } else { rows = m_Page.x + (float)m_BufferNo; cols = m_Page.y; } // 5 1 m_InstantiateSize = new Vector2(rows, cols); } return m_InstantiateSize; } } /// <summary> /// 返回的是行數成於列數 /// </summary> public int PageCount { get { return (int)m_Page.x * (int)m_Page.y; } } public int PageScale { get { return direction == Direction.Horizontal ? (int)m_Page.x : (int)m_Page.y; } }//返回的是幾行幾列 private ScrollRect m_ScrollRect; private RectTransform m_Rect; public int InstantiateCount { get { return (int)InstantiateSize.x * (int)InstantiateSize.y; } } protected void Awake() { m_ScrollRect = GetComponentInParent<ScrollRect>(); m_ScrollRect.horizontal = direction == Direction.Horizontal;//滾動方向是否是垂直 m_ScrollRect.vertical = direction == Direction.Vertical;//滾動方向是否是水平 m_Rect = GetComponent<RectTransform>(); if (m_Cell.transform.parent != null) m_Cell.gameObject.SetActive(false);//隱藏第一個Item面板 m_Rect.anchorMax = Vector2.zero;//設定中心點Content m_Rect.anchorMin = Vector2.zero;//設定錨點Content m_Rect.pivot = Vector2.zero;//它在這個RectTransform中旋轉的標準化位置。 m_Rect.anchoredPosition = new Vector2(0f, 0f);//相對於錨點的位置設定為0,0 } /// <summary> /// 設定資料 資料格式為IList /// </summary> /// <param name="data">Data.</param> public void Data(object data) { Reset(); m_Datas = data as IList; if (m_Datas.Count > PageCount)//item數量是否大於 行成於列 { //to setBound(getRectByNum(m_Datas.Count));//構建滑動框的大小 } else { setBound(m_Page); } if (m_Datas.Count > InstantiateCount)//是否Item數量大於 tiem數量加上行的數量 { // print(m_Datas.Count+" "+InstantiateCount ); //toto while (m_InstantiateItems.Count < InstantiateCount) { createItem(m_InstantiateItems.Count);//在這裡生成的五個 } } else { while (m_InstantiateItems.Count > m_Datas.Count) { removeItem(m_InstantiateItems.Count - 1); } while (m_InstantiateItems.Count < m_Datas.Count) { createItem(m_InstantiateItems.Count); } } if (m_Datas.Count > 0) { int count = Mathf.Min(m_InstantiateItems.Count, m_Datas.Count); for (int i = 0; i < count; i++) { updateItem(i, m_InstantiateItems[i].gameObject); } } } private void Reset() { for (int i = 0; i < m_InstantiateItems.Count; i++) { m_InstantiateItems[i].gameObject.SetActive(false); m_oldItems.Add(m_InstantiateItems[i]); } m_InstantiateItems.Clear(); m_PrevPos = 0; m_CurrentIndex = 0; selectedObject = null; selectedItem = null; this.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, 0); } public void SetIndexToBottom(int itemIndex) { this.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, CellRect.y * itemIndex - m_ScrollRect.GetComponent<RectTransform>().sizeDelta.y + CellRect.y * 2 + topPadding + bottomPadding); } private void createItem(int index)//0 { RectTransform item = null; if (m_oldItems.Count > 0) { item = m_oldItems[0]; m_oldItems.Remove(item); } else { item = GameObject.Instantiate(m_Cell); item.SetParent(transform, false); item.anchorMax = Vector2.zero; item.anchorMin = Vector2.zero; item.pivot = Vector2.zero; } item.name = "item" + index; item.anchoredPosition = getPosByIndex(index); // print(getPosByIndex(index)); m_InstantiateItems.Add(item);//新增進集合中 item.gameObject.SetActive(true); //updateItem(index, item.gameObject); } private void removeItem(int index) { RectTransform item = m_InstantiateItems[index]; m_InstantiateItems.Remove(item); item.gameObject.SetActive(false); m_oldItems.Add(item); //RectTransform.Destroy(item.gameObject); } /// <summary> /// 由格子數量獲取多少行多少列 /// </summary> /// <param name="num"></param>格子個數 /// <returns></returns> private Vector2 getRectByNum(int num) { return direction == Direction.Horizontal ? new Vector2(m_Page.x, Mathf.CeilToInt(num / m_Page.x)) : new Vector2(Mathf.CeilToInt(num / m_Page.y), m_Page.y); } /// <summary> /// 設定Content的大小 /// </summary> /// <param name="rows"></param>行數 /// <param name="cols"></param>列數 private void setBound(Vector2 bound) { // Debug.Log(bound.y * CellRect.x+":X");Debug.Log(bound.x * CellRect.y + bottomPadding + topPadding + ":Y"); m_Rect.sizeDelta = new Vector2(bound.y * CellRect.x, bound.x * CellRect.y + bottomPadding + topPadding); } public float MaxPrevPos { get { float result; Vector2 max = getRectByNum(m_Datas.Count); if (direction == Direction.Horizontal) { result = max.y - m_Page.y; } else { result = max.x - m_Page.x; } return result * CellScale; } } public float scale { get { return direction == Direction.Horizontal ? 1f : -1f; } }//是橫向就是1不是為-1 private bool isFirst = true; void Update() { if (isFirst == true) { isFirst = false; return; } //1或-1 ×與 X或Y -0 while (scale * DirectionPos - m_PrevPos < -CellScale * 2) { //Debug.Log(scale.ToString() + " " + DirectionPos.ToString() + " " + m_PrevPos.ToString() + "_____________" + -CellScale * 2); if (m_PrevPos <= -MaxPrevPos) return; m_PrevPos -= CellScale; List<RectTransform> range = m_InstantiateItems.GetRange(0, PageScale); m_InstantiateItems.RemoveRange(0, PageScale); m_InstantiateItems.AddRange(range); for (int i = 0; i < range.Count; i++) { moveItemToIndex(m_CurrentIndex * PageScale + m_InstantiateItems.Count + i, range[i]); } m_CurrentIndex++; } while (scale * DirectionPos - m_PrevPos > -CellScale) { // Debug.Log(scale.ToString() + " " + DirectionPos.ToString() + " " + m_PrevPos.ToString() + "_____________" + -CellScale); if (Mathf.RoundToInt(m_PrevPos) >= 0) return; m_PrevPos += CellScale; m_CurrentIndex--; if (m_CurrentIndex < 0) return; List<RectTransform> range = m_InstantiateItems.GetRange(m_InstantiateItems.Count - PageScale, PageScale); m_InstantiateItems.RemoveRange(m_InstantiateItems.Count - PageScale, PageScale); m_InstantiateItems.InsertRange(0, range); for (int i = 0; i < range.Count; i++) { moveItemToIndex(m_CurrentIndex * PageScale + i, range[i]); } } } private void moveItemToIndex(int index, RectTransform item) { item.anchoredPosition = getPosByIndex(index); updateItem(index, item.gameObject); } /// <summary> /// 初始化間距位置 /// </summary> /// <param name="index"></param> /// <returns></returns> private Vector2 getPosByIndex(int index) { return direction == Direction.Horizontal ? new Vector2(Mathf.Floor(index / InstantiateSize.x) * CellRect.x, -(index % InstantiateSize.x) * CellRect.y) : new Vector2((index % InstantiateSize.y) * CellRect.x, -Mathf.Floor(index / InstantiateSize.y) * CellRect.y - topPadding); //float x, y; //if(direction == Direction.Horizontal) //{ // x = index % m_Page.x; // y = Mathf.FloorToInt(index / m_Page.x); //} //else //{ // x = Mathf.FloorToInt(index / m_Page.y); // y = index % m_Page.y; //} //return new Vector2(y * CellRect.x, -x * CellRect.y); } private object selectedObject = null; [System.NonSerialized] public UILoopItem selectedItem = null; private void updateItem(int index, GameObject item) { item.SetActive(index < m_Datas.Count); if (item.activeSelf) { UILoopItem lit = item.GetComponent<UILoopItem>(); lit.UpdateItem(index, item); lit.Data(m_Datas[index]);//列印函式 Debug.Log(selectedObject + " 666 " + m_Datas[index]); if (selectedObject == m_Datas[index]) { lit.SetSelected(true); } else { lit.SetSelected(false); } //該BUtton在列表中的索引 if (lit.GetComponent<Button>() != null && onSelectedEvent != null && addClickEventList.IndexOf(lit.GetComponent<Button>()) < 0) { addClickEventList.Add(lit.GetComponent<Button>()); lit.GetComponent<Button>().onClick.AddListener(delegate () { if (onSelectedEvent != null) { if (selectedItem != null && selectedItem != item.GetComponent<UILoopItem>()) { selectedItem.SetSelected(false); } selectedItem = item.GetComponent<UILoopItem>(); selectedObject = selectedItem.GetData(); selectedItem.SetSelected(true); onSelectedEvent(selectedItem); } }); } } } private List<Button> addClickEventList = new List<Button>(); /// <summary> /// 選中的物件 /// </summary> /// <returns></returns> public object GetSelectedData() { return selectedObject; } /// <summary> /// 設定選中物件 /// </summary> /// <param name="selectedIndex"></param> public void SetSelectedIndex(int selectedIndex) { if (m_Datas.Count > 0 && m_Datas.Count > selectedIndex) { selectedObject = m_Datas[selectedIndex]; if (selectedItem != null) { selectedItem.SetSelected(false); } for (int i = 0; i < m_InstantiateItems.Count; i++) { if (selectedObject == m_InstantiateItems[i].GetComponent<UILoopItem>().GetData()) { m_InstantiateItems[i].GetComponent<UILoopItem>().SetSelected(true); selectedItem = m_InstantiateItems[i].GetComponent<UILoopItem>(); } } } } }