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