列记录下自己经常使用的这个UI框架。首先说下这个UI框架整体吧,该框架主要实现了UI的的显示、隐藏、按钮点击、UI数值更新,这也是大多数游戏UI的功能。

    该框架主要分三个部分来理解,分别是窗口(window)、视图(view)、控制(control),看起来有点像mvc框架是吧,但这里并没有实现model数据这块,现在只是实现UI的一些显示功能,并不需要做Model数据处理内容,所以暂时不管。如果加上model模块,那么窗口和视图可以合成View的。

    细说三部分

    1、窗口(window):为了将UI分类,将其分成相应的窗口理解,如:商店窗口、设置窗口等;
    2、视图(view):每个窗口内的具体UI视图,一个窗口可有多个UI视图,如:商店窗口有显示所有商品的视图、然后选择单个商品时又可以弹出一个商品详情视图,点击购买又可以弹出一个购买视图。当然,把这些都放在同一视图也行,但是就是显得脚本程序臃肿了,毕竟分类管理能让程序更直观清晰。
    3、控制(control):用来实现UI的创建、记录、显示和隐藏等功能的具体逻辑。

代码实现

    根据上面三部分可以创建三个类UIBaseWindow(窗口)、UIBaseView(视图)、UIWindowCtrl(控制);
    详细如下图:
UI框架(二)之美_知识分享

1、首先我们看看UIBaseWindow类下包含四个主要方法,由命名可以看出其功能分别是窗口的创建、显示、隐藏、重现;下面几个子类则可以直接继承这些功能并使用,这几个子类分别是主界面、商城、设置等窗口。子类的作用主要是用来初始化对应显示的视图。

2、下面看看UIBaseView类,该类的作用则是包含了具体的UI视图了,该基类也自带了几个非常重要的方法,包括Veiw的初始化、获取当前View所属窗口、设置View所属窗口、View的显示和隐藏,而继承该基类的子类除了显示具体的UI外,还扩展了按钮的点击事件;从上图看到多了一个叫CommonView的通用视图类,因为我们的View UI通常都是共用一个父窗口的,就是外部轮廓一致的,只有标题不同而已,而且窗口都会带有一个关闭按钮的,那么我们就可以把这些提取出来做成一个通用的CommonView视图了。

3、最后一个UIWindowCtrl类,顾名思义就是UI总控制类,包括窗口的具体创建逻辑、已有窗口的存储、已有窗口重现的判断逻辑等功能都是写在这个类。该类是个管理类,不允许继承的。
 

首先是窗口类UIBaseWindow:

public class UIBaseWindow {

    public GameObject winParent = null;

    public virtual void OnCreate(string winName)
    {
        if (winParent == null){
        // GameSettings.GetInstance().UIWin是一个对象,可自定义,也可以直接使用CanVas,反正是用来存放UI的
            winParent = ResMgr.CreateEmptyGameObject(GameSettings.GetInstance().UIWin, winName);
        }
    }

    // 在当前窗口切换其他窗口
    public void StartWindow(System.Type windowName)
    {
        UIWindowCtrl.GetInstance().ShowWindow(windowName);
    }

    // 重现当前窗口
    public virtual void OnResume()
    {
        winParent.SetActive(true);
    }

    // 隐藏当前窗口
    public virtual void OnStop()
    {
        winParent.SetActive(false);
    }
}

该基类主要封装了几个方法,都很简单,主要说下几点。
(1)OnCreate(string winName)方法创建一个空对象,是用来存放该窗口的后续所有子对象,其中用到调用的方法是我封装的一个方法。

 public static GameObject CreateEmptyGameObject(GameObject parent, string name)
    {
        GameObject obj = new GameObject(name);
        obj.transform.SetParent(parent.transform);
        obj.transform.localPosition = Vector3.zero;
        obj.transform.localEulerAngles = Vector3.zero;
        obj.transform.localScale = Vector3.one;
        return obj;
    }

2)StartWindow(System.Type windowName)方法是启动一个窗口,参数是一个窗口类名的类型,如:typeof(Win_Shop);至于调用的UIWindowCtrl类方法将在后面说明。

下面我们实现一个继承于基类的具体窗口类Win_Main。

public class Win_Main : UIBaseWindow {

    private MainView mMainView;

    public override void OnCreate(string winName)
    {
        base.OnCreate(winName);
        mMainView = new MainView(this, "Prefas/UI/UIMain/MainView");
    }

    public override void OnResume()
    {
        base.OnResume();
    }
}

好了,现在我们再来梳理下这个UI框架,以上面代码为例;比如我要显示一个主界面Main,这个界面主要包含一些背景图和、商城、设置、帮助、等按钮,这些都做成一个预设MainView,然后从上面代码看到一个同名类MainView,该类实现了预设的各种功能。所以当我要OnCreate()一个窗口时就会调用这个MainVeiw预设界面了。
 

 

下面继续实现第二个基类UIBaseView。

abstract public class UIBaseView {

    public GameObject view;
    private UIBaseWindow _window;
    public UIBaseView(UIBaseWindow parent,string path)
    {
        _window = parent;
        view = ResMgr.InitLoadPrefabsInParent(parent.winParent, path);
        Start();
    }

    public abstract void Start();

    // 获取当前View所属Window
    public virtual UIBaseWindow GetWindow()
    {
        return _window;
    }

    // 设置当前View所属Window
    public virtual void SetWindow(UIBaseWindow window)
    {
        _window = window;
    }

    public virtual void Hide()
    {
        view.SetActive(false);
    }

    public virtual void Show()
    {
        view.SetActive(true);
    }
}

该类的一个实例MainView

public class MainView : UIBaseView {

    public MainView(UIBaseWindow window,string path)
        :base(window,path)
    {
    }

    public override void Start()
    {
        view.SetOnClickListener(OnButtonClick);
    }

    private void OnButtonClick(GameObject btn)
    {
        switch(btn.name)
        {
            case BUTTON.Btn_Set:
                // 调用当前View所属Window的StartWindow方法切换来窗口
                GetWindow().StartWindow(typeof(Win_Set));
                break;
            case BUTTON.Btn_Shop:
                GetWindow().StartWindow(typeof(Win_Shop));
                break;
        }
    }

    private struct BUTTON{
        public const string Btn_Set = "Btn_Set";
        public const string Btn_Shop = "Btn_Shop";
    }
}

上面有段按钮点击回调方法绑定的代码view.SetOnClickListener(OnButtonClick);方法SetOnClickListener其实是在其他脚本封装的一个点击事件绑定方法,还是个扩展方法。

    扩展方法
    (1)给某个类扩展一个方法出来;
    (2)该方法必须为static,参数一为类本身,必须带this关键字(this GameObject obj)


 

public static void SetOnClickListener(this GameObject obj,EventTriggerListener.methodDelegate method)
    {
        foreach (Button btn in obj.GetComponentsInChildren<Button>()) // 获取obj所有子物体的Button组件
        {
            EventTriggerListener.getListener(btn.gameObject).onClick = method;
        }
    }

上面的MainView实现了点击按钮显示Set、Shop窗口。

 

最后一个类,也是最重要的一个控制类,功能包括窗口的创建、显示、
隐藏、重现、删除等。

public class UIWindowCtrl {

    private UIBaseWindow currentWindow;
    private List<UIBaseWindow> mWindowList;

    private static UIWindowCtrl _instance;
    public static UIWindowCtrl GetInstance(){
        if (null == _instance)
        {
            _instance = new UIWindowCtrl();
        }
        return _instance;
    }

    private UIWindowCtrl()
    {
        mWindowList = new List<UIBaseWindow>();
    }

    // 当前显示的窗口
    public UIBaseWindow GetCurrentWindow()
    {
        return currentWindow;
    }

    // 显示一个窗口
    public void ShowWindow(System.Type type)
    {
        if (null != currentWindow && !(currentWindow.GetType().Equals(typeof(Win_Main))) )
        {
            // 如果是Win_Main主窗口不关闭,可以在其上加显示其他窗口
            // 否则先关闭当前窗口,再显示其他窗口
            if (!currentWindow.GetType().Equals(type))
            {
                WindowStop(currentWindow);
                currentWindow = null;
            }
        }
        UIBaseWindow window = WindowContains(type);
        if(null!=window){
            WindowResume(window);
        }else{
            CreateWindow(type);
        }
    }

    // 创建一个窗口实例存于窗口列表
    private void CreateWindow(Type type)
    {
        UIBaseWindow window;
        window = Activator.CreateInstance(type) as UIBaseWindow;
        mWindowList.Add(window);
        currentWindow = window;
        window.OnCreate(window.ToString());
        WindowResume(window);
    }

    // 窗口的重现
    public void WindowResume(UIBaseWindow window)
    {
        currentWindow = window;
        window.OnResume();
    }

    // 判断窗口是否已存在,避免重复创建
    private UIBaseWindow WindowContains(Type type)
    {
        foreach(UIBaseWindow window in mWindowList){

            if(type.Equals(window.GetType())){
                return window;
            }        
        }
        return null;
    }

    public void WindowStop(UIBaseWindow window)
    {
        window.OnStop();
    }

    public void ClearWindowData()
    {
        if(mWindowList.Count>0){
            mWindowList.Clear();
        }
        currentWindow = null;
    }
}