Unity3D UGUI Scroll View 二级滚动菜单

前言

如果有更好的写法或是代码有什么错误等等,还请大佬教教我。

最终效果

大概就这么个效果,使用的Scroll View做的,我使用的是中文版,看不懂英文,没得法

准备

预制件(各预制件结构在文中会有,顺着看就好):

 脚本:

一、创建Scroll View对象

二、修改Scroll View对象中的组件属性,并挂载MenuData脚本

将Scroll View改名为MenuView

MenuView对象结构:

创建Menus.cs脚本文件,脚本内容为:

using System.Collections.Generic;

namespace Assets.Scripts.Models
{
    public class Menus
    {
        public int Id { set; get; }
        public string MenuName { set; get; }
        public List<MenuChilds> MenuChilds { set; get; }
    }
    public class MenuChilds
    {
        public int Id { set; get; }
        public string MenuChildName { set; get; }
    }
}

创建MenuData.cs脚本文件并挂载,脚本内容(此脚本相当于菜单的数据源):

using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MenuData : MonoBehaviour
{
    public List<Menus> menus = new List<Menus>();
    public ContentData contentData;

    void Start()
    {
        #region 菜单列表数据,后续替换为从数据库查询
        for (int i = 0; i < 8; i++)
        {
            List<MenuChilds> MenuChilds = new List<MenuChilds>();
            for (int j = 0; j < 7; j++)
            {
                MenuChilds.Add(new MenuChilds()
                {
                    Id = j + 1,
                    MenuChildName = $"第{j + 1}个"
                });
            }
            Menus Menu = new Menus()
            {
                Id = i + 1,
                MenuName = $"第{i + 1}个菜单",
                MenuChilds = MenuChilds
            };
            menus.Add(Menu);
        } 
        #endregion

        //将菜单列表数据传给Content对象
        contentData.SetMenu(menus);
    }

}

这个感觉有点多余,但还是把这个脚本放在了最外层,或许什么时候就用到了也说不定

三、修改MenuView的子对象Content对象的组件属性

 

Content Size Fitter和Vertical Layout Group这两个组件需要点击添加组件自己添加

创建并挂载ContentData.cs脚本文件,脚本内容:

(此脚本用于创建一级菜单,并提供菜单无参委托事件,方便在点击之前和点击之后做一些操作)

using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ContentData : MonoBehaviour
{
    private Transform oldTransform; //上一个点击的按钮

    public delegate void BtnClickDelegate();

    /// <summary>
    /// 一级菜单列表
    /// </summary>
    public List<Menus> Menus;

    /// <summary>
    /// 一级菜单预制件
    /// </summary>
    public GameObject menuPrefab;

    /// <summary>
    /// 添加一级菜单
    /// </summary>
    /// <param name="menus"></param>
    public void SetMenu(List<Menus> menus)
    {
        Menus = menus;
        for (int i = 0; i < Menus.Count; i++)
        {
            GameObject towMenuBtnObj = Instantiate(menuPrefab, transform);
            towMenuBtnObj.GetComponent<OneMenuBtn>().SetMenuData(Menus[i]);
        }
    }

    #region 一级菜单点击事件(委托)
    public void BtnClick(BtnClickDelegate btnClickDelegate, Transform newTransform)
    {
        bool equals = false;
        if (oldTransform != null)
        {
            equals = oldTransform.Equals(newTransform) || newTransform == null;

            if (!equals)
            {
                //关闭上次点击的菜单
                oldTransform.GetComponent<OneMenuBtn>().OnBtnClick();
            }
        }

        oldTransform = newTransform;

        //执行委托方法,由子对象的OneMenuBtn脚本调用
        if (newTransform != null)
        {
            btnClickDelegate?.Invoke();

            if (equals)
            {
                RemoveOldTransform();
            }
        }
    }
    #endregion

    #region 关闭菜单
    /// <summary>
    /// 关闭菜单
    /// </summary>
    public void RemoveOldTransform()
    {
        BtnClick(null, null);
    }
    #endregion
}

 四、创建一级菜单预制件,并修改对象组件的属性

 一级菜单预制件结构

一级菜单预制件属性

创建并挂载OneMenuBtn.cs脚本文件,脚本内容:

using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class OneMenuBtn : MonoBehaviour
{
    private ChildContentData childContentData; //ChildContent对象的ChildContentData脚本组件
    private Image image; //按钮图片组件
    private Text text; //按钮文本组件
    private bool OnOff = false; //是否已展开
    private RectTransform rectParent;
    private RectTransform childContentRect; //ChildContent对象的RectTransform组件
    private GridLayoutGroup gridLayout; //ChildContent对象的GridLayoutGroup组件
    private float OldHeight; //展开前高度
    private float Height; //展开后高度

    public string MenuName;

    public Color BtnColorOpen;
    public Color TextColorOpen;

    public Color BtnColorColse;
    public Color TextColorColse;

    /// <summary>
    /// 初始化
    /// </summary>
    void Awake()
    {
        image = transform.GetChild(0).GetComponent<Image>();
        text = transform.GetChild(0).GetChild(0).GetComponent<Text>();

        rectParent = transform.GetComponent<RectTransform>();
        childContentRect = transform.GetChild(2).GetComponent<RectTransform>();
        gridLayout = transform.GetChild(2).GetComponent<GridLayoutGroup>();
    }

    /// <summary>
    /// 初始化
    /// </summary>
    void Start()
    {
        OldHeight = rectParent.rect.height;

        if (childContentRect.childCount > 0)
        {
            RectTransform rectTransformChild = childContentRect.GetChild(childContentRect.childCount - 1).GetComponent<RectTransform>();

            //计算出展开后的高度
            Height = (childContentRect.childCount / 2 + childContentRect.childCount % 2) * rectTransformChild.rect.height + gridLayout.padding.top + rectTransformChild.rect.height / 2 + gridLayout.spacing.y;
            
            if (Height < OldHeight)
            {
                Height = OldHeight;
            }
        }

    }

    /// <summary>
    /// 设置当前菜单按钮的属性并将二级菜单列表传给子对象中的ChildContent对象
    /// </summary>
    /// <param name="Menu"></param>
    public void SetMenuData(Menus Menu)
    {
        MenuName = Menu.MenuName;
        text.text = MenuName;
        gameObject.name = MenuName;

        //transform.GetChild(2)为ChildContent对象
        childContentData = transform.GetChild(2).GetComponent<ChildContentData>();
        childContentData.SetMenuChilds(Menu.MenuChilds);
    }

    /// <summary>
    /// 菜单点击触发按钮
    /// </summary>
    public void Press()
    {
        ContentData contentData = transform.parent.GetComponent<ContentData>();

        //委托方法
        contentData.BtnClick(OnBtnClick, transform);
    }

    /// <summary>
    /// 按钮委托事件
    /// </summary>
    public void OnBtnClick()
    {
        OnOff = !OnOff;
        if (OnOff)
        {
            //展开菜单执行的方法。。。。
            image.color = BtnColorOpen;
            text.color = TextColorOpen;

            StartCoroutine(MenuCoroutine(OldHeight));

            Debug.Log("点击" + MenuName);
            childContentData.SetObjActive(OnOff);
        }
        else
        {
            //收缩菜单执行的方法。。。。
            image.color = BtnColorColse;
            text.color = TextColorColse;

            childContentData.RemoveOldTransform(); //关闭二级菜单选中效果
            childContentData.SetObjActive(OnOff);
            StartCoroutine(MenuCoroutine(Height));
        }
    }

    #region 开关携程(实现伸缩动画效果)
    public IEnumerator MenuCoroutine(float nowHeight)
    {
        while (true)
        {
            if (OnOff)
            {
                nowHeight = Mathf.Clamp(nowHeight + Time.deltaTime * 500, OldHeight, Height);
                rectParent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, nowHeight);
            }
            else
            {
                nowHeight = Mathf.Clamp(nowHeight - Time.deltaTime * 500, OldHeight, Height);
                rectParent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, nowHeight);
            }

            if (nowHeight == Height || nowHeight == OldHeight)
            {
                break;
            }
            yield return null;
        }
    }
    #endregion
}

NameImage对象属性

 MenuName对象属性

 BottonImage属性

这个图片自己整吧,这些属性都可以自己去调整成自己需要的样子

ChildContent对象属性

Grid Layout Group组件需要自己添加,在本例里面这里的约束计数为2代表有两列

创建并挂载ChildContentData.cs脚本文件,脚本内容:

using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ChildContentData : MonoBehaviour
{
    private Transform oldTransform; //上一个点击的按钮

    public delegate void BtnClickDelegate();

    /// <summary>
    /// 二级菜单预制件
    /// </summary>
    public GameObject childPrefab;

    /// <summary>
    /// 二级菜单列表
    /// </summary>
    public List<MenuChilds> MenuChilds;

    /// <summary>
    /// 添加二级菜单
    /// </summary>
    /// <param name="menuChilds"></param>
    public void SetMenuChilds(List<MenuChilds> menuChilds)
    {
        MenuChilds = menuChilds;
        for (int i = 0; i < MenuChilds.Count; i++)
        {
            GameObject towMenuBtnObj = Instantiate(childPrefab, transform);
            towMenuBtnObj.GetComponent<TowMenuBtn>().SetMenuData(MenuChilds[i]);
        }
    }

    /// <summary>
    /// 此处作用为显示或隐藏二级菜单
    /// </summary>
    /// <param name="active"></param>
    public void SetObjActive(bool active)
    {
        gameObject.SetActive(active);
    }

    #region 二级菜单点击事件(委托)
    public void BtnClick(BtnClickDelegate btnClickDelegate, Transform newTransform)
    {
        bool equals = false;
        if (oldTransform != null)
        {
            equals = oldTransform.Equals(newTransform) || newTransform == null;

            if (!equals || newTransform == null)
            {
                //关闭上次点击的菜单
                oldTransform.GetComponent<TowMenuBtn>().OnBtnClick();
            }
        }

        oldTransform = newTransform;

        //执行委托方法,由子对象的TowMenuBtn脚本调用
        if (newTransform != null)
        {
            btnClickDelegate?.Invoke();

            if (equals)
            {
                RemoveOldTransform();
            }
        }
    }
    #endregion

    #region 关闭菜单
    /// <summary>
    /// 关闭菜单
    /// </summary>
    public void RemoveOldTransform()
    {
        BtnClick(null, null);
    } 
    #endregion

}

五、创建二级菜单预制件,并修改对象组件的属性

二级菜单预制件结构

二级菜单预制件属性

创建并挂载TowMenuBtn.cs脚本文件,脚本内容:

using Assets.Scripts.Models;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TowMenuBtn : MonoBehaviour
{
    private bool OnOff = false;//是否点击
    private Image image; //按钮图片组件
    private Text text; //按钮文本组件
    private string TowMenuName; //按钮名称
    public MenuChilds MenuChild; //按钮名称

    public Color BtnColorOpen;
    public Color TextColorOpen;

    public Color BtnColorColse;
    public Color TextColorColse;

    /// <summary>
    /// 设置当前菜单按钮的属性
    /// </summary>
    /// <param name="MenuChild"></param>
    public void SetMenuData(MenuChilds MenuChild)
    {
        if (text == null)
            text = transform.GetChild(0).GetComponent<Text>();
        if (image == null)
            image = transform.GetComponent<Image>();

        this.MenuChild = MenuChild;
        text.text = MenuChild.MenuChildName;
        TowMenuName = MenuChild.MenuChildName;
        gameObject.name = MenuChild.MenuChildName;
    }

    /// <summary>
    /// 菜单点击触发按钮
    /// </summary>
    public void Press()
    {
        ChildContentData childContentData = transform.parent.GetComponent<ChildContentData>();

        //委托方法
        childContentData.BtnClick(OnBtnClick, transform);
    }

    /// <summary>
    /// 按钮委托事件
    /// </summary>
    public void OnBtnClick()
    {
        OnOff = !OnOff;
        if (OnOff)
        {
            //点击菜单后执行的方法。。。。
            image.color = BtnColorOpen;
            text.color = TextColorOpen;

            Debug.Log("这里是" + TowMenuName);
        }
        else
        {
            image.color = BtnColorColse;
            text.color = TextColorColse;
        }
    }
}

Text对象属性

文中一些GameObject的组件属性都可以自行修改成你想要的效果

完,如果本文对你有些帮助,麻烦点个赞支持一下,感激不尽~

  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊脑袋_YA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值