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