Unity 多级下拉菜单

Unity自带的Dropdown只能出现一级下拉菜单 在尝试修改之后 无法实现 索性自己写了一个
效果如下
在这里插入图片描述
组件结构
在这里插入图片描述
在这里插入图片描述

主按钮 MainButton 点击之后出现菜单

菜单 dropdownpanel 放置多个按钮Item

菜单列表 dropdown列表 放置多个菜单

按钮Item dropdownItem模板 每个菜单按钮的模板

获取dropdownItem模板大小 本想自动适配大小 后来没有用

背景按钮隐藏 一个巨大的下层Mask 用于点击外部关闭整个菜单

代码

界面部分

public class MoreDropdown : MonoBehaviour
    {
        [Header("主按钮")]
        public Button mainButton;
        [Header("dropdownPanel模板")]
        public Image dropdownPanel;
        [Header("dropdown列表")]
        public Image dropdownGrid;
        [Header("dropdownItem模板")]
        public Button dropdownItem;
        [Header("获取dropdownItem模板大小")]
        public RectTransform dropdownItemRT;
        [Header("背景按钮隐藏")]
        public Button hideBG;
        //下拉菜单集
        private List<Image> dropdownPanels = new List<Image>();
        //菜单数据
        private static List<IMoreDropdownInfo> allInfo;
        //记录点击位置顺序
        private int[] clickOrder = new int[10];
        private int orderIndex = 0;
        //是否显示
        private bool isShowFirstPanel = true;
        //选中按钮
        private List<Button> pointerButtonList = new List<Button>();
        //当前选中按钮数据
        private Button enterButton;
        private int enterButtonLevel;
        private IMoreDropdownInfo enterButtonInfo;
        //按钮选中颜色状态
        private enum ButtonColorState
        {
            Normal,
            Enter,
            Exit,
            Click,
        }
        //多下拉菜单辅助Action
        public Action onCreateDropdown;

        void Awake()
        {
            //下拉菜单
            mainButton.onClick.AddListener(delegate ()
            {
                if (isShowFirstPanel)
                {
                    isShowFirstPanel = false;
                    hideBG.gameObject.SetActive(true);
                    //置于顶部
                    transform.SetAsLastSibling();
                    //开始创建列表
                    onCreateDropdown?.Invoke();
                    CreateDropdown(0, allInfo);
                }
                else
                {
                    HideFirstPanel();
                }
            });

            //背景全部隐藏
            hideBG.onClick.AddListener(delegate ()
            {
                HideFirstPanel();
            });
        }

        /// <summary>
        /// 创建下拉菜单
        /// </summary>
        /// <param name="level">第几级菜单</param>
        private void CreateDropdown(int level, List<IMoreDropdownInfo> infoList)
        {
            Image dropdown = Instantiate(dropdownPanel);
            dropdownPanels.Add(dropdown);
            dropdown.transform.parent = dropdownGrid.transform;
            dropdown.transform.localScale = new Vector3(1f, 1f, 1f);
            dropdown.gameObject.SetActive(true);

            dropdownGrid.gameObject.SetActive(true);

            for (int k = 0; k < infoList.Count; k++)
            {
                //二级及以上的第一位不显示(填充到了前一级的位置)
                if ((level > 0) && (k == 0))
                    continue;

                IMoreDropdownInfo info = infoList[k];
                Button cloneButton = Instantiate(dropdownItem);
                cloneButton.transform.parent = dropdown.transform;
                cloneButton.transform.localScale = new Vector3(1f, 1f, 1f);
                Image dropdownButton = cloneButton.GetComponent<Image>();
                Text dropdownText = cloneButton.transform.Find("dropdownText").GetComponent<Text>();
                Image dropdownArrow = cloneButton.transform.Find("dropdownArrow").GetComponent<Image>();

                //创建时 选中按钮默认记录第一个
                if (k == 1)
                    pointerButtonList.Add(cloneButton);
                //判断是否有下一级
                if (info.str != null)
                {
                    dropdownArrow.gameObject.SetActive(false);
                    dropdownText.text = info.str;
                }
                else
                {
                    dropdownArrow.gameObject.SetActive(true);
                    dropdownText.text = info.list[0].str;
                }

                //处理选中状态
                cloneButton.gameObject.SetActive(true);
                MCsUIListener listener = MCsUIListener.Get(cloneButton.gameObject);
                listener.onEnter = (go, eventData) =>
                {
                    SetButtonState(cloneButton, ButtonColorState.Enter);
                    enterButton = cloneButton;
                    enterButtonLevel = level;
                    enterButtonInfo = info;
                };
          //这是项目封装的代码 可以继承IPointerClickHandler接口
                listener.onExit = (go, eventData) =>
                {
                    SetButtonState(cloneButton, ButtonColorState.Exit);
                    enterButton = null;
                    enterButtonLevel = -1;
                    enterButtonInfo = null;
                };

                listener.onUp = (go, eventData) =>
                {
                    if (enterButton != null)
                        OnSelectDropdownItem();
                };
            }
        }

        /// <summary>
        /// 移除第几级及后的菜单
        /// </summary>
        /// <param name="level"></param>
        private void RemovePanelItems(int level)
        {
            //判断是否不是第一级
            if (level < dropdownPanels.Count)
            {
                //点击级之后的全部清除
                for (int k = dropdownPanels.Count - 1; k >= level; k--)
                {
                    for (int kk = dropdownPanels[k].transform.childCount - 1; kk >= 0; kk--)
                    {
                        Destroy(dropdownPanels[k].transform.GetChild(kk).gameObject);
                    }
                    //清除背景
                    Destroy(dropdownPanels[k].gameObject);
                    dropdownPanels.RemoveAt(k);
                    //保护防止越界
                    orderIndex = orderIndex >= 0 ? orderIndex : 0;
                    //清除位置
                    clickOrder[orderIndex] = 0;
                    orderIndex--;
                    //清除记录按钮
                    pointerButtonList.RemoveAt(k);
                }
            }
        }

        //设置按钮颜色状态
        private void SetButtonState(Button button, ButtonColorState state)
        {
            Text buttonText = button.transform.Find("dropdownText").GetComponent<Text>();
            Image buttonArrow = button.transform.Find("dropdownArrow").GetComponent<Image>();
            if (state == ButtonColorState.Normal)
            {
                buttonText.color = new Color((111f / 256f), (111f / 256f), (111f / 256f), 1f);
                buttonArrow.color = new Color((111f / 256f), (111f / 256f), (111f / 256f), 1f);
            }
            else if (state == ButtonColorState.Enter)
            {
                Color oldColor = buttonText.color;
                buttonText.color = new Color(oldColor.r, oldColor.g, oldColor.b, (120f / 256f));
                buttonArrow.color = new Color(oldColor.r, oldColor.g, oldColor.b, (120f / 256f));
            }
            else if (state == ButtonColorState.Exit)
            {
                Color oldColor = buttonText.color;
                buttonText.color = new Color(oldColor.r, oldColor.g, oldColor.b, 1f);
                buttonArrow.color = new Color(oldColor.r, oldColor.g, oldColor.b, 1f);
            }
            else if (state == ButtonColorState.Click)
            {
                buttonText.color = new Color((84f / 256f), (145f / 256f), (220f / 256f), 1f);
                buttonArrow.color = new Color((84f / 256f), (145f / 256f), (220f / 256f), 1f);
            }
        }

        //执行最终选中按钮数据处理
        private void OnSelectDropdownItem()
        {
            //记录点击位置
            clickOrder[enterButtonLevel] = enterButtonInfo.index;
            orderIndex = enterButtonLevel + 1;
            if (enterButtonInfo.str != null)
            {
                ChangeMainText(enterButtonInfo.str);
            }
            else
            {
                List<IMoreDropdownInfo> nextList = enterButtonInfo.list;
                nextList = enterButtonInfo.list;
                RemovePanelItems(enterButtonLevel + 1);
                CreateDropdown(enterButtonLevel + 1, nextList);
                //处理选中按钮状态
                if (pointerButtonList.Count >= enterButtonLevel)
                    SetButtonState(pointerButtonList[enterButtonLevel], ButtonColorState.Normal);
                pointerButtonList[enterButtonLevel] = enterButton;
                SetButtonState(enterButton, ButtonColorState.Click);
            }
        }

        //隐藏
        private void HideFirstPanel()
        {
            isShowFirstPanel = true;
            hideBG.gameObject.SetActive(false);
            RemovePanelItems(0);
            orderIndex = 0;
        }

        //显示
        private void ShowFirstPanel()
        {
            isShowFirstPanel = true;
            dropdownGrid.gameObject.SetActive(true);
        }

        //选好收回的Action回调
        public Action<String, String> onClickItem;
        //设置主按钮的文字
        private void ChangeMainText(String str)
        {
            Text firstText = mainButton.transform.Find("mainText").GetComponent<Text>();
            firstText.text = str;
            //生成返回字符串
            string orderStr = "";
            for (int i=0; i<orderIndex; i++)
            {
                if (i == 0)
                {
                    orderStr += (clickOrder[i]+1);
                }
                else
                {
                    orderStr += "|" + clickOrder[i];
                }
            }
            //隐藏所有并清空所有临时数据
            HideFirstPanel();
            //回调
            onClickItem?.Invoke(str, orderStr);
        }

        //传入值
        public static void SetAllInfo(List<IMoreDropdownInfo> _allInfo)
        {
            allInfo = _allInfo;
        }
    }

按钮数据类

//按钮数据
    public class IMoreDropdownInfo
    {
        //记录位置
        public int index;
        //字符串或list
        public string str;
        public List<IMoreDropdownInfo> list;

        public IMoreDropdownInfo(String _str) { str = _str; }
        public IMoreDropdownInfo(List<IMoreDropdownInfo> _list) { list = _list; }
    }

按钮数据生成类

//按钮数据处理逻辑
    public class MoreDropdownItem
    {
        //创建一个独立按钮
        public static IMoreDropdownInfo CreateInfo(String str)
        {
            return new IMoreDropdownInfo(str);
        }

        //创建一个菜单
        public static List<IMoreDropdownInfo> CreateList()
        {
            List<IMoreDropdownInfo> _list = new List<IMoreDropdownInfo>();
            return _list;
        }

        //独立按钮添加到菜单中
        public static List<IMoreDropdownInfo> AddInfo(List<IMoreDropdownInfo> _list, IMoreDropdownInfo _info)
        {
            _info.index = _list.Count;
            _list.Add(_info);
            return _list;
        }

        //子级菜单添加到菜单中
        public static List<IMoreDropdownInfo> AddInfo(List<IMoreDropdownInfo> _list, List<IMoreDropdownInfo> _info)
        {
            IMoreDropdownInfo info = new IMoreDropdownInfo(_info);
            info.index = _list.Count;
            _list.Add(info);
            return _list;
        }

    }

Lua层数据处理

显示数据

local textTable = {
    "全部",
    {
        "这是1",
        "这是2",
        "这是3",
        "这是4",
    },
    {
        "这是另一个1",
        "这是另一个2",
        "这是另一个3",
        "这是另一个4",
    },
}

生成菜单数据

function CreateDropdownStrTable(data)
    local itemList = MoreDropdownItem.CreateList()
    for i, v in ipairs(data) do
        if type(v) == "table" then
            local list = CreateDropdownStrTable(v)
            itemList = MoreDropdownItem.AddInfo(itemList, list)
        elseif type(v) == "string" then
            local info = MoreDropdownItem.CreateInfo(v)
            itemList = MoreDropdownItem.AddInfo(itemList, info)
        end
    end

    return itemList
end

实际使用

moreDropdown.onCreateDropdown = function()
    local strData = CreateDropdownStrTable(strTable)
    moreDropdown.SetAllInfo(strData)
end
moreDropdown.onClickItem = function(str, orderStr)
    --TODO
end

prefab
https://pan.baidu.com/s/1aEZtUdVPJAYk9oIn-zgxPg 提取码:gg9o

  • 13
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
Unity多级菜单栏是一种通过使用锚点、轴心、ContentSizeFitter、Vertical Layout Group和协程等组件和技术来实现的。具体的步骤如下: 1. 首先,创建一个父物体,用来容纳多级菜单栏的所有子物体。 2. 在父物体上添加Vertical Layout Group组件,将子物体按照垂直方向排列。 3. 在父物体上添加ContentSizeFitter组件,确保父物体的大小能够适应子物体的大小。 4. 创建子物体列表,每个子物体代表一个菜单项。如果菜单有多行,可以将子物体的布局组件从GridLayoutGroup换成Horizontal Layout Group。 5. 在子物体上添加按钮组件,并设置按钮的文本和点击事件。 6. 使用协程来控制菜单的展开和收起。可以通过设置子物体的活动状态来控制菜单的显示和隐藏。 参考资料:[2]<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Unity:UI自动布局与多级菜单](https://blog.csdn.net/qq_42987967/article/details/125451721)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Unity使用UGUI设置一个简单多级水平方向下拉菜单(不需要代码)](https://blog.csdn.net/qq_37524903/article/details/125507576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值