抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

简介

UGUI 实现滚动列表,支持非固定大小的 Item 以及多种不同的数据,可自定义单元格偏移量、单元格间隔、单元格重复数量限制,可以根据单元格重复数量自动调整滚动方向。

演示

单类型单元格

复数类型单元格

使用方法

UI

在 Canvas 上挂载 Scroll View 预制体,选中 Content 对象,挂载管理脚本。预制体中默认挂载了一个 TestScrollView 脚本。

  • PrefabItem 列表用于挂载不同的 Item 预制体,需要多少种就挂载多少个。
  • Stack 用于获取对象池节点,即上图的 Stack 节点。
  • Scroll 用于获取 Scroll View 节点。
  • FitSize 开启后列表将渲染所有单元格,并且面板大小自动调整为适合的大小,并禁止滚动。
  • SpaceX,单元格 X 方向间隔。
  • SpaceY,单元格 Y 方向间隔。
  • OffsetX,列表 X 方向偏移。
  • OffsetY,列表 Y 方向偏移。
  • RepeatX,列表 X 方向单元格最大数量。0 为无限制。
  • RepeatY,列表 Y 方向单元格最大数量。0 为无限制。当 RepeatX 设置后,该项失效(可能会出 bug,不建议同时设置)。

代码

Content 对象挂载的脚本需要继承 ScrollViewScript。如 TestScrollView:

TestScrollView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System.Collections.Generic;
using TMPro;

public class TestScrollView : ScrollViewScript<int>
{
private List<int> _subData = new List<int>();

void Awake()
{
for (int i = 0; i < 10; i++)
{
_subData.Add(i);
}

actions.Add(UpdateItem);
actions.Add(UpdateItem);
}

protected override void InitItemType()
{
List<int[]> type = new List<int[]>();

int i = 0;
int j = 0;
while (i < data.Count || j < _subData.Count)
{
if (i < data.Count)
{
type.Add(new int[] { 0, i++ });
}
if (j < _subData.Count)
{
type.Add(new int[] { 1, j++ });
}
}

itemType = type;
}

private void UpdateItem(ItemTransformData cell, int index)
{
cell.item.transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = "单元格 " + data[index];
}
}

泛型代表了列表默认数据 data 的数据类型。对 data 赋值即可刷新列表。

列表单元格渲染函数需要在 Start 函数之前定义并存入渲染函数数组actions中。本例中在 Awake 进行初始化。有多少 Item 类型就要初始化多少渲染函数,并且顺序要一一对应。不过如果 Item 数量大于渲染函数数量,程序也不会出问题,如果是中间 Item 类型的渲染函数不存在,那就会出现渲染函数和 Item 不匹配。因此最好是一一对应。

如果需要额外的数据,那就需要重写InitItemType方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// 初始化单元格类型
/// </summary>
/// <param name="itemType">int[2] 0为button类型 1为对应类型的数据索引</param>
protected virtual void InitItemType()
{
List<int[]> type = new List<int[]>();
for (int i = 0, len = _data.Count; i < len; i++)
{
type.Add(new int[] { 0, i });
}

itemType = type;
}

该方法负责管理虚拟列表中 Item 对应索引的 Item 类型和其在对应数据列表中的索引。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected override void InitItemType()
{
List<int[]> type = new List<int[]>();

int i = 0;
int j = 0;
while (i < data.Count || j < _subData.Count)
{
if (i < data.Count)
{
type.Add(new int[] { 0, i++ });
}
if (j < _subData.Count)
{
type.Add(new int[] { 1, j++ });
}
}

itemType = type;
}

_subData为任务滚动列表类自定义的属性,本例根据一个 Item0 和一个 Item1 交替填充到itemType中,即管理虚拟列表中 Item 对应索引的 Item 类型和其在对应数据列表中的索引的列表中。

注意,如果列表不是单列或单行,建议不要使用多种大小不同的 Item 混搭,本项目尚未对该情况进行适配,很有可能会出现 Item 覆盖的情况。

原理

概述

Item 固定大小的循环列表会在滚动面板视野外额外生成一个 Item,在上下滚动的时候通过改变额外 Item 的坐标并且重新渲染该 Item 来实现列表的滚动效果。

本项目因为需要支持非固定大小的 Item,因此使用对象池的方式来模拟。通过判断上下滚动时的头尾节点相较于滚动面板的位置来判断是要回收 Item 还是创建 Item。

单元格根据类型调用对应的渲染函数,在 data 数据发生变化的时候会刷新列表;在单元格大小发生变化时会刷新单元格坐标。

单元格数据类

所有单元格都遵循该类的设置。滚动列表单元格之间按照逻辑顺序使用 next 和 parent 形成双向链表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using UnityEngine;
using UnityEngine.UI;

public class ItemTransformData
{
/// <summary>
/// 坐标
/// </summary>
public Vector2 pos
{
get
{
return _pos;
}
set
{
_pos = value;
item.transform.localPosition = pos;
}
}
/// <summary>
/// 高度
/// </summary>
public float height
{
get
{
if (_item_rect)
{
return _item_rect.rect.height;
}
else
{
return 0f;
}
}
}
/// <summary>
/// 宽度
/// </summary>
public float width
{
get
{
if (_item_rect)
{
return _item_rect.rect.width;
}
else
{
return 0f;
}
}
}
/// <summary>
/// 宽高
/// </summary>
public Vector2 size
{
set
{
_item_rect.sizeDelta = value;
}
}
/// <summary>
/// 虚拟列表索引
/// </summary>
public int cell_index { get; set; }
/// <summary>
/// 数据列表索引
/// </summary>
public int item_index { get; set; }
/// <summary>
/// 单元格对象
/// </summary>
public Button item
{
get
{
return _item;
}
set
{
_item = value;
_item_rect = _item.GetComponent<RectTransform>();
}
}
/// <summary>
/// 单元格类型
/// </summary>
public int item_type { get; set; }
/// <summary>
/// 下一单元格
/// </summary>
public ItemTransformData next { get; set; }
/// <summary>
/// 上一单元格
/// </summary>
public ItemTransformData parent { get; set; }

private Button _item;

private RectTransform _item_rect;

private Vector2 _pos;

public void CloneTo(ItemTransformData itd)
{
itd.pos = pos;
itd.cell_index = cell_index;
itd.item_index = item_index;
itd.item = item;
itd.item_type = item_type;
}

public ItemTransformData Clone()
{
ItemTransformData itd = new ItemTransformData();
CloneTo(itd);
return itd;
}
}

初始化

  1. 生成单元格对象池

    1. 首先初始化列表的视口大小。
    2. 然后遍历 Item 预制体类型,根据视口大小循环创建 Item 对象并存入对象池(也可以在运行时创建,根据需求即可)。
    3. 根据面板配置设置对应的控制条件。
    4. 监听滚动。
  2. 根据数据生成单元格

    1. 回收所有显示的单元格。
    2. 初始化虚拟列表
    3. while 循环生成单元格,非 FitSize 情况下当单元格超出视界后不生成,只获取并计算位置和大小信息。

列表滚动刷新

前提条件:Y 轴向下为负值,向下滚动为滚动面板 Y 值增加,X 方向相反

滚动列表刷新按照滚动方向分为两类:

  1. 向下\右滚动

    • 对于头单元格,当其 Y 方向最低点加上滚动距离大于 0 时,或者其 X 方向最右点加上滚动距离小于 0 时,回收头单元格,然后置头单元格为下一单元格(next)。

      Y:_head.pos.y - _head.height + rect > 0

      X:_head.pos.x + _head.width + rect < 0

    • 对于尾单元格,当其 Y 方向最高点加上滚动距离大于负的滚动面板高度时,或者其 X 方向最左点加上滚动距离小于滚动面板宽度时,生成新单元格,并置尾单元格为该单元格。

      Y:_tail.pos.y + rect > -_viewHeight

      X:_tail.pos.x + rect < _viewWidth

  2. 向上\左滚动

    • 对于头单元格,当其 Y 方向最低点加上滚动距离小于 0 时,或者其 X 方向最右点加上滚动距离大于 0 时,生成新单元格,并置头单元格为该单元格。

      Y:_head.pos.y - _head.height + rect < 0

      X:_head.pos.x + _head.width + rect > 0

    • 对于尾单元格,当其 Y 方向最高点加上滚动距离小于负的滚动面板高度时,或者其 X 方向最左点加上滚动距离大于滚动面板宽度时,回收尾单元格,然后置尾单元格为上一单元格(parent)。

      Y:_tail.pos.y + rect < -_viewHeight

      X:_tail.pos.x + rect > _viewWidth

改变单元格大小

调用SetCellSize方法设置单元格大小,并且默认刷新单元格位置。

代码

ItemTransformData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using UnityEngine;
using UnityEngine.UI;

public class ItemTransformData
{
/// <summary>
/// 坐标
/// </summary>
public Vector2 pos
{
get
{
return _pos;
}
set
{
_pos = value;
item.transform.localPosition = pos;
}
}
/// <summary>
/// 高度
/// </summary>
public float height
{
get
{
if (_item_rect)
{
return _item_rect.rect.height;
}
else
{
return 0f;
}
}
}
/// <summary>
/// 宽度
/// </summary>
public float width
{
get
{
if (_item_rect)
{
return _item_rect.rect.width;
}
else
{
return 0f;
}
}
}
/// <summary>
/// 宽高
/// </summary>
public Vector2 size
{
set
{
_item_rect.sizeDelta = value;
}
}
/// <summary>
/// 虚拟列表索引
/// </summary>
public int cell_index { get; set; }
/// <summary>
/// 数据列表索引
/// </summary>
public int item_index { get; set; }
/// <summary>
/// 单元格对象
/// </summary>
public Button item
{
get
{
return _item;
}
set
{
_item = value;
_item_rect = _item.GetComponent<RectTransform>();
}
}
/// <summary>
/// 单元格类型
/// </summary>
public int item_type { get; set; }
/// <summary>
/// 下一单元格
/// </summary>
public ItemTransformData next { get; set; }
/// <summary>
/// 上一单元格
/// </summary>
public ItemTransformData parent { get; set; }

private Button _item;

private RectTransform _item_rect;

private Vector2 _pos;

public void CloneTo(ItemTransformData itd)
{
itd.pos = pos;
itd.cell_index = cell_index;
itd.item_index = item_index;
itd.item = item;
itd.item_type = item_type;
}

public ItemTransformData Clone()
{
ItemTransformData itd = new ItemTransformData();
CloneTo(itd);
return itd;
}
}

ScrollViewScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ScrollViewScript<T> : MonoBehaviour
{
/// <summary>
/// 单元格预制体
/// </summary>
public List<Button> PrefabItem;
/// <summary>
/// 单元格回收节点
/// </summary>
public GameObject Stack;
/// <summary>
/// 滚动节点
/// </summary>
public ScrollRect Scroll;
/// <summary>
/// 自适应大小
/// </summary>
public bool FitSize;
/// <summary>
/// X方向单元格间隔
/// </summary>
public float SpaceX;
/// <summary>
/// Y方向单元格间隔
/// </summary>
public float SpaceY;
/// <summary>
/// X方向偏移
/// </summary>
public float OffsetX;
/// <summary>
/// Y方向偏移
/// </summary>
public float OffsetY;
/// <summary>
/// X方向重复单元格个数
/// </summary>
public int RepeatX;
/// <summary>
/// Y方向重复单元格个数
/// </summary>
public int RepeatY;
/// <summary>
/// 默认列表数据
/// </summary>
public List<T> data
{
get
{
return _data;
}
set
{
RefreshList(value);
}
}
/// <summary>
/// 列表单元格类型
/// </summary>
protected List<int[]> itemType
{
get
{
return _itemType;
}
set
{
_itemType = value;
}
}

protected List<Action<ItemTransformData, int>> actions
{
get
{
return _actions;
}
}
private bool _inited = false;
private List<int[]> _itemType = new List<int[]>();
/// <summary>
/// 默认列表数据
/// </summary>
private List<T> _data;
/// <summary>
/// 单元格虚拟数量最大值
/// </summary>
private int _length;
/// <summary>
/// 显示的单元格列表
/// </summary>
private List<ItemTransformData> _items = new List<ItemTransformData>();
/// <summary>
/// 回收的单元格列表
/// </summary>
private List<Stack<ItemTransformData>> _itemStack = new List<Stack<ItemTransformData>>();
/// <summary>
/// content面板transform
/// </summary>
private RectTransform _rectTransform;
/// <summary>
/// content视图高度
/// </summary>
private float _viewHeight;
/// <summary>
/// content视图宽度
/// </summary>
private float _viewWidth;

//private int _repeatX;

private ItemTransformData _head;

private ItemTransformData _tail;

private Vector2 _lastScrollPos = new Vector2();

private List<Vector2> _itemPos = new List<Vector2>();

private Dictionary<int, Dictionary<int, Vector2>> _itemSize = new Dictionary<int, Dictionary<int, Vector2>>();

private int _scrollDirection = 0;

private List<Action<ItemTransformData, int>> _actions = new List<Action<ItemTransformData, int>>();

public void Start()
{
_rectTransform = GetComponent<RectTransform>();

_viewWidth = transform.parent.GetComponent<RectTransform>().rect.width;
_viewHeight = transform.parent.GetComponent<RectTransform>().rect.height;

for (int i = 0, len = PrefabItem.Count; i < len; i++)
{
//创建回收栈节点对应类型的回收节点
GameObject obj = new GameObject();
obj.name = "ItemType_" + i;
obj.transform.SetParent(Stack.transform);

Button item = Instantiate(PrefabItem[i]);
item.transform.SetParent(obj.transform);
RectTransform itemRect = item.GetComponent<RectTransform>();

ItemTransformData itd = new ItemTransformData();
itd.item = item;
itd.pos = new Vector2();
itd.item_type = i;

if (_itemStack.Count <= i)
{
_itemStack.Add(new Stack<ItemTransformData>());
}
_itemStack[i].Push(itd);

for (int j = 0, len2 = (int)Mathf.Ceil(_viewHeight / itd.height) * (int)Mathf.Ceil(_viewWidth / itd.width); j < len2; j++)
{
Button item2 = Instantiate(PrefabItem[i]);
item2.transform.SetParent(obj.transform);
RectTransform itemRect2 = item.GetComponent<RectTransform>();

ItemTransformData itd2 = new ItemTransformData();
itd2.item = item2;
itd2.pos = new Vector2();
itd2.item_type = i;

_itemStack[i].Push(itd2);
}
}

if (FitSize)
{
Scroll.vertical = false;
Scroll.horizontal = false;
}
else
{
if (RepeatY != 0)
{
Scroll.vertical = false;
}
else
{
Scroll.horizontal = false;
}
}

Scroll.onValueChanged.AddListener(OnScroll);
_inited = true;
if (data != null && data.Count > 0)
{
data = data;
}
}

private void OnScroll(Vector2 v)
{
////Debug.Log(v);
if (_head == null || _tail == null) return;
float rect;
float diff;
if (RepeatY != 0)
{
rect = _rectTransform.localPosition.x;
diff = _lastScrollPos.x - rect;
}
else
{
rect = _rectTransform.localPosition.y;
diff = rect - _lastScrollPos.y;
}

if (diff > 0)
{
_scrollDirection = 1;
}
else if (diff < 0)
{
_scrollDirection = -1;
}
else
{
_scrollDirection = 0;
}

if (_scrollDirection == 1)
{
bool checkRecycleHead;
if (RepeatY != 0)
{
checkRecycleHead = _head.pos.x + _head.width + rect < 0;
}
else
{
checkRecycleHead = _head.pos.y - _head.height + rect > 0;
}

if (checkRecycleHead)
{
if (_head.next != null)
{
//Debug.Log("回收头" + _head.cell_index + " == " + _head.next.cell_index);
RecycleCell(_head.item_type, _head);
_head = _head.next;
}
}

bool checkRecycleTail;
if (RepeatY != 0)
{
checkRecycleTail = _tail.pos.x + rect < _viewWidth;
}
else
{
checkRecycleTail = _tail.pos.y + rect > -_viewHeight;
}
//Debug.Log(_tail.pos.y + rectY + " : " + _viewHeight);
if (checkRecycleTail)
{
int index = _tail.cell_index + 1;
if (index < _itemType.Count)
{
int[] itemType = _itemType[index];
ItemTransformData item = CreateItem(itemType[0]);
Vector2 pos = _itemPos[index];
item.cell_index = index;
item.item_index = itemType[1];
item.parent = _tail;
_tail.next = item;
item.pos = pos;
item.item.transform.localPosition = pos;
item.item.name = index + "";
_tail = item;
//Debug.Log("创建尾" + _tail.cell_index + " " + pos + " " + index);
if (itemType[0] < _actions.Count)
{
_actions[itemType[0]](item, item.item_index);
}
}
}
}
else if (_scrollDirection == -1)
{
bool checkRecycleHead;
if (RepeatY != 0)
{
checkRecycleHead = _head.pos.x + _head.width + rect > 0;
}
else
{
checkRecycleHead = _head.pos.y - _head.height + rect < 0;
}
//Debug.Log(_head.pos.y - _head.height + rectY);
if (checkRecycleHead)
{
int index = _head.cell_index - 1;
if (index >= 0)
{
int[] itemType = _itemType[index];
ItemTransformData item = CreateItem(itemType[0]);
Vector2 pos = _itemPos[index];
item.cell_index = index;
item.item_index = itemType[1];
item.next = _head;
_head.parent = item;
item.pos = pos;
item.item.transform.localPosition = pos;
item.item.name = index + "";
_head = item;
//Debug.Log("创建头" + _head.cell_index + " " + pos + " " + index);
if (itemType[0] < _actions.Count)
{
_actions[itemType[0]](item, item.item_index);
}
}
}

bool checkRecycleTail;
if (RepeatY != 0)
{
checkRecycleTail = _tail.pos.x + rect > _viewWidth;
}
else
{
checkRecycleTail = _tail.pos.y + rect < -_viewHeight;
}
//Debug.Log(_tail.pos.y + rectY + " : " + -_viewHeight);
if (checkRecycleTail)
{
if (_tail.parent != null)
{
//Debug.Log("回收尾" + _tail.cell_index);
RecycleCell(_tail.item_type, _tail);
_tail = _tail.parent;
}
}
}

_lastScrollPos.x = _rectTransform.localPosition.x;
_lastScrollPos.y = _rectTransform.localPosition.y;
}

/// <summary>
/// 创建ItemTransformData
/// </summary>
/// <param name="itemType"></param>
/// <returns></returns>
private ItemTransformData CreateItem(int itemType, bool init = true)
{
ItemTransformData item;
//Debug.Log(itemType + " " + _itemStack.Count);
if (_itemStack[itemType].Count > 0)
{
item = _itemStack[itemType].Pop();
}
else
{
Button button = Instantiate(PrefabItem[itemType]);
item = new ItemTransformData();
item.item = button;
}

if (init)
{
item.item.transform.SetParent(transform);
_items.Add(item);
}
else
{
_itemStack[itemType].Push(item);
}

return item;
}

/// <summary>
/// 回收所有单元格
/// </summary>
private void RecycleAllCells()
{
for (int i = 0, len = _items.Count; i < len; i++)
{
ItemTransformData item = _items[i];
item.item.transform.SetParent(Stack.transform.GetChild(item.item_type));
_itemStack[item.item_type].Push(item);
}
_items.Clear();
_itemPos.Clear();
}

/// <summary>
/// 回收一个单元格
/// </summary>
/// <param name="itemType"></param>
/// <param name="itd"></param>
private void RecycleCell(int itemType, ItemTransformData itd)
{
itd.item.transform.SetParent(Stack.transform.GetChild(itemType));
//itd.next = null;
//itd.parent = null;
_items.Remove(itd);
_itemStack[itemType].Push(itd);
}

private Vector2 CalculatePosition(ItemTransformData last, ItemTransformData current)
{

float bonusX = 0;
float bonusY = 0;
if (RepeatX != 0)
{
if (current.cell_index == 0)
{
bonusX = OffsetX;
bonusY = OffsetY;
}
else if (current.cell_index % RepeatX == 0)
{
//换行
bonusX = -last.pos.x + OffsetX;
bonusY = last.height + SpaceY;
}
else
{
bonusX = last.width + SpaceX;
}
}
else if (RepeatY != 0)
{
if (current.cell_index == 0)
{
bonusY = OffsetY;
bonusX = OffsetX;
}
else if (current.cell_index % RepeatY == 0)
{
//换行
bonusX = last.width + SpaceX;
bonusY = last.pos.y + OffsetY;
}
else
{
bonusY = last.height + SpaceY;
}
Debug.Log(bonusY);
}
else
{
if (current.cell_index == 0)
{
bonusX = OffsetX;
bonusY = OffsetY;
}
else if (last.pos.x + last.width + SpaceX + current.width > _viewWidth)
{
//换行
bonusX = -last.pos.x + OffsetX;
bonusY = last.height + SpaceY;
}
else
{
bonusX = last.width + SpaceX;
}
}

//_tempVec2.x = last.pos.x + bonusX;
//_tempVec2.y = last.pos.y - bonusY;
Vector2 pos = new Vector2(last.pos.x + bonusX, last.pos.y - bonusY);
return pos;
}

protected void RefreshList(List<T> data)
{
//Debug.Log("设置数据");
_data = data;

if (_inited)
{
RecycleAllCells();
InitItemType();
ResetCells();
}
}

private void ResetCells()
{
if (FitSize)
{
_viewWidth = transform.parent.GetComponent<RectTransform>().rect.width;
_viewHeight = transform.parent.GetComponent<RectTransform>().rect.height;
}

ItemTransformData last = new ItemTransformData();
int i = 0;
float maxY = 0;
float maxX = 0;
float maxHeight = 0;
float maxWidth = 0;
while (i < itemType.Count)
{
int type = itemType[i][0];

bool isInit = true;
if (!FitSize)
{
if (RepeatY != 0)
{
isInit = maxX < _viewHeight;
}
else
{
isInit = maxY > -_viewHeight;
}
}
ItemTransformData itd = CreateItem(type, isInit);
itd.cell_index = i;
itd.item_index = itemType[i][1];
itd.item.name = i + "";

if (isInit)
{
if (type < _actions.Count)
{
_actions[type](itd, itd.item_index);
}
}

Vector2 pos = CalculatePosition(last, itd);
_itemPos.Add(pos);
//第一次设置数据的时候初始化单元格大小 之后设置单元格大小
Dictionary<int, Vector2> sizeDic;
_itemSize.TryGetValue(type, out sizeDic);
if (sizeDic == null)
{
sizeDic = new Dictionary<int, Vector2>();
_itemSize.Add(type, sizeDic);
}
if (!sizeDic.ContainsKey(itd.item_index))
{
sizeDic.Add(itd.item_index, new Vector2(itd.width, itd.height));
}
else
{
itd.size = sizeDic[itd.item_index];
}

itd.pos = pos;
last.next = itd;
if (i != 0)
{
itd.parent = last;
}
last = itd;

maxY = pos.y;
maxX = pos.x;
float maxH = pos.y - itd.height;
float maxW = pos.x + itd.width;
if (maxH < maxHeight)
{
maxHeight = maxH;
}
if (maxW > maxWidth)
{
maxWidth = maxW;
}

i++;
//Debug.Log("设置item " + (i - 1) + " " + maxHeight + " " + pos.y + " " + itd.height);
}

if (_items.Count > 0)
{
_head = _items[0];
_tail = _items[_items.Count - 1];
}
else
{
_head = null;
_tail = null;
}


//更新UI大小
_rectTransform.sizeDelta = new Vector2(maxWidth, -maxHeight);

if (FitSize)
{
float contentHeight = _rectTransform.rect.height;
_viewHeight = contentHeight;
float contentWidth = _rectTransform.rect.width;
_viewWidth = contentWidth;
//if (contentHeight < _viewHeight)
//{
// _viewHeight = contentHeight;
//}

RectTransform scrollRect = Scroll.GetComponent<RectTransform>();
scrollRect.sizeDelta = new Vector2(maxWidth, -maxHeight);
}
}

private void Resize()
{
RecycleAllCells();
ResetCells();
}

/// <summary>
/// 初始化单元格类型
/// </summary>
/// <param name="itemType">int[2] 0为button类型 1为对应类型的数据索引</param>
protected virtual void InitItemType()
{
List<int[]> type = new List<int[]>();
for (int i = 0, len = _data.Count; i < len; i++)
{
type.Add(new int[] { 0, i });
}

itemType = type;
}

/// <summary>
/// 设置单元格大小
/// </summary>
/// <param name="itd"></param>
/// <param name="size"></param>
protected void SetCellSize(ItemTransformData itd, Vector2 size, bool resize = true)
{
_itemSize[itd.item_type][itd.item_index] = size;
if (resize)
{
Resize();
}
}
}

评论