测试:
数据 & 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 }
效果: