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