在使用UGUI的 DropDown 时, Canvas 的 Render Mode 选择了 Screen Space--Camera, 此时遇到一个小bug, 当我把这个下拉组件放到屏幕中间附近时, 下拉列表显示是正常的。当我把组件整体移到边缘,突然出现下拉列表的 Content 的坐标 不合法,由于 ugui 的点击关闭处理是在 Canvas 的子节点最下方又生成一个 全屏的 遮罩 来保证实现 “点击关闭”,所以此时整个界面卡死……翻遍源码断点我也没找到问题。
![UGUI DropDown遇到的问题总结](https://i-blog.csdnimg.cn/blog_migrate/03d05a36ab8b6d47f8e0608e5c2a9a79.png)
(中间:边缘)
![UGUI DropDown遇到的问题总结](https://i-blog.csdnimg.cn/blog_migrate/b8fdda08bea905b414b31a7e32ac7052.png)
![UGUI DropDown遇到的问题总结](https://i-blog.csdnimg.cn/blog_migrate/ded224f34bf1b19b3d2b157fee94b483.png)
另外,他的下拉列表实现, 不适合多个数据,假如有百十个就生成百十个下来的子菜单,这明显是不合理的,所以,把下拉改成 无限循环 列表是必须的。
于是,开始自己动手造轮子:
AخA
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using Assets.UI;
5
using UnityEngine;
6
using UnityEngine.UI;
7
/// <summary>
8
/// Introduction: GDropDown
9
/// Author: Cheng
10
/// Time:
11
/// </summary>
12
[AddComponentMenu("UI/GDropdown", 100)]
13
[RequireComponent(typeof(RectTransform))]
14
public class GDropDown : MonoBehaviour
15
{
16
[Tooltip("Button of Whole Component")]
17
[SerializeField]
18
private Toggle m_CaptionToggle;
19
/// <summary>
20
/// Button of Whole Component
21
/// </summary>
22
public Toggle CaptionToggle { get { return m_CaptionToggle; } set { SetCaptionButton(value);} }
23
[Tooltip("Display Text of Selected Item")]
24
[SerializeField]
25
private Text m_CaptionText;
26
/// <summary>
27
/// Display Text of Selected Item
28
/// </summary>
29
public Text CaptionText { get { return m_CaptionText; } set { m_CaptionText = value; } }
30
[Tooltip("Display Image of Selected Item")]
31
[SerializeField]
32
private Image m_CaptionImage;
33
/// <summary>
34
/// Display Image of Selected Item
35
/// </summary>
36
public Image CaptionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; } }
37
[Space]
38
[Tooltip("Drop List")]
39
[SerializeField]
40
private ScrollRect m_ScrollRect;
41
/// <summary>
42
/// Drop List
43
/// </summary>
44
public ScrollRect ScrollRect { get { return m_ScrollRect; } set { m_ScrollRect = value; } }
45
[Tooltip("Template of Drop List's Item")]
46
[SerializeField]
47
private GameObject m_DropItem;
48
/// <summary>
49
/// Template of Drop List's Item
50
/// </summary>
51
public GameObject DropItem { get { return m_DropItem; } set { SetDropItem(value); } }
52
[Tooltip("Current Select Index")]
53
[SerializeField]
54
private int m_Index;
55
/// <summary>
56
/// Current Select Index
57
/// </summary>
58
public int Index { get { return m_Index; } set { SetSelectIndex(value); }}
59
public string Text { get { return m_DropData.Count > Index ? m_DropData[Index].text : ""; } }
60
/// <summary>
61
/// Drop Down Value Changed
62
/// </summary>
63
public Callback_1<int> OnValueChanged;
64
[Tooltip("Drop Data")]
65
[SerializeField]
66
List<GItemData> m_DropData = new List<GItemData>();
67
private ScrollList m_ScrollList;
68
private Dictionary<Transform, GItem> m_Items = new Dictionary<Transform, GItem>();
69
private RectTransform m_PointerMask;
70
private Transform m_Canvas;
71
void Awake()
72
{
73
m_ScrollList = GetOrAddComponent<ScrollList>(ScrollRect.gameObject);
74
m_ScrollList.onItemRender = OnItemRender;
75
SetSelectIndex(m_Index);
76
SetDropItem(m_DropItem);
77
RefreshShowValue();
78
SetCaptionButton(m_CaptionToggle);
79
m_CaptionToggle.isOn = false;
80
CloseMask();
81
}
82
public void AddOptions(string[] options)
83
{
84
for (int i = 0; i < options.Length; i++)
85
this.m_DropData.Add(new GItemData(options[i]));
86
if (m_CaptionToggle.isOn)
87
OpenMask();
88
}
89
public void AddOptions(Sprite[] options)
90
{
91
for (int i = 0; i < options.Length; i++)
92
this.m_DropData.Add(new GItemData(options[i]));
93
if (m_CaptionToggle.isOn)
94
OpenMask();
95
}
96
public void AddOptions(GItemData[] options)
97
{
98
for (int i = 0; i < options.Length; i++)
99
this.m_DropData.Add(options[i]);
100
if (m_CaptionToggle.isOn)
101
OpenMask();
102
}
103
public void RemoveAt(int index)
104
{
105
if (this.m_DropData.Count > index)
106
{
107
this.m_DropData.RemoveAt(index);
108
if (index == Index)
109
{
110
SetSelectIndex(index-1);
111
}
112
}
113
}
114
public void ClearOptions()
115
{
116
this.m_DropData.Clear();
117
if (m_CaptionToggle.isOn)
118
OpenMask();
119
}
120
/// <summary>
121
/// Refresh Display View
122
/// </summary>
123
private void RefreshShowValue()
124
{
125
if (m_DropData.Count > Index)
126
{
127
GItemData data = m_DropData[Index];
128
if (CaptionText != null)
129
CaptionText.text = data.text;
130
if (CaptionImage != null)
131
CaptionImage.sprite = data.image;
132
}
133
else
134
{
135
if (CaptionText != null)
136
CaptionText.text = "";
137
if (CaptionImage != null)
138
CaptionImage.sprite = null;
139
}
140
}
141
/// <summary>
142
/// Render Item in List
143
/// </summary>
144
/// <param name="index"></param>
145
/// <param name="child"></param>
146
private void OnItemRender(int index, Transform child)
147
{
148
GItem item;
149
if (!m_Items.TryGetValue(child, out item))
150
item = new GItem(this, child);
151
if (m_DropData.Count > index)
152
{
153
item.Reset(m_DropData[index].text, m_DropData[index].image, index == Index);
154
}
155
}
156
/// <summary>
157
/// Set Cur Select Index When Click
158
/// </summary>
159
/// <param name="p"></param>
160
internal void SetSelectIndex(int p)
161
{
162
if (p < m_DropData.Count) //if exist data
163
{
164
m_Index = p;
165
RefreshShowValue();
166
if (OnValueChanged != null)
167
{
168
OnValueChanged(m_Index);
169
}
170
}
171
else
172
{
173
if (m_DropData.Count > 0)//Back To First
174
{
175
m_Index = 0;
176
RefreshShowValue();
177
if (OnValueChanged != null)
178
{
179
OnValueChanged(m_Index);
180
}
181
}
182
}
183
foreach (var value in m_Items.Values)
184
{
185
value.SetActive(false);
186
}
187
m_CaptionToggle.isOn = false;
188
}
189
/// <summary>
190
/// Open Mask to Poniters Out of List
191
/// </summary>
192
private void OpenMask()
193
{
194
if (m_PointerMask == null)//Create Mask
195
{
196
GameObject o = new GameObject("Pointer Mask");
197
o.transform.SetParent(transform);
198
Image mask = o.AddComponent<Image>();
199
mask.color = new Color(1, 1, 1, 0);
200
m_PointerMask = o.transform as RectTransform;
201
m_PointerMask.sizeDelta = new Vector2(Screen.width, Screen.height);
202
Button btnMask = o.AddComponent<Button>();
203
btnMask.onClick.AddListener(CloseMask);
204
}
205
if (m_Canvas == null)//Find Canvas
206
{
207
Canvas canvas = GameObject.FindObjectOfType<Canvas>();
208
m_Canvas = canvas.transform;
209
}
210
m_PointerMask.gameObject.SetActive(true);
211
m_PointerMask.SetParent(m_Canvas);
212
m_PointerMask.localPosition = Vector3.zero;
213
if (m_ScrollRect != null)
214
{
215
m_ScrollRect.transform.SetParent(m_Canvas);
216
m_ScrollRect.gameObject.SetActive(true);
217
m_ScrollList.ChildCount = m_DropData.Count;
218
}
219
}
220
/// <summary>
221
/// Close Mask to Other Pointers
222
/// </summary>
223
private void CloseMask()
224
{
225
if (m_PointerMask != null)
226
{
227
m_PointerMask.transform.SetParent(transform);
228
m_PointerMask.gameObject.SetActive(false);
229
}
230
if (m_ScrollRect != null)
231
{
232
m_ScrollRect.transform.SetParent(transform);
233
m_ScrollRect.gameObject.SetActive(false);
234
}
235
}
236
/// <summary>
237
/// Set Drop Item, Set Anchor Left-Top
238
/// </summary>
239
/// <param name="item"></param>
240
private void SetDropItem(GameObject item)
241
{
242
RectTransform rect = item.transform as RectTransform;
243
rect.anchorMin = Vector2.up;
244
rect.anchorMax = Vector2.up;
245
m_DropItem = item;
246
m_ScrollList.Child = item;
247
}
248
/// <summary>
249
/// Set Outter Button of Whole Component
250
/// </summary>
251
/// <param name="btn"></param>
252
private void SetCaptionButton(Toggle btn)
253
{
254
if (m_CaptionToggle != null)
255
m_CaptionToggle.onValueChanged.RemoveListener(OnCaptionButtonClicked);
256
m_CaptionToggle = btn;
257
if(m_CaptionToggle != null)
258
m_CaptionToggle.onValueChanged.AddListener(OnCaptionButtonClicked);
259
}
260
/// <summary>
261
/// On Caption Button Clicked
262
/// </summary>
263
private void OnCaptionButtonClicked(bool active)
264
{
265
if (active)
266
OpenMask();
267
else
268
CloseMask();
269
}
270
/// <summary>
271
/// Get Or Add Component on O
272
/// </summary>
273
/// <typeparam name="T"></typeparam>
274
/// <param name="o"></param>
275
/// <returns></returns>
276
T GetOrAddComponent<T>(GameObject o) where T : Component
277
{
278
T com = o.GetComponent<T>();
279
if (com == null)
280
com = o.AddComponent<T>();
281
return com;
282
}
283
/// <summary>
284
/// Release All
285
/// </summary>
286
public void OnDestroy()
287
{
288
//If Release on Drop State, Delete Mask
289
if(m_PointerMask != null)
290
m_PointerMask.SetParent(transform);
291
}
292
/// <summary>
293
/// Renderer Item in Endless List
294
/// </summary>
295
protected internal class GItem
296
{
297
GDropDown dropDown;
298
Transform item;
299
Button btn;
300
Text text;
301
Image image;
302
GameObject selected;
303
bool activeSelf;
304
public string m_Text { get { return text.text; } set { text.text = value; } }
305
public Sprite m_Image { get { return image.sprite; } set { image.sprite = value; } }
306
public GItem(GDropDown parent, Transform item)
307
{
308
this.dropDown = parent;
309
this.item = item;
310
activeSelf = false;
311
Transform t_trans = item.FindChild("text");
312
if (t_trans)
313
{
314
text = t_trans.gameObject.GetComponent<Text>();
315
}
316
Transform t_image = item.FindChild("image");
317
if (t_image)
318
{
319
image = t_image.gameObject.GetComponent<Image>();
320
}
321
Transform t_selected = item.FindChild("selected");
322
if (t_selected)
323
{
324
selected = t_selected.gameObject;
325
}
326
btn = item.GetComponent<Button>();
327
if (btn == null)
328
{
329
Transform t_btn = item.FindChild("btn");
330
if (t_btn != null)
331
btn = dropDown.GetOrAddComponent<Button>(t_btn.gameObject);
332
else
333
btn = dropDown.GetOrAddComponent<Button>(item.gameObject);
334
}
335
btn.onClick.AddListener(OnBtnItemClicked);
336
}
337
private void OnBtnItemClicked()
338
{
339
if (!activeSelf)
340
{
341
SetActive(true);
342
dropDown.SetSelectIndex(int.Parse(item.name));
343
}
344
}
345
internal void SetActive(bool active)
346
{
347
this.activeSelf = active;
348
if(selected != null)
349
selected.SetActive(active);
350
}
351
internal void Reset(string txt, Sprite sprite, bool active)
352
{
353
if (text != null)
354
m_Text = txt;
355
if (image != null)
356
m_Image = sprite;
357
SetActive(active);
358
}
359
}
360
/// <summary>
361
/// Cache Data
362
/// </summary>
363
[Serializable]
364
public class GItemData
365
{
366
[SerializeField]
367
private string m_Text;
368
[SerializeField]
369
private Sprite m_Image;
370
public string text { get { return m_Text; } set { m_Text = value; } }
371
public Sprite image { get { return m_Image; } set { m_Image = value; } }
372
public GItemData(){}
373
public GItemData(string text)
374
{
375
this.text = text;
376
}
377
public GItemData(Sprite image)
378
{
379
this.image = image;
380
}
381
public GItemData(string text, Sprite image)
382
{
383
this.text = text;
384
this.image = image;
385
}
386
}
387
}
![UGUI DropDown遇到的问题总结](https://i-blog.csdnimg.cn/blog_migrate/9a17f4d8e04aeed668bd64224cd2020c.png)
组件的点击由Toggle 来控制 与 系统UGUI的类似,支持 点击选中 图片和文字, 自己添加一个下拉列表。
注意: 下拉子选项中, 命名 “text”的“Text”组件、命名 “image”的“Image”组件为该选项的可填充值,看代码一眼便知。命名“selected”的表示下拉列表打开时该选项选中的表现,与toggle相同,我只是乐意改成了button表示而已。