NGUI实现的一套不同大小 Item 的循环滚动代码

测试:

数据 & Item  的 Ctrl :

 1 using UnityEngine;
 2 
 3 public class ScrollViewItemData
 4 {
 5     public int index;
 6     public string name;
 7     public ScrollViewItemData(int index, string name)
 8     {
 9         this.index = index;
10         this.name = name;
11     }
12 }
13 
14 
15 public class ScrollViewItem : MonoBehaviour
16 {
17     UISprite sprBG;
18     UILabel label;
19     public ScrollViewItemData data;
20 
21     void Awake()
22     {
23         sprBG = GetComponent<UISprite>();
24         label = transform.Find("Label").GetComponent<UILabel>();
25     }
26 
27     public Vector3 BgSize
28     {
29         get
30         {
31             if (null != sprBG)
32                 return sprBG.localSize;
33             return Vector3.zero;
34         }
35 
36     }
37 
38     public int BgHeigth
39     {
40         get
41         {
42             if (null != sprBG)
43                 return sprBG.height;
44             return 0;
45         }
46     }
47 
48     public void SetData(ScrollViewItemData data, int index)
49     {
50         this.data = data;
51         label.text = data.name;
52         sprBG.Update();
53         //sprBG.ForceUpdate();
54         this.gameObject.name = index.ToString();
55     }
56 }

Item 大小一致的实现代码:

using System.Collections.Generic;
using UnityEngine;

public class ScrollViewManager : MonoBehaviour
{
    [SerializeField]
    GameObject itemPrefab;

    List<ScrollViewItem> itemList = new List<ScrollViewItem>();
    List<ScrollViewItemData> itemDataList = new List<ScrollViewItemData>();

    UIScrollView sv;
    UIGrid grid;

    //记录scrollviet上一次的位置,用于判断scrollview的移动方向
    float svLastPos = 0;

    //最大y坐标
    float maxHeight = 0;

    //最小y坐标
    float minHeight = 0;

    // Use this for initialization
    void Start()
    {
        //初始化测试数据

        for (int i = 0; i < 50; i++)
        {
            itemDataList.Add(new ScrollViewItemData(i, "" + (i + 1) + " 个元素"));
        }


        sv = transform.GetComponent<UIScrollView>();
        grid = transform.Find("Grid").GetComponent<UIGrid>();

        Vector2 viewsize = transform.GetComponent<UIPanel>().GetViewSize();
        int count = (int)(viewsize.y / grid.cellHeight + 2);
        Debug.Log(count);
        for (int i = 0; i < count; i++)
        {
            if (itemDataList.Count <= i)
                break;

            GameObject obj = NGUITools.AddChild(grid.gameObject, itemPrefab);

            ScrollViewItem item = obj.GetComponent<ScrollViewItem>();
            itemList.Add(item);

            item.SetData(itemDataList[i], i);
        }
        grid.repositionNow = true;
        grid.Reposition();

        svLastPos = grid.transform.localPosition.y;

        maxHeight = viewsize.y / 2 + grid.cellHeight / 2;

        minHeight = -maxHeight;

        itemPrefab.SetActive(false);

    }

    // Update is called once per frame
    void Update()
    {
        float moveDis = sv.transform.localPosition.y - svLastPos;
        if (Mathf.Abs(moveDis) > 0.05)
        {
            bool isup = moveDis > 0;
            if (isup)
            {
                while (itemList[0].transform.localPosition.y + sv.transform.localPosition.y > maxHeight &&
                       itemList[itemList.Count - 1].data.index < itemDataList.Count - 1)
                {
                    ScrollViewItem item = itemList[0];
                    item.SetData(itemDataList[itemList[itemList.Count - 1].data.index + 1], item.data.index);

                    itemList.Add(item);
                    itemList.RemoveAt(0);

                    item.transform.localPosition = itemList[itemList.Count - 2].transform.localPosition - new Vector3(0, grid.cellHeight, 0);
                }
            }
            else
            {
                while (itemList[itemList.Count - 1].transform.localPosition.y + sv.transform.localPosition.y < minHeight &&
                    itemList[0].data.index > 0)
                {
                    ScrollViewItem item = itemList[itemList.Count - 1];
                    item.SetData(itemDataList[itemList[0].data.index - 1], item.data.index);

                    itemList.Insert(0, item);
                    itemList.RemoveAt(itemList.Count - 1);

                    item.transform.localPosition = itemList[1].transform.localPosition + new Vector3(0, grid.cellHeight, 0);
                }
            }

            svLastPos = sv.transform.localPosition.y;
        }
    }
}

Item 大小不一致的代码实现:

  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using System.Text;
  5 using UnityEngine;
  6 
  7 public class GUIScrollViewManager : MonoBehaviour
  8 {
  9     [SerializeField]
 10     GameObject itemPrefab;
 11 
 12     List<ScrollViewItem> itemList = new List<ScrollViewItem>();
 13     List<ScrollViewItemData> itemDataList = new List<ScrollViewItemData>();
 14     List<String> strList = new List<string>();
 15 
 16     UIScrollView sv;
 17     UITable table;
 18 
 19     //记录scrollviet上一次的位置,用于判断scrollview的移动方向
 20     float svLastPos = 0;
 21 
 22     //最大y坐标
 23     float maxHeight = 288.0f;
 24 
 25     //最小y坐标
 26     float minHeight = -288.0f;
 27 
 28     const string tmpStr = "初始化测试数据";
 29     const int Count = 50;
 30 
 31     // Use this for initialization
 32     void Start()
 33     {
 34         //初始化测试数据
 35         System.Random random = new System.Random();
 36         StringBuilder sb = new StringBuilder();
 37 
 38         int sbCounter = 0;
 39         for (int i = 0; i < Count; i++)
 40         {
 41             int tmpCount = random.Next(3, 15);
 42             sbCounter = 0;
 43             while (sbCounter < tmpCount)
 44             {
 45                 ++sbCounter;
 46                 sb.Append(tmpStr);
 47             }
 48             itemDataList.Add(new ScrollViewItemData(i, "" + (i + 1) + " 个元素" + sb.ToString()));
 49             sb.Remove(0, sb.Length);
 50 
 51         }
 52 
 53         sv = transform.GetComponent<UIScrollView>();
 54         table = transform.Find("Table").GetComponent<UITable>();
 55 
 56         int count = 10;
 57         for (int i = 0; i < count; i++)
 58         {
 59             if (itemDataList.Count <= i)
 60                 break;
 61 
 62             GameObject obj = NGUITools.AddChild(table.gameObject, itemPrefab);
 63             ScrollViewItem item = obj.GetComponent<ScrollViewItem>();
 64             itemList.Add(item);
 65 
 66             item.SetData(itemDataList[i], i);
 67         }
 68 
 69         table.Reposition();
 70 
 71         itemPrefab.SetActive(false);
 72     }
 73 
 74     void Update()
 75     {
 76         float moveDis = sv.transform.localPosition.y - svLastPos;
 77 
 78         if (Mathf.Abs(moveDis) > 0.05)
 79         {
 80             bool isup = moveDis > 0;
 81             if (isup)
 82             {
 83                 float height = NGUIMath.CalculateRelativeWidgetBounds(itemList[0].transform).size.y;
 84                 while (itemList[0].transform.localPosition.y + sv.transform.localPosition.y - height > maxHeight &&
 85                        itemList[itemList.Count - 1].data.index < itemDataList.Count - 1)
 86                 {
 87                     ScrollViewItem item = itemList[0];
 88                     item.SetData(itemDataList[itemList[itemList.Count - 1].data.index + 1], item.data.index);
 89 
 90                     itemList.Add(item);
 91                     itemList.RemoveAt(0);
 92 
 93                     float _height = NGUIMath.CalculateRelativeWidgetBounds(itemList[itemList.Count - 2].transform).size.y;
 94                     item.transform.localPosition = itemList[itemList.Count - 2].transform.localPosition - new Vector3(0, _height + 4, 0);
 95                 }
 96             }
 97             else
 98             {
 99                 while (itemList[itemList.Count - 1].transform.localPosition.y + sv.transform.localPosition.y < minHeight &&
100                     itemList[0].data.index > 0)
101                 {
102                     ScrollViewItem item = itemList[itemList.Count - 1];
103                     item.SetData(itemDataList[itemList[0].data.index - 1], item.data.index);
104 
105                     itemList.Insert(0, item);
106                     itemList.RemoveAt(itemList.Count - 1);
107 
108                     float _height = NGUIMath.CalculateRelativeWidgetBounds(item.transform).size.y;
109                     item.transform.localPosition = itemList[1].transform.localPosition + new Vector3(0, _height + 4, 0);
110                 }
111 
112             }
113 
114             svLastPos = sv.transform.localPosition.y;
115         }
116     }
117 }

以上两种情况下 Prefab 的结构如下:

效果:

  

具体到项目中的实现代码(只记录了大致逻辑,具体细节无记录):

  1 using ClientData;
  2 using Modules.UI;
  3 using System;
  4 using System.Collections.Generic;
  5 using UnityEngine;
  6 
  7 /// <summary>
  8 /// Item 大小可变的动态循环列表实现(只支持竖向,暂且未实现对横向的支持)
  9 /// 该脚本需要挂在 UIScrollView  所在的 gameObject 上
 10 /// </summary>
 11 public class DynamicFlexibleSizeScrollView : MonoBehaviour
 12 {
 13     //定义的一些 delegate 为了,实现组件的通用化,把逻辑的实现下放到具体的实例中
 14     public delegate void OnInitItem(DFSItemCtrl item, DFSItemData data);
 15     public delegate void OnHideAllItem(List<DFSItemCtrl> itemList);
 16     public delegate DFSItemCtrl OnGetItemCtrl();
 17     public delegate void OnRecycleAllChatInfoCell(List<DFSItemCtrl> list);
 18 
 19 
 20     [SerializeField]
 21     Vector2 originalPanelClipOffset = new Vector2(0, 0);
 22     [SerializeField]
 23     Vector3 originalPanelPosition = new Vector3(0, 0, 0);
 24     [SerializeField]
 25     Vector3 originalTablePosition = new Vector3(0, 0, 0);
 26     [SerializeField]
 27     float maxHeight;        //最大y坐标
 28     [SerializeField]
 29     float minHeight;        //最小y坐标
 30     [SerializeField]
 31     int itemCount = 10;     //创建多少数量的 item 来做循环滚动
 32     [SerializeField]
 33     float offset = 6.0f;    //item 中间空出的距离
 34 
 35     float svLastPos = 0;        //记录scrollviet上一次的位置,用于判断scrollview的移动方向
 36     List<DFSItemCtrl> itemList = new List<DFSItemCtrl>();
 37     List<DFSItemData> itemDataList = new List<DFSItemData>();
 38     UIScrollView sv;
 39     UIPanel currUIPanel;
 40     UITable table;            //只是在初始化的时候会用到,在滑动的时候,不会再次操作该组件
 41 
 42     void InitNGUIComponent()
 43     {
 44         if (null == sv)
 45             sv = GetComponent<UIScrollView>();
 46 
 47         if (null != sv && null == currUIPanel)
 48             currUIPanel = sv.panel;
 49 
 50         if (null == table)
 51             table = GetComponentInChildren<UITable>();
 52     }
 53 
 54     public float MaxHeight
 55     {
 56         set { maxHeight = value; }
 57     }
 58 
 59     public float MinHeight
 60     {
 61         set { minHeight = value; }
 62     }
 63 
 64     public OnInitItem onInitItem;   //刷新 item
 65     public OnHideAllItem onHideAllItem;  //隐藏所有 item
 66 
 67     public OnGetItemCtrl getItemCtrl;
 68     public OnRecycleAllChatInfoCell recycleAllChatInfoCell;
 69 
 70 
 71     bool CheckContainData(DFSItemData data)
 72     {
 73         if (null != data)
 74         {
 75             for (int i = 0, iMax = itemDataList.Count - 1; i <= iMax; ++i)
 76             {
 77                 if (itemDataList[i].Index == data.Index)
 78                     return true;
 79             }
 80         }
 81 
 82         return false;
 83     }
 84 
 85     void AddData(DFSItemData data)
 86     {
 87         if (!CheckContainData(data))
 88         {
 89             itemDataList.Add(data);
 90         }
 91     }
 92 
 93     DFSItemCtrl tmpCtrl = null;
 94     public void ShowSingleItem(DFSItemData data)
 95     {
 96         if (null == data) return;
 97 
 98         InitNGUIComponent();
 99 
100         AddData(data);
101 
102         if (itemList.Count < itemCount)
103         {
104             AddItem(data);
105 
106             if (null != table)
107                 table.Reposition();
108 
109             if (null != sv)
110                 sv.ResetPosition();
111         }
112         else
113         {
114             AutoDragUIPanelByData(data);
115         }
116     }
117 
118     void AutoDragUIPanelByData(DFSItemData data)
119     {
120         if (null != data)
121         {
122             if (null != getItemCtrl)
123             {
124                 //从池中拿出一个 item 计算 panel 需要移动的位置
125                 if (null == tmpCtrl)
126                     tmpCtrl = getItemCtrl();
127 
128                 if (null != tmpCtrl && null != onInitItem)
129                 {
130                     onInitItem(tmpCtrl, data);
131                     float height = NGUIMath.CalculateRelativeWidgetBounds(tmpCtrl.transform).size.y;
132                     Vector2 vec2 = new Vector2(0, height + offset);
133                     SetPanelClipOff(vec2);
134                     tmpCtrl.cachedGameObject.SetActive(false);
135                 }
136             }
137         }
138     }
139 
140     void SetPanelClipOff(Vector2 vec2)
141     {
142         Vector2 tmpOff = currUIPanel.clipOffset;
143         tmpOff -= vec2;
144         currUIPanel.clipOffset = tmpOff;
145 
146         Vector3 tmpPos = currUIPanel.cachedTransform.localPosition;
147         tmpPos = new Vector3(tmpPos.x, -tmpOff.y, 0);
148         currUIPanel.cachedTransform.localPosition = tmpPos;
149     }
150 
151     public void ShowAllItems(List<DFSItemData> dataList)
152     {
153         if (null == dataList) return;
154 
155         svLastPos = 0;
156         InitNGUIComponent();
157 
158         if (null != sv)
159         {
160             currUIPanel.clipOffset = originalPanelClipOffset;
161             sv.transform.localPosition = originalPanelPosition;
162         }
163         if (null != table)
164             table.transform.localPosition = originalTablePosition;
165 
166         //数据保存
167         for (int i = 0, iMax = dataList.Count; i < iMax; ++i)
168             AddData(dataList[i]);
169 
170 
171         //如果数据数量大于了创建的item数量,自动滑动panel
172         if (itemDataList.Count > itemCount)
173         {
174             int i = itemDataList.Count - itemCount,
175                 iMax = itemDataList.Count;
176             for (; i < iMax; ++i)
177             {
178                 AddItem(itemDataList[i]);
179             }
180         }
181         else
182         {
183             for (int i = 0; i < itemCount; ++i)
184             {
185                 if (itemDataList.Count <= i) break;
186                 AddItem(itemDataList[i]);
187             }
188 
189         }
190 
191         ResetPanelStatus();
192     }
193 
194     void ResetPanelStatus()
195     {
196         if (null != table)
197             table.Reposition();
198         if (null != sv)
199             sv.ResetPosition();
200     }
201 
202     public void RecycleAllChatInfoCell()
203     {
204         if (null != recycleAllChatInfoCell)
205             recycleAllChatInfoCell(itemList);
206         itemDataList.Clear();
207     }
208 
209     void AddItem(DFSItemData data)
210     {
211         DFSItemCtrl item = null;
212         if (null != getItemCtrl)
213             item = getItemCtrl();
214 
215         if (null != data && null != item && null != onInitItem)
216         {
217             this.itemList.Add(item);
218             item.cachedTransform.parent = table.transform;
219             onInitItem(item, data);
220         }
221     }
222 
223     void Update()
224     {
225         if (null == onInitItem || null == itemDataList || itemList.Count <= 0) return;
226 
227         float moveDis = sv.transform.localPosition.y - svLastPos;
228 
229         if (Mathf.Abs(moveDis) > 0.05)
230         {
231             bool isup = moveDis > 0;
232             if (isup)
233             {
234                 float height = NGUIMath.CalculateRelativeWidgetBounds(itemList[0].transform).size.y;
235                 while (itemList[0].transform.localPosition.y + sv.transform.localPosition.y - height > maxHeight &&
236                        itemList[itemList.Count - 1].Data.Index < itemDataList.Count - 1)
237                 {
238                     DFSItemCtrl item = itemList[0];
239                     DFSItemData data = itemDataList[itemList[itemList.Count - 1].Data.Index + 1];
240                     onInitItem(item, data);
241 
242                     itemList.Add(item);
243                     itemList.RemoveAt(0);
244 
245                     float _height = NGUIMath.CalculateRelativeWidgetBounds(itemList[itemList.Count - 2].transform).size.y;
246                     item.transform.localPosition = itemList[itemList.Count - 2].transform.localPosition - new Vector3(0, _height + offset, 0);
247                 }
248             }
249             else
250             {
251                 while (itemList[itemList.Count - 1].transform.localPosition.y + sv.transform.localPosition.y < minHeight &&
252                     itemList[0].Data.Index > 0)
253                 {
254                     DFSItemCtrl item = itemList[itemList.Count - 1];
255                     DFSItemData data = itemDataList[itemList[0].Data.Index - 1];
256                     onInitItem(item, data);
257 
258                     itemList.Insert(0, item);
259                     itemList.RemoveAt(itemList.Count - 1);
260 
261                     float _height = NGUIMath.CalculateRelativeWidgetBounds(item.transform).size.y;
262                     item.transform.localPosition = itemList[1].transform.localPosition + new Vector3(0, _height + offset, 0);
263                 }
264             }
265 
266             svLastPos = sv.transform.localPosition.y;
267         }
268     }
269 }
270 
271 /// <summary>
272 /// 列表中显示的 Item  ,应用到项目的时候,实际的 Item 可以继承自该类
273 /// </summary>
274 public class DFSItemCtrl : MonoBehaviour
275 {
276     protected DFSItemData data;
277 
278     public DFSItemData Data
279     {
280         get { return data; }
281     }
282 
283     GameObject obj = null;
284     public GameObject cachedGameObject
285     {
286         get
287         {
288             if (null == obj)
289                 obj = gameObject;
290             return obj;
291         }
292     }
293 
294     Transform tran = null;
295     public Transform cachedTransform
296     {
297         get
298         {
299             if (null == tran)
300                 tran = transform;
301             return tran;
302         }
303     }
304 
305     public void ForceUpdateItemSize()
306     {
307 
308     }
309 
310     public virtual void SetData(DFSItemData data,params UnityEngine.Object[] list)
311     {
312         if (null != data)
313         {
314             this.data = data;
315             ChatInfo info = data as ChatInfo;
316             this.cachedGameObject.name = info.Index.ToString();
317         }
318 
319         //强制刷新一下 Item 的大小
320         ForceUpdateItemSize();
321     }
322 }
323 
324 /// <summary>
325 /// ItemData ,应用到项目的时候,实际的数据可以继承自该类
326 /// </summary>
327 public class DFSItemData
328 {
329     int index = -1;
330 
331     public virtual int Index
332     {
333         get { return index; }
334         set { index = value; }
335     }
336 }

效果:

原文地址:https://www.cnblogs.com/luguoshuai/p/10214280.html