下拉菜单在游戏或者应用中都有很多应用,比如背包中的分类,语言设置等等。Unity UGUI提供了Dropdown组件来实现这个功能。参考官方手册
1.介绍
一张运行展开下拉菜单图如下,默认状态是收起状态,没有DropdownList(DropdownList运行时动态生成的临时性交互界面,它在用户点击下拉框时被创建,在选择完成后自动销毁)。
组件层级结构 。在Hierarchy 中新建 UI ▸ Dropdown-TextMeshPro 时,Unity 会自动生成 4 个必备节点:
- Dropdown-TextMeshPro(根节点,挂有下拉逻辑脚本 TMP_Dropdown)
- Label(TextMeshPro-UGUI,显示当前选中的文字)
- Arrow(Image,右侧小三角)
- Template(整个下拉列表模板,内含滚动区域、Viewport、Content 以及 Item 模板)

对象 | 中文名 | 作用 |
---|---|---|
Dropdown (根) | 下拉根节点 | 挂 Dropdown 脚本 |
Label | 标题文本 | 显示当前选中文字(对应 Caption Text) |
Arrow | 展开箭头 | 指示点击区域,旋转 180° 表示展开/收起 |
Template | 列表模板 | 非激活,展开时克隆为下拉列表 |
Item (在 Template 内) | 列表项模板 | 含 Toggle + Item Text / Item Image |
DropdownList (展开状态下自动生成) | 下拉项列表 | 运行时动态生成的临时性交互界面,它在用户点击下拉框时被创建,在选择完成后自动销毁。 |
2.组成
(1).Dropdown

一、Dropdown(TMP)参数
以下按在上图参数出现顺序列出常用字段,并给出用途说明。
-
Interactable / Transition / Navigation
与 Button、Toggle 等标准 UI 控件的交互状态完全一致,不再赘述。
-
Template
关联第 4 个节点 RectTransform。运行时列表展开时会把 Template 的副本实例化并显示。Template 里必须包含:
• ScrollRect(滚动区域)
• Content(Vertical Layout Group + Content Size Fitter,用于动态排列 Item)
• Item(Item 模板,挂有 Toggle + Layout Element)
-
Caption Text
指向 Label 上的 TextMeshPro-UGUI 组件,用于显示当前选项文本。
-
Caption Image
如果想在文字前面再显示一个小图标(如国旗),可把 Image 拖进来,运行时会把当前选项对应的 Sprite 赋值给它。
-
Item Text
指向 Item 模板里的 TextMeshPro-UGUI,用于渲染列表中每一行的文字。
-
Item Image
同理,对应每一行的图标 Image。
-
Value
当前选中项的索引(int)。-1 表示未选中任何项,此时 Caption Text 会显示 Placeholder 文字。
-
Options(List)
在 Inspector 中可逐条添加下拉选项。每条 OptionData 包含:
• text(string)
• image(Sprite,可为空)
-
Placeholder
当 Value == -1 时,Label 会显示此占位符文字;常用于"请选择..."提示。
-
Multi-select
勾选后允许用户通过 Ctrl/Shift 多选;目前需自行在代码里读取 selectedIndices 数组。
-
On Value Changed(Int32)
标准 UnityEvent,当选中索引发生变化时触发。可拖脚本,也可运行时 AddListener。
二、常用拓展与脚本控制
- 动态增删选项
csharp
var dropdown = GetComponent<TMP_Dropdown>();
dropdown.options.Add(new TMP_Dropdown.OptionData("新选项"));
dropdown.RefreshShownValue();
- 显示/隐藏图标
csharp
dropdown.captionImage.sprite = mySprite;
dropdown.itemImage.sprite = mySprite;
- 监听事件
csharp
dropdown.onValueChanged.AddListener(index => Debug.Log($"选了第 {index} 项"));
-
样式与特效
由于文本已替换成 TextMeshPro-UGUI,可直接在 Label/Item 上加材质参数(Outline、Shadow、Glow 等),或利用富文本标签 <sprite=0> 等实现彩色图文混排。
-
性能与滚动
Template 里的 ScrollRect 拥有 Scroll Sensitivity 字段,可调整滚轮速度;若选项极多,可把 Content 上的 Vertical Layout Group + Content Size Fitter 换成更轻量的 Layout Group 或对象池方案。
(2).Laber
Label是下拉框用来显示"当前选中项文字"的专用 TextMeshPro-UGUI 节点,位于层级结构的第二层级(Dropdown-TextMeshPro ▸ Label)。它既受 TMP_Dropdown 逻辑驱动,也拥有完整的 TextMeshPro-UGUI 能力。Hierarchy 中新建 UI ▸ Dropdown-TextMeshPro 时,Unity 会同步生成 1 个 Label 节点。

(3).Arrow
Arrow(Dropdown-TextMeshPro 中的下拉箭头) 是位于 Caption 区域最右侧的视觉指示器,用来告诉用户"这里可以点开下拉列表"。它本质上是一个 Image 控件,挂靠在 Dropdown-TextMeshPro 的第三层级,仅做显示与过渡效果,不参与文本逻辑。细心的小伙伴可能已经看出来,这个Arrow只有RT和Image组件,并没有类似Button一样的组件,实际上他是通过射线检测的。(感兴趣点击查看原理)
性能这一块
• 箭头仅 1 个 Image,顶点数极低,无需对象池。
• 若使用 Addressable 图标,确保 Sprite Atlas 打包,避免额外 DrawCall。
• 在移动平台,箭头尺寸建议 ≥ 44×44 pt(苹果 HIG 最小触控区域),避免误触。
(4).Template
Template(下拉列表模板)是 Dropdown-TextMeshPro 的模板,负责在运行时把一份预先搭好的 UI 结构克隆出来,变成玩家真正能看到、能滚动的下拉列表。它平时处于 禁用状态,只有在点击下拉框时才被 实例化并激活。
一、它为什么会出现(设计目的)
- 一次性预制
下拉列表通常包含 N 个 Item,Item 数量在运行时可能动态变化。如果把所有元素都直接放在场景里,每次增删都得实时创建/销毁,成本高。 - 统一样式
通过模板,可以保证所有下拉框的外观、布局、动画完全一致,只需改一份 Template 即可全局生效。 - 惰性实例化
Template 默认是 Inactive 的,不占用渲染、布局、更新开销;点击后才Instantiate
,用完Destroy
。
二、Template 本身是什么
- 一个普通的 GameObject,挂有 RectTransform。
- 不挂载任何逻辑脚本 (逻辑全部由 TMP_Dropdown 在
Show()
里接管)。 - Scene 可见性 :Inspector 里默认是灰色(未激活),运行时会被克隆为
Dropdown List
(名字固定)。 - 引用方式 :TMP_Dropdown 组件的
Template
字段拖拽关联该物体;缺失会抛出MissingReferenceException
。
三、Template 的标准组成(层级关系 )
Template (RectTransform)
├─ Viewport (RectTransform + Mask 或 RectMask2D + GraphicRaycaster*)
│ ├─ Content (RectTransform + Vertical Layout Group + Content Size Fitter)
│ │ ├─ Item (Toggle + Layout Element + Image + TextMeshPro-UGUI)
│ │ ├─ Item (Clone ...)
│ │ └─ ...
│ └─ Scrollbar (可选,Scrollbar 组件)
└─ Canvas (可选,仅当需要独立 Canvas 时)
注:带 * 的组件由 TMP_Dropdown 在运行时动态附加,模板本身不需要提前挂。
四、每个子物体的职责与关键参数
子物体 | 必须/可选 | 关键组件 & 常用设置 | 作用说明 |
---|---|---|---|
Template | 必须 | RectTransform | 模板根节点,决定整个下拉列表的锚点、尺寸;TMP_Dropdown 会把它作为克隆源。 |
Viewport | 必须 | Mask / RectMask2D + Image | 把超出边缘的 Item 裁剪掉;Image 用来接收射线(点击空白处自动关闭下拉)。 |
Content | 必须 | Vertical Layout Group + Content Size Fitter | 垂直排列 Item;Content Size Fitter 的 Preferred Size 让高度随 Item 数量动态增长。 |
Item | 必须 | Toggle + Layout Element | 单条选项的可交互单元;Toggle 的 onValueChanged 由 TMP_Dropdown 统一接管。 |
└─ Item Background | 内部 | Image | 背景图,支持不同状态(Normal / Highlighted / Pressed)。 |
└─ Item Checkmark | 内部 | Image | 当前选中项的对勾图标,仅在被选中时激活。 |
└─ Item Label | 内部 | TextMeshPro-UGUI | 显示文字,对应 OptionData.text。 |
Scrollbar | 可选 | Scrollbar + Image + Mask | 当 Item 过多时出现;可替换成 ScrollRect 的 horizontal / verticalScrollbar 。 |
五、运行时的极简时序图
用户点击 Dropdown → TMP_Dropdown.Show()
1. clone = Instantiate(Template)
2. clone.name = "Dropdown List"
3. clone.SetActive(true)
4. clone.AddComponent<Canvas>(); clone.AddComponent<GraphicRaycaster>();
5. 填充 Content 的子物体:根据 options.Count 复制 Item
6. 绑定事件:点击 Item → 修改 dropdown.value → 关闭列表
用户再次点击或选择 → TMP_Dropdown.Hide()
1. Destroy(clone)
一句话总结
Template 就是 Dropdown 的「蓝图」。
(4).Dropdown List

Dropdown List(运行时展开的下拉列表) 是 Template 的克隆体 ,在玩家点击下拉框的瞬间由 TMP_Dropdown 动态生成、填充并摆到屏幕最上层。它只在 Show() → Hide() 之间存活,生命周期极短,却是用户真正能看到、能滚动、能点选的完整 UI。
一、出现目的(为什么要克隆出它)
- 隔离运行时数据
Template 只是"蓝图"------保持编辑状态不变;克隆体可随意增删 Item、改 Rect、加动画而不影响原模板。 - 独立渲染层级
下拉列表需要覆盖在其他 UI 之上,因此运行时会给克隆体额外挂一个 Canvas + GraphicRaycaster ,并把sortingOrder
调高(默认 30000)。 - 用完即毁
用户选中或点击外部后立刻Destroy
避免常驻内存和更新开销。
二、Dropdown List 本身是什么
- 名字固定 :运行时实例化后统一命名为 "Dropdown List"(可在层级视图动态看到)。
- 根节点 :一个 RectTransform 组件,完全复制 Template 的锚点、尺寸、Pivot。
- 激活开关 :
SetActive(true)
时才可见;关闭后SetActive(false)
或直接销毁。 - 脚本 :不挂任何自定义脚本,所有逻辑仍由 TMP_Dropdown 持有并驱动。
三、标准组成(层级 & 组件)
Dropdown List (RectTransform)
├─ Viewport (RectTransform + Mask/RectMask2D + GraphicRaycaster*)
│ ├─ Content (RectTransform + Vertical Layout Group + Content Size Fitter)
│ │ ├─ Item (Toggle + Layout Element + Image + TextMeshPro-UGUI)
│ │ ├─ Item (Clone ...)
│ │ └─ ... (根据 options.Count 动态克隆)
│ └─ Scrollbar (Vertical) (可选)
└─ Canvas* + GraphicRaycaster* ← 运行时由 TMP_Dropdown 自动添加
带 * 的组件在 Template 里 不存在 ,是运行时 额外加 的。
四、每个子物体的运行时职责 & 参数
子物体 | 运行时来源 | 关键组件 & 常用设置 | 作用说明 |
---|---|---|---|
Dropdown List | Instantiate(Template) | RectTransform + Canvas + GraphicRaycaster | 根节点;Canvas 负责独立排序,GraphicRaycaster 负责接收射线。 |
Viewport | 克隆自 Template.Viewport | Mask / RectMask2D | 把超出边缘的 Item 裁剪掉;Image 背景同时作为"点击空白关闭"的检测区域。 |
Content | 克隆自 Template.Content | Vertical Layout Group + Content Size Fitter | 垂直排列 Item;Content Size Fitter 的 PreferredSize 让高度随 Item 数量动态增长。 |
Item(N 条) | 克隆自 Template.Item | Toggle + Layout Element + Image + TextMeshPro-UGUI | 单条可交互选项;Toggle.group 由 TMP_Dropdown 自动管理,保证单选。 |
└─ Item Background | 克隆 | Image | 背景图;支持不同状态(Normal / Highlighted / Pressed)。 |
└─ Item Checkmark | 克隆 | Image | 对勾图标;仅当前选中项显示。 |
└─ Item Label | 克隆 | TextMeshPro-UGUI | 显示文字,自动绑定 OptionData.text。 |
Scrollbar | 克隆自 Template.Scrollbar(可选) | Scrollbar + Image + Mask | 当 Item 数量超出 Viewport 高度时出现;与 ScrollRect 联动。 |
五、生命周期 & 关键代码片段
csharp
// TMP_Dropdown.Show() 精简流程
GameObject list = Instantiate(template.gameObject);
list.name = "Dropdown List";
list.SetActive(true);
// 1. 额外挂 Canvas 与 GraphicRaycaster
Canvas c = list.AddComponent<Canvas>();
c.overrideSorting = true;
c.sortingOrder = 30000;
list.AddComponent<GraphicRaycaster>();
// 2. 填充 Content
RectTransform content = list.transform.Find("Viewport/Content") as RectTransform;
foreach (var option in options)
{
GameObject item = Instantiate(itemTemplate, content);
item.GetComponentInChildren<TMP_Text>().text = option.text;
item.GetComponent<Toggle>().onValueChanged.AddListener(OnItemValueChanged);
}
// 3. 点击空白关闭
list.GetComponentInChildren<MaskableGraphic>().AddComponent<CloseOnOutsideClick>();
总结
Dropdown List 就是 Template 的"副本":只在需要时出生,带着独立 Canvas、排好 Item、等用户点选;任务完成立即销毁。