Unity 的 UGUI 组件Dropdown简析

Dropdown设置属性

Unity 会自动帮我们创建一个下拉菜单的模板、并且添加好 Dropdown 组件.

我们需要做的就是,设置我们需要的属性、以及功能即可

将我们的脚本、需要的选择项、UI选项设置好就可以用了

1:首先看一下控件

自己添加了一个新的Item Image,后面会提到它的用处。

Image用来做赋值对象的,后面会提到

Lable和Arrow是用来显示初始化的文字和勾选项的

2:看一下Dropdown的属性面板

Caption TextCaption Image是作为下拉列表首选项的文字和图片显示

Item TextItem Image是作为下拉列表中每个item的文字和图片显示

Value值会随着下拉列表选项的不同而变化,可通过代码利用

Options选项栏内:通过代码可赋值给相应的Item对象  Dropdown.OptionData

 

Demo1:

using UnityEngine;
using UnityEngine.UI;

private Dropdown dropDown;

/// <summary>
/// 测试Dropdown组件例子脚本
/// </summary>
public class TestDropdown : MonoBehaviour
{
    void Start()
    {
        //代码动态绑定的方法,如果通过代码添加监听事件,外部就无需再做添加 
        //查找Dropdown组件
        dropDown = GameObject.Find("Dropdown").GetComponent<Dropdown>();
        //监听组件事件
        dropDown.onValueChanged.AddListener(ConsoleResult);
    }


    /// <summary>
    /// 输出结果 —— 添加监听事件时要注意,需要绑定动态方法
    /// </summary>
    public void ConsoleResult(int value)
    {
        //这里用 if else if也可,看自己喜欢
        //分别对应:第一项、第二项....以此类推
        switch (value)
        {
            case 0:
                print("第1页");
                break;
            case 1:
                print("第2页");
                break;
            case 2:
                print("第3页");
                break;
            case 3:
                print("第4页");
                break;
            //如果只设置的了4项,而代码中有第五个,是调用不到的
            //需要对应在 Dropdown组件中的 Options属性 中增加选择项即可
            case 4:
                print("第5页");
                break;
        }
    }
}

 

Demo2:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropDownTest : MonoBehaviour {

    public Dropdown dropDown;

	void Start () {

        //是否可以点击
        dropDown.interactable = true;

        #region 添加下拉选项,,,设置文字,底图
        //添加一个下拉选项
        Dropdown.OptionData data = new Dropdown.OptionData();
        data.text = "方案一";
        //data.image = "指定一个图片做背景不指定则使用默认";
        dropDown.options.Add(data);

        //另一种添加方式 , 不过用起来并不比第一个方便,
        List<Dropdown.OptionData> listOptions = new List<Dropdown.OptionData>();       
        listOptions.Add(new Dropdown.OptionData("方案二"));
        listOptions.Add(new Dropdown.OptionData("方案三"));
        dropDown.AddOptions(listOptions);

        //设置显示字体大小
        dropDown.captionText.fontSize = 14;
        //dropDown.captionImage = "底图";
        //设置要复制字体大小
        dropDown.itemText.fontSize = 15;
        //dropDown.itemImage = "底图";

        //PS:我一般是使用循环 使用第一种形式添加
        #endregion

        #region 添加完成就可以使用了,那么当我们想要复用怎么办呢?,这时就用到了移除OptionData,下面的每个注释打开都是一个功能
        //直接清理掉所有的下拉选项,
        dropDown.ClearOptions();
        //亲测不是很好用
        //dropDown.options.Clear(); 

        //对象池回收时,有下拉状态的,直接干掉... (在极限点击测试的情况下会出现)
        if (dropDown.transform.childCount == 3)
        {
            Destroy(dropDown.transform.GetChild(2).gameObject);
        }

        //移除指定数据   参数:OptionData
        dropDown.options.Remove(data);
        //移除指定位置   参数:索引
        dropDown.options.RemoveAt(0); 
        #endregion

        #region 添加监听函数
        //当点击后值改变是触发 (切换下拉选项)
        dropDown.onValueChanged.AddListener((int v) => OnValueChange(v));
        //若有多个,可以将自己当做参数传递进去,已做区分。
        //dropDown.onValueChanged_1.AddListener((int v) => OnValueChange(dropDown.gameobject,v));
        #endregion
    }

    /// <summary>
    /// 当点击后值改变是触发 (切换下拉选项)
    /// </summary>
    /// <param name="v">是点击的选项在OptionData下的索引值</param>
    void OnValueChange(int v)
    {
        //切换选项 时处理其他的逻辑...
        Debug.Log("点击下拉控件的索引是..." + v);
    }	
	
}

 

Demo3:

public class UIDropdownItem : MonoBehaviour {

    Dropdown dropdownItem;
    List<string> tempNames;

    void Awake()
    {
        dropdownItem = GetComponent<Dropdown>();
        tempNames = new List<string>();
    }

    void Start()
    {
        AddNames();
        UpdateDropdownView(tempNames);
    }

    /// <summary>
    /// 刷数据
    /// </summary>
    /// <param name="showNames"></param>
    private void UpdateDropdownView(List<string> showNames)
    {
        dropdownItem.options.Clear();
        Dropdown.OptionData tempData;
        for (int i = 0; i < showNames.Count; i++)
        {
            tempData = new Dropdown.OptionData();
            tempData.text = showNames[i];
            dropdownItem.options.Add(tempData);
        }
        dropdownItem.captionText.text = showNames[0];
    }
    /// <summary>
    /// 模拟数据
    /// </summary>
    private void AddNames()
    {
        string s1 = "小白";
        string s2 = "小胖";
        string s3 = "桃桃";
        string s4 = "南瓜";
        string s5 = "宝哥哥";

        tempNames.Add(s1);
        tempNames.Add(s2);
        tempNames.Add(s3);
        tempNames.Add(s4);
        tempNames.Add(s5);
    }
}


Demo4:

要实现的功能:1下拉列表里显示image,2每次选择后dropdown里的图片跟着一起改变。

1.看一下控件

自己添加了一个新的Item Image,后面会提到它的用处。

Image用来做赋值对象的,后面会提到

Lable和Arrow是用来显示初始化的文字和勾选项的

2:看一下Dropdown的属性面板

 

 

3. 脚本和需要使用的对象

ShowText自己测试用的每个Item的Text

Sprite自己用来做每个Item的图片显示

想实现如下效果:

other_img是随便的一个img对象用来作为第三者赋值的,看代码

代码模块

说明:参考他人的代码,并做了修改

public string[] showText;
    public Sprite[] sprite;
    Dropdown dropDownItem;
    List<string> temoNames;
    List<Sprite> sprite_list;
    public Image other_img;//任意的img,用作被赋值替换
    void Start()
    {
        dropDownItem = this.GetComponent<Dropdown>();
        temoNames = new List<string>();
        sprite_list = new List<Sprite>();
        
        AddNames();
        UpdateDropDownItem(temoNames);
        
    }


    void UpdateDropDownItem(List<string> showNames)
    {
        dropDownItem.options.Clear();
        Dropdown.OptionData temoData;
        for (int i = 0; i <showNames.Count; i++)
        {
            //给每一个option选项赋值
            temoData = new Dropdown.OptionData();
            temoData.text = showNames[i];
            temoData.image = sprite_list[i];
            dropDownItem.options.Add(temoData);
        }
        //初始选项的显示
        dropDownItem.captionText.text = showNames[0];
        other_img.sprite = sprite_list[0];
        dropDownItem.captionImage =other_img;
        
    }
    void Update()
    {
        if (dropDownItem.value==0)
        {
            Debug.Log("11111111");            
        }
        if (dropDownItem.value==1)
        {
            Debug.Log("22222222");
        }
        if (dropDownItem.value==2)
        {
            Debug.Log("33333333");
        }


       this.GetComponent<Image>().sprite = other_img.sprite;
    }
 void AddNames()
    {
        for (int i = 0; i <showText.Length; i++)
        {
            temoNames.Add(showText[i]);
        }
        for (int i = 0; i <sprite.Length; i++)
        {
            sprite_list.Add(sprite[i]);
        }
    }


补充代码说明:
 

captionText和cationImage作为首选项,内容显示

此处将每一次的选择下拉列表后的值显示在dropdown里,发现文字显示了,dropdown的Imge属性一直是null,所以此处先将每次选择后的image属性保存起来,然后再Update中更新赋值。

需要转换Image和sprite的属性,故用了第三者other_image

如果不需要dropdown显示出下拉列表的image图,只需要去掉上面红框里的代码和Update里的代码即可。自己也可单独给表头添加一张图,这样游戏内的看着好看的下拉列表就完成了。

注意:放入update要小心,性能堪忧,不建议。

可以在如下位置分别格外添加Image节点,作为自定义的背景图或图标,这样代码也省了

 

Demo5

扩展Dropdown, 使dropdown支持多选

 参考文章 https://www.cnblogs.com/chinarbolg/p/9601417.html 进行扩展后,dropdown已经可以支持重复选择了。

 添加一个标记选项状态的值

    // 当前筛选的技能质量标记
    public int SelectIndexBitMark = 0;

修改重写的Show()方法

复制代码

    public new void Show () {
        base.Show ();
        Transform toggleRoot = transform.Find ("Dropdown List/Viewport/Content");
        Toggle[] toggleList = toggleRoot.GetComponentsInChildren<Toggle> (false);
        for (int i = 0; i < toggleList.Length; i++) {
            Toggle temp = toggleList[i];
            temp.onValueChanged.RemoveAllListeners ();
            // temp.isOn = false;   // 原文
            temp.isOn = ((1 << i) & SelectIndexBitMark) > 0; // 改造后
            temp.onValueChanged.AddListener (x => OnSelectItemEx (temp));
        }
    }

复制代码

在SelectItemEx中增加状态标记的赋值

复制代码

    public void OnSelectItemEx (Toggle toggle) {
        if (!toggle.isOn) {
            toggle.isOn = true;
            return;
        }

        int selectedIndex = -1;
        Transform tr = toggle.transform;
        Transform parent = tr.parent;
        for (int i = 0; i < parent.childCount; i++) {
            if (parent.GetChild (i) == tr) {
                selectedIndex = i - 1;
                break;
            }
        }

        if (selectedIndex < 0)
            return;
        if (value == selectedIndex && AlwaysCallback)
            onValueChanged.Invoke (value);
        else
            value = selectedIndex;
        SelectIndexBitMark ^= 1 << value;   // 新增
        Hide ();
    }

复制代码

 

Demo6

可编辑下拉框, 

难点
  处理输入框和下拉框的优先级显示问题,做到二者无缝衔接。当下拉框选中某一项时,输入框内容清空且变透明;当输入框有文本输入时,下拉框选中默认项(空白项)且变透明

实现
dropdown上添加一个Input Field组件,调整长度使其不覆盖下拉箭头
代码中监听dropdown和Input Field的ValueChanged事件,这里用0和1区分是dropdown还是Input Field引起的函数调用,代码如下:
 

public void Start()
{
    // 设置下拉框和输入框的值变化监听
    cityDropdown.onValueChanged.AddListener(delegate { OnCityValueChanged(0); });
    cityInput.onValueChanged.AddListener(delegate {OnCityValueChanged(1);});
}

// 事件处理函数
void OnCityValueChanged(int type)
{
    switch (type)
    {
        case 0:// 下拉框
            // 获取选中项的值
            pressCity = cityDropdown.options[cityDropdown.value].text;
            // 只处理不等于0的情况,即下拉框选中的不是默认值
            if (cityDropdown.value != 0)
            {
                // 清空输入框的值
                cityInput.text = "";
                // 下拉框不透明
                cityDropdown.GetComponent<Image>().color = new Color(1, 1, 1, 1);
                // 输入框透明,防止遮挡
                cityInput.GetComponent<Image>().color = new Color(1, 1, 1, 0);
            }
            break;

        case 1:// 输入框
            // 获取输入框的值
            pressCity = cityInput.text;
            // 只处理输入框文本不为空的情况
            if (cityInput.text != "")
            {
                // 下拉框选中默认值(空白)
                cityDropdown.value = 0;
                // 下拉框透明    
                cityDropdown.GetComponent<Image>().color = new Color(1, 1, 1, 0);
                // 输入框不透明,使输入框文本内容置顶
                cityInput.GetComponent<Image>().color = new Color(1, 1, 1, 1);
            }
            break;
    }
}

Demo7

Dropdown List对象是以下拉菜单对象下的子对象“Template”对象为模板动态创建的。 也就是说,每当我打开下拉菜单,都会以“Template”对象为模板创建一个Dropdown List对象,两者所附加的脚本以及各项设置均相同。于是乎,我想到如果在“Template”对象上添加一个脚本,在Start()中读取脚本所附加的对象名,判断是否为“DropDown List”,如果是的话,证明打开了下拉菜单:

void Start()
{
    if (this.name == "Dropdown List")
    {
        //FileListOpen是其他下拉菜单所附加的脚本中表示文件下拉菜单是否展开的一个布尔值
        //我在这里相当于告诉其他下拉菜单,文件下拉菜单已经展开
        OtherDropList.GetComponent<DropListController>().FileListOpen = true;
    }
}

当下拉菜单关闭时,DropDown List会被销毁,所以在OnDestroy()中修改其他下拉菜单上的脚本中表示文件下拉菜单是否展开的布尔值:

void OnDestroy()
{
    if (this.name == "Dropdown List")
    {        
         OtherDropList.GetComponent<DropListController>().FileListOpen = false;
    }
}

这种办法避免了使用Find(),也不用在Update()或者FixedUpdate()中执行,但是却可以实时的获取下拉菜单的状态。

 

Demo8

需求:点击下拉列表的内容后就执行功能,类似Button事件。其实组件下方的OnValueChanged这个UnityEvent已经是我们要的功能了

下面用两种方法达到同样效果,推荐方法2官方 OnValueChanged接口

使用方法1:

a:首先创建一个脚本挂载在Dropdown组件下,继承ISelectHandler,把dropdown组件拖过来

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class Drop : MonoBehaviour,ISelectHandler
{
    public Dropdown drop;
    private int lastIndex;
   
    public void OnSelect(BaseEventData eventData)
    {
        //避免点击下拉列表item和dropdown重复调用
        if (drop.value == lastIndex) return;
        
        //处理逻辑
        //
 
        Debug.Log("OnSelect=" + drop.value);
        lastIndex = drop.value;
    }
}

 

Demo9: OnValueChanged传多个参数

using UnityEngine;
using UnityEngine.UI;


/// <summary>
/// 测试Dropdown组件例子脚本
/// </summary>
public class TestDropdown : MonoBehaviour
{
    void Start()
    {
        int testArg = 123;
        //代码动态绑定的方法,如果通过代码添加监听事件,外部就无需再做添加
        //GameObject.Find("Dropdown").GetComponent<Dropdown>().onValueChanged.AddListener(ConsoleResult);
        GameObject.Find("Dropdown").GetComponent<Dropdown>().onValueChanged.AddListener((int value)=> ConsoleResult(value,testArg));
    }


    /// <summary>
    /// 输出结果 —— 添加监听事件时要注意,需要绑定动态方法
    /// </summary>
    public void ConsoleResult(int value,int testArg)
    {
        //这里用 if else if也可,看自己喜欢
        //分别对应:第一项、第二项....以此类推
        switch (value)
        {
            case 0:
                print("第1页,测试传参:"+ testArg);

                break;
            case 1:
                print("第2页");
                break;
            case 2:
                print("第3页");
                break;
            case 3:
                print("第4页");
                break;
            //如果只设置的了4项,而代码中有第五个,是调用不到的
            //需要对应在 Dropdown组件中的 Options属性 中增加选择项即可
            case 4:
                print("第5页");
                break;
        }
    }
}

 

 

 

 

 

注意事项:

1.
手动添加监听事件,需绑定动态方法(Unity会自动生成)

2.
设置自己需要的默认项,默认为0,当下拉菜单弹起时,默认就是第一项

3.
下拉菜单的机制,就是选中的选项,如果再次点击是不会调用方法的!!!
(也就是说,你已经选择了第一页,再次点击第一页,是不会调用任何函数的)

4.  首先dropdown是可以自动上下弹出的根据你放置的位置自动选择向上,还是向下弹出列表。

5. 可以通过dropdown.addoptions(“放入要显示的list数据”)方法显示要显示的内容;

6. dropdown的shown()和hide()方法相信大家都能看懂。我主要要实现的功能是当用户选择其中一项的时候,动态的删掉选中的这一项,删完以后让dropdown继续还原到显示初始的值。通过mydrop.options.RemoveAt(mydrop.value)方法 来移除当前选中的内容。

7. mydrop.value返回的是当前值所在list中的位置,位置是从0开始算起的。

8. 若想获取当前选中的dropdown的文本内容主要有以下两种方法:

一:mydrop.options[mydrop.value].text;

二:mydrop.captionText.text;

9. 想要让dropdown选中指定的内容需要设置  mydrop.value = 一个数值 就可以实现。

10、Dropdown组件已经自动适配大小:根据添加的项数

11、模板Template是一个scroll Rect,其中content的大小有玄机:

如果content的高度小于单个item的大小,显示时,不能显示所有项,需要滚动。

如果content的高度等于单个item的大小,则显示时,刚好能显示所有item项,item项与conten上下边界之间没有空隙。

如果content的高度大于单个item的大小,显示时,item和content之间上下边界处有留空间,总共留的空隙大小为:content的高度-item的高度。此时上下边界各留多少,取决于item的坐标对齐方式。

12. 下拉列表有时会因画布层级被其它UI遮挡,解决办法:Dropdown Canvas SortingLayer https://blog.csdn.net/qq_42672770/article/details/109443837


 

底层实现:https://gameinstitute.qq.com/community/detail/121823

扩展: https://www.bilibili.com/video/av40188564/

重写: https://blog.csdn.net/ChinarCSDN/article/details/80233602

官方文档翻译: https://www.cnblogs.com/SolarWings/p/8185573.html

https://blog.csdn.net/weixin_42612319/article/details/104699250 

https://blog.csdn.net/qq_37699470/article/details/103279990

 

Dropdown下拉列表因Canvas SortingLayer被遮挡 https://blog.csdn.net/qq_42672770/article/details/109443837

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值