lyh916

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

需要注意的有下面几点:

1.

区分好表现上的index和逻辑上的index。表现上的index是指这个go是go列表中的第几项,但实际上这个index的意义并不大,因为在滚动的过程中go列表是轮转的;逻辑上的index是指这个go对应数据中的第几项,在滑动的过程中不断地更新逻辑上的index,然后取对应的数据去刷新显示即可。在一般的滑动列表中,有几项数据就生成几个go,因此表现上的index和逻辑上的index是一致的;而在循环利用的循环列表中,这两个是不一致的。

那么,在实现上,就是需要知道每个go对应的逻辑index是多少了。而这个可以简化为,只需要知道第一个对应的逻辑index是多少,因为后面的就是依次递增的。

2.

做好缓存策略。对于循环利用的列表,需要生成的个数等于能显示的最大个数加上2-3个的缓存个数,防止滑动过快时出现穿帮。

3.

关于循环利用的实现。其实就是在滑动过程中,收集被移除显示的go,然后对这些go重新调整位置,并刷新go上的控件显示。那么如何收集呢,就是将go对应的逻辑index和当前的逻辑index范围进行比较,将不在这个范围内的go收集即可。然后在需要的时候取出来,刷新这些go即可。

 

代码如下:

  1 using UnityEngine;
  2 using System.Collections.Generic;
  3 using System;
  4 using UnityEngine.UI;
  5 
  6 [RequireComponent(typeof(ScrollRect))]
  7 public class LoopScrollView : MonoBehaviour {
  8 
  9     private List<GameObject> goList;//当前显示的go列表
 10     private Queue<GameObject> freeGoQueue;//空闲的go队列,存放未显示的go
 11     private Dictionary<GameObject, int> goIndexDic;//key:所有的go value:真实索引
 12     private ScrollRect scrollRect;
 13     private RectTransform contentRectTra;
 14     private Vector2 scrollRectSize;
 15     private Vector2 cellSize;
 16     private int startIndex;//起始索引
 17     private int maxCount;//创建的最大数量
 18     private int createCount;//当前显示的数量
 19 
 20     private const int cacheCount = 2;//缓存数目
 21     private const int invalidStartIndex = -1;//非法的起始索引
 22 
 23     private int dataCount;
 24     private GameObject prefabGo;
 25     private Action<GameObject, int> updateCellCB;
 26     private float cellPadding;
 27 
 28     //初始化SV并刷新
 29     public void Show(int dataCount, GameObject prefabGo, Action<GameObject, int> updateCellCB, float cellPadding = 0f)
 30     {
 31         //数据和组件初始化
 32         this.dataCount = dataCount;
 33         this.prefabGo = prefabGo;
 34         this.updateCellCB = updateCellCB;
 35         this.cellPadding = cellPadding;
 36 
 37         goList = new List<GameObject>();
 38         freeGoQueue = new Queue<GameObject>();
 39         goIndexDic = new Dictionary<GameObject, int>();
 40         scrollRect = GetComponent<ScrollRect>();
 41         contentRectTra = scrollRect.content;
 42         scrollRectSize = scrollRect.GetComponent<RectTransform>().sizeDelta;
 43         cellSize = prefabGo.GetComponent<RectTransform>().sizeDelta;
 44         startIndex = 0;
 45         maxCount = GetMaxCount();
 46         createCount = 0;
 47 
 48         if (scrollRect.horizontal)
 49         {
 50             contentRectTra.anchorMin = new Vector2(0, 0);
 51             contentRectTra.anchorMax = new Vector2(0, 1);
 52         }
 53         else
 54         {
 55             contentRectTra.anchorMin = new Vector2(0, 1);
 56             contentRectTra.anchorMax = new Vector2(1, 1);
 57         }
 58         scrollRect.onValueChanged.RemoveAllListeners();
 59         scrollRect.onValueChanged.AddListener(OnValueChanged);
 60         ResetSize(dataCount);
 61     }
 62 
 63     //重置数量
 64     public void ResetSize(int dataCount)
 65     {
 66         this.dataCount = dataCount;
 67         contentRectTra.sizeDelta = GetContentSize();
 68 
 69         //回收显示的go
 70         for (int i = goList.Count - 1; i >= 0; i--)
 71         {
 72             GameObject go = goList[i];
 73             RecoverItem(go);
 74         }
 75       
 76         //创建或显示需要的go
 77         createCount = Mathf.Min(dataCount, maxCount);
 78         for (int i = 0; i < createCount; i++)
 79         {
 80             CreateItem(i);
 81         }
 82 
 83         //刷新数据
 84         startIndex = -1;
 85         contentRectTra.anchoredPosition = Vector3.zero;
 86         OnValueChanged(Vector2.zero);
 87     }
 88 
 89     //更新当前显示的列表
 90     public void UpdateList()
 91     {
 92         for (int i = 0; i < goList.Count; i++)
 93         {
 94             GameObject go = goList[i];
 95             int index = goIndexDic[go];
 96             updateCellCB(go, index);
 97         }
 98     }
 99 
100     //创建或显示一个item
101     private void CreateItem(int index)
102     {
103         GameObject go;
104         if (freeGoQueue.Count > 0)//使用原来的
105         {
106             go = freeGoQueue.Dequeue();
107             goIndexDic[go] = index;
108             go.SetActive(true);
109         }
110         else//创建新的
111         {
112             go = Instantiate<GameObject>(prefabGo);
113             goIndexDic.Add(go, index);
114             go.transform.SetParent(contentRectTra.transform);
115 
116             RectTransform rect = go.GetComponent<RectTransform>();
117             rect.pivot = new Vector2(0, 1);
118             rect.anchorMin = new Vector2(0, 1);
119             rect.anchorMax = new Vector2(0, 1);
120         }
121         goList.Add(go);
122         go.transform.localPosition = GetPosition(index);
123         updateCellCB(go, index);
124     }
125 
126     //回收一个item
127     private void RecoverItem(GameObject go)
128     {
129         go.SetActive(false);
130         goList.Remove(go);
131         freeGoQueue.Enqueue(go);
132         goIndexDic[go] = invalidStartIndex;
133     }
134 
135     //滑动回调
136     private void OnValueChanged(Vector2 vec)
137     {
138         int curStartIndex = GetStartIndex();
139         //Debug.LogWarning(curStartIndex);
140 
141         if ((startIndex != curStartIndex) && (curStartIndex > invalidStartIndex))
142         {
143             startIndex = curStartIndex;
144 
145             //收集被移出去的go
146             //索引的范围:[startIndex, startIndex + createCount - 1]
147             for (int i = goList.Count - 1; i >= 0; i--)
148             {
149                 GameObject go = goList[i];
150                 int index = goIndexDic[go];
151                 if (index < startIndex || index > (startIndex + createCount - 1))
152                 {
153                     RecoverItem(go);
154                 }
155             }
156 
157             //对移除出的go进行重新排列
158             for (int i = startIndex; i < startIndex + createCount; i++)
159             {
160                 if (i >= dataCount)
161                 {
162                     break;
163                 }
164 
165                 bool isExist = false;
166                 for (int j = 0; j < goList.Count; j++)
167                 {
168                     GameObject go = goList[j];
169                     int index = goIndexDic[go];
170                     if (index == i)
171                     {
172                         isExist = true;
173                         break;
174                     }
175                 }
176                 if (isExist)
177                 {
178                     continue;
179                 }
180 
181                 CreateItem(i);
182             }
183         }
184     }
185 
186     //获取需要创建的最大prefab数目
187     private int GetMaxCount()
188     {
189         if (scrollRect.horizontal)
190         {
191             return Mathf.CeilToInt(scrollRectSize.x / (cellSize.x + cellPadding)) + cacheCount;
192         }
193         else
194         {
195             return Mathf.CeilToInt(scrollRectSize.y / (cellSize.y + cellPadding)) + cacheCount;
196         }
197     }
198 
199     //获取起始索引
200     private int GetStartIndex()
201     {
202         if (scrollRect.horizontal)
203         {
204             return Mathf.FloorToInt(-contentRectTra.anchoredPosition.x / (cellSize.x + cellPadding));
205         }
206         else
207         {
208             return Mathf.FloorToInt(contentRectTra.anchoredPosition.y / (cellSize.y + cellPadding));
209         }
210     }
211 
212     //获取索引所在位置
213     private Vector3 GetPosition(int index)
214     {
215         if (scrollRect.horizontal)
216         {
217             return new Vector3(index * (cellSize.x + cellPadding), 0, 0);
218         }
219         else
220         {
221             return new Vector3(0, index * -(cellSize.y + cellPadding), 0);
222         }
223     }
224 
225     //获取内容长宽
226     private Vector2 GetContentSize()
227     {
228         if (scrollRect.horizontal)
229         {
230             return new Vector2(cellSize.x * dataCount + cellPadding * (dataCount - 1), contentRectTra.sizeDelta.y);
231         }
232         else
233         {
234             return new Vector2(contentRectTra.sizeDelta.x, cellSize.y * dataCount + cellPadding * (dataCount - 1));
235         }
236     }
237 }

 

 1 using UnityEngine;
 2 using System.Collections;
 3 using UnityEngine.UI;
 4 
 5 public class TestLoopScrollView : MonoBehaviour {
 6 
 7     public LoopScrollView loopScrollView;
 8     public LoopScrollView loopScrollView2;
 9     public GameObject prefabGo;
10     public GameObject prefabGo2;
11 
12     private void Start ()
13     {
14         
15     }
16 
17     private void Update()
18     {
19         if (Input.GetKeyDown(KeyCode.Q))
20         {
21             loopScrollView.Show(100, prefabGo, UpdateSV);
22         }
23         else if(Input.GetKeyDown(KeyCode.W))
24         {
25             loopScrollView.ResetSize(5);
26         }
27         else if (Input.GetKeyDown(KeyCode.E))
28         {
29             loopScrollView.ResetSize(50);
30         }
31         else if (Input.GetKeyDown(KeyCode.R))
32         {
33             loopScrollView.UpdateList();
34         }
35 
36         if (Input.GetKeyDown(KeyCode.A))
37         {
38             loopScrollView2.Show(100, prefabGo2, UpdateSV);
39         }
40         else if (Input.GetKeyDown(KeyCode.S))
41         {
42             loopScrollView2.ResetSize(5);
43         }
44         else if (Input.GetKeyDown(KeyCode.D))
45         {
46             loopScrollView2.ResetSize(50);
47         }
48         else if (Input.GetKeyDown(KeyCode.F))
49         {
50             loopScrollView.UpdateList();
51         }
52     }
53 
54     private void UpdateSV(GameObject go, int index)
55     {
56         Text text = go.transform.Find("Text").GetComponent<Text>();
57         text.text = index.ToString();
58     }
59 }

 

效果:

posted on 2018-04-05 11:39  艰苦奋斗中  阅读(3340)  评论(0编辑  收藏  举报