Unity 多级下拉菜单

 Unity自带的Dropdown只能出现一级下拉菜单 在尝试修改之后 无法实现 索性自己写了一个

效果如下

组件结构

 

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

菜单 dropdownpanel 放置多个按钮Item

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

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

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

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

代码

界面部分

  1 public class MoreDropdown : MonoBehaviour
  2     {
  3         [Header("主按钮")]
  4         public Button mainButton;
  5         [Header("dropdownPanel模板")]
  6         public Image dropdownPanel;
  7         [Header("dropdown列表")]
  8         public Image dropdownGrid;
  9         [Header("dropdownItem模板")]
 10         public Button dropdownItem;
 11         [Header("获取dropdownItem模板大小")]
 12         public RectTransform dropdownItemRT;
 13         [Header("背景按钮隐藏")]
 14         public Button hideBG;
 15         //下拉菜单集
 16         private List<Image> dropdownPanels = new List<Image>();
 17         //菜单数据
 18         private static List<IMoreDropdownInfo> allInfo;
 19         //记录点击位置顺序
 20         private int[] clickOrder = new int[10];
 21         private int orderIndex = 0;
 22         //是否显示
 23         private bool isShowFirstPanel = true;
 24         //选中按钮
 25         private List<Button> pointerButtonList = new List<Button>();
 26         //当前选中按钮数据
 27         private Button enterButton;
 28         private int enterButtonLevel;
 29         private IMoreDropdownInfo enterButtonInfo;
 30         //按钮选中颜色状态
 31         private enum ButtonColorState
 32         {
 33             Normal,
 34             Enter,
 35             Exit,
 36             Click,
 37         }
 38         //多下拉菜单辅助Action
 39         public Action onCreateDropdown;
 40 
 41         void Awake()
 42         {
 43             //下拉菜单
 44             mainButton.onClick.AddListener(delegate ()
 45             {
 46                 if (isShowFirstPanel)
 47                 {
 48                     isShowFirstPanel = false;
 49                     hideBG.gameObject.SetActive(true);
 50                     //置于顶部
 51                     transform.SetAsLastSibling();
 52                     //开始创建列表
 53                     onCreateDropdown?.Invoke();
 54                     CreateDropdown(0, allInfo);
 55                 }
 56                 else
 57                 {
 58                     HideFirstPanel();
 59                 }
 60             });
 61 
 62             //背景全部隐藏
 63             hideBG.onClick.AddListener(delegate ()
 64             {
 65                 HideFirstPanel();
 66             });
 67         }
 68 
 69         /// <summary>
 70         /// 创建下拉菜单
 71         /// </summary>
 72         /// <param name="level">第几级菜单</param>
 73         private void CreateDropdown(int level, List<IMoreDropdownInfo> infoList)
 74         {
 75             Image dropdown = Instantiate(dropdownPanel);
 76             dropdownPanels.Add(dropdown);
 77             dropdown.transform.parent = dropdownGrid.transform;
 78             dropdown.transform.localScale = new Vector3(1f, 1f, 1f);
 79             dropdown.gameObject.SetActive(true);
 80 
 81             dropdownGrid.gameObject.SetActive(true);
 82 
 83             for (int k = 0; k < infoList.Count; k++)
 84             {
 85                 //二级及以上的第一位不显示(填充到了前一级的位置)
 86                 if ((level > 0) && (k == 0))
 87                     continue;
 88 
 89                 IMoreDropdownInfo info = infoList[k];
 90                 Button cloneButton = Instantiate(dropdownItem);
 91                 cloneButton.transform.parent = dropdown.transform;
 92                 cloneButton.transform.localScale = new Vector3(1f, 1f, 1f);
 93                 Image dropdownButton = cloneButton.GetComponent<Image>();
 94                 Text dropdownText = cloneButton.transform.Find("dropdownText").GetComponent<Text>();
 95                 Image dropdownArrow = cloneButton.transform.Find("dropdownArrow").GetComponent<Image>();
 96 
 97                 //创建时 选中按钮默认记录第一个
 98                 if (k == 1)
 99                     pointerButtonList.Add(cloneButton);
100                 //判断是否有下一级
101                 if (info.str != null)
102                 {
103                     dropdownArrow.gameObject.SetActive(false);
104                     dropdownText.text = info.str;
105                 }
106                 else
107                 {
108                     dropdownArrow.gameObject.SetActive(true);
109                     dropdownText.text = info.list[0].str;
110                 }
111 
112                 //处理选中状态
113                 cloneButton.gameObject.SetActive(true);
114                 MCsUIListener listener = MCsUIListener.Get(cloneButton.gameObject);
115                 listener.onEnter = (go, eventData) =>
116                 {
117                     SetButtonState(cloneButton, ButtonColorState.Enter);
118                     enterButton = cloneButton;
119                     enterButtonLevel = level;
120                     enterButtonInfo = info;
121                 };
122           //这是项目封装的代码 可以继承IPointerClickHandler接口 123 listener.onExit = (go, eventData) => 124 { 125 SetButtonState(cloneButton, ButtonColorState.Exit); 126 enterButton = null; 127 enterButtonLevel = -1; 128 enterButtonInfo = null; 129 }; 130 131 listener.onUp = (go, eventData) => 132 { 133 if (enterButton != null) 134 OnSelectDropdownItem(); 135 }; 136 } 137 } 138 139 /// <summary> 140 /// 移除第几级及后的菜单 141 /// </summary> 142 /// <param name="level"></param> 143 private void RemovePanelItems(int level) 144 { 145 //判断是否不是第一级 146 if (level < dropdownPanels.Count) 147 { 148 //点击级之后的全部清除 149 for (int k = dropdownPanels.Count - 1; k >= level; k--) 150 { 151 for (int kk = dropdownPanels[k].transform.childCount - 1; kk >= 0; kk--) 152 { 153 Destroy(dropdownPanels[k].transform.GetChild(kk).gameObject); 154 } 155 //清除背景 156 Destroy(dropdownPanels[k].gameObject); 157 dropdownPanels.RemoveAt(k); 158 //保护防止越界 159 orderIndex = orderIndex >= 0 ? orderIndex : 0; 160 //清除位置 161 clickOrder[orderIndex] = 0; 162 orderIndex--; 163 //清除记录按钮 164 pointerButtonList.RemoveAt(k); 165 } 166 } 167 } 168 169 //设置按钮颜色状态 170 private void SetButtonState(Button button, ButtonColorState state) 171 { 172 Text buttonText = button.transform.Find("dropdownText").GetComponent<Text>(); 173 Image buttonArrow = button.transform.Find("dropdownArrow").GetComponent<Image>(); 174 if (state == ButtonColorState.Normal) 175 { 176 buttonText.color = new Color((111f / 256f), (111f / 256f), (111f / 256f), 1f); 177 buttonArrow.color = new Color((111f / 256f), (111f / 256f), (111f / 256f), 1f); 178 } 179 else if (state == ButtonColorState.Enter) 180 { 181 Color oldColor = buttonText.color; 182 buttonText.color = new Color(oldColor.r, oldColor.g, oldColor.b, (120f / 256f)); 183 buttonArrow.color = new Color(oldColor.r, oldColor.g, oldColor.b, (120f / 256f)); 184 } 185 else if (state == ButtonColorState.Exit) 186 { 187 Color oldColor = buttonText.color; 188 buttonText.color = new Color(oldColor.r, oldColor.g, oldColor.b, 1f); 189 buttonArrow.color = new Color(oldColor.r, oldColor.g, oldColor.b, 1f); 190 } 191 else if (state == ButtonColorState.Click) 192 { 193 buttonText.color = new Color((84f / 256f), (145f / 256f), (220f / 256f), 1f); 194 buttonArrow.color = new Color((84f / 256f), (145f / 256f), (220f / 256f), 1f); 195 } 196 } 197 198 //执行最终选中按钮数据处理 199 private void OnSelectDropdownItem() 200 { 201 //记录点击位置 202 clickOrder[enterButtonLevel] = enterButtonInfo.index; 203 orderIndex = enterButtonLevel + 1; 204 if (enterButtonInfo.str != null) 205 { 206 ChangeMainText(enterButtonInfo.str); 207 } 208 else 209 { 210 List<IMoreDropdownInfo> nextList = enterButtonInfo.list; 211 nextList = enterButtonInfo.list; 212 RemovePanelItems(enterButtonLevel + 1); 213 CreateDropdown(enterButtonLevel + 1, nextList); 214 //处理选中按钮状态 215 if (pointerButtonList.Count >= enterButtonLevel) 216 SetButtonState(pointerButtonList[enterButtonLevel], ButtonColorState.Normal); 217 pointerButtonList[enterButtonLevel] = enterButton; 218 SetButtonState(enterButton, ButtonColorState.Click); 219 } 220 } 221 222 //隐藏 223 private void HideFirstPanel() 224 { 225 isShowFirstPanel = true; 226 hideBG.gameObject.SetActive(false); 227 RemovePanelItems(0); 228 orderIndex = 0; 229 } 230 231 //显示 232 private void ShowFirstPanel() 233 { 234 isShowFirstPanel = true; 235 dropdownGrid.gameObject.SetActive(true); 236 } 237 238 //选好收回的Action回调 239 public Action<String, String> onClickItem; 240 //设置主按钮的文字 241 private void ChangeMainText(String str) 242 { 243 Text firstText = mainButton.transform.Find("mainText").GetComponent<Text>(); 244 firstText.text = str; 245 //生成返回字符串 246 string orderStr = ""; 247 for (int i=0; i<orderIndex; i++) 248 { 249 if (i == 0) 250 { 251 orderStr += (clickOrder[i]+1); 252 } 253 else 254 { 255 orderStr += "|" + clickOrder[i]; 256 } 257 } 258 //隐藏所有并清空所有临时数据 259 HideFirstPanel(); 260 //回调 261 onClickItem?.Invoke(str, orderStr); 262 } 263 264 //传入值 265 public static void SetAllInfo(List<IMoreDropdownInfo> _allInfo) 266 { 267 allInfo = _allInfo; 268 } 269 }

 按钮数据类

//按钮数据
    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/1Ydt6goicLsN4jyPYwL7KUg

 

posted @ 2018-11-12 11:31  萧熊猫  阅读(3143)  评论(1编辑  收藏  举报