木骰

用栈来管理UI界面:ViewManager

上一个项目里我的工作主要在UI上,于是就写了这个ViewManager,以栈的形式来管理各个UI界面。
主要分为两个部分:一个界面管理器ViewManager和一个界面的的基类IView。考虑到切场景的情况,所以还会有一个切场景的管理器SceneManager。

栈元素结构
----------------------------------------------------------------------------------
一般的View可能会有 弹窗类型,顶掉上一个界面的类型以及是否能被返回这么几种类型。所以View应该要有一个ViewType来标记界面类型。
因为先前入栈的界面,它们的View实例在游戏中有可能会被销毁掉,比如因为跳转场景就销毁了当前场景下的所有View实例,所以栈中存放的应该是一个能够重新拿到View实例的关键字,而不是View实例本身。这里以一个name来作这个关键字比较合适。
那么原本存在view实例里的viewType这时候也应该存个备份到栈里了,这样view实例被销毁后ViewManager还能知道这个view对应的viewtype.
考虑到跳转场景的因素,所以栈里还要再存一个场景标记,就用场景名好了
那么就建了一个栈元素的结构体。


public struct ViewInfo
{
    public ViewInfo(string scene,string name,ViewType viewtype)
    {
        this.scene = scene;
        this.name = name;
        this.viewType = viewtype;
    }
    public string scene;
    public string name;
    public ViewType viewType;
}

跳转界面JumpTo
--------------------------------------------------------------------------------------
本来跳界面跳转的逻辑是很简单的,但是一考虑到跳转场景就变得比较复杂了,而且也容易出问题,所以应该尽量以单场景的方式开发。结构上比较简单。不过对于跳转场景的情况还是应该考虑进来的。
记一个curScene表示当前场景,若跳转界面的时候发现跟当前场景不一致则先跳转场景。


public static void JumpTo(string scene,string viewName,ViewType? type=null )
{
    if (!string.IsNullOrEmpty(curScene) && scene.Equals(curScene))
    {
        EnterView(viewName, type);
    } else
    {
        EnterScene(scene, delegate ()
        {
            EnterView(viewName, type);
        });
    }
}

static void EnterView(string viewName,ViewType? type)
{
    //若将进入的界面存在且不是悬浮界面 则先退出当前界面即栈顶界面
    var curView = GetTopView();
    if (!string.IsNullOrEmpty(curView.name)&&type !=null&&(type & ViewType.levitate) <= 0)
    {
        if (viewPool[curView.name] != null)
            viewPool[curView.name].Exit();
    }
    if (type == null)//默认界面类型 不过可能会在运行时被修改
    {
        IView view = GetView(viewName);
        type = view == null ? default(ViewType) : view.viewType;
    }
    ViewInfo kv = new ViewInfo(curScene, viewName, type.Value);
    PushView(kv);
    OpenView(kv);
}

static void OpenView(ViewInfo kv)
{
    if (kv.scene.Equals(curScene))
    {
        IView view = GetView(kv.name);
        if (view != null)
        {
            view.SetViewType(kv.viewType);
            view.Enter();
        }
    }
    else
    {
        EnterScene(kv.scene, delegate ()
        {
            OpenView(kv);
        });
    }
}


static void EnterScene(string scene,System.Action callBack)
{
    SceneLoadManager.Instance.LoadSceneAsync(scene, delegate ()
     {
         curScene = scene;
         if (callBack != null)
             callBack();
     });
}

回退BackView
---------------------------------------------------------------------------------
栈深度大于1时才能回退


public static void BackView()
{
    if(ViewStack.Count>1)
    {
        ViewInfo popView=PopView();
        ViewInfo peekView = GetTopView();
        //若当前界面为悬浮界面,则只退出当前界面,不用重进栈顶 因为悬浮界面不会顶掉原界面
        if(!string.IsNullOrEmpty(popView.name)&&(popView.viewType&ViewType.levitate)>0)
        {
            if (viewPool[popView.name] != null)
            {
                viewPool[popView.name].Exit();
            }
            //若发生过场景切换或界面已被销毁 则重新打开一下
            if (!peekView.scene.Equals(curScene)||viewPool[peekView.name]==null)
            {
                OpenView(peekView);
            }
        }
        else
        {
            //寻找一个可以返回的界面
            while(!string.IsNullOrEmpty(peekView.name)&&(peekView.viewType&ViewType.cantBack)>0)
            {
                PopView();
                peekView = GetTopView();
            }
            //若不需要跳转场景并且当前界面存在 则退出当前界面
            if(viewPool[popView.name]!=null&&popView.scene.Equals(curScene))
                viewPool[popView.name].Exit();
            OpenView(peekView);
        }
#if UNITY_EDITOR
        LogStackInfo();
#endif
    }
#if UNITY_EDITOR
    else
    {
        Debug.LogError("Can't BackView:stack depth is 1");
    }
#endif
}

只跳一个场景的情况
----------------------------------------------------------------------------
考虑单纯地跳一个场景的情况。同样也会入栈,但是name字段为null。BackView方法也同样生效,viewType也能生效,可以用来区分当前场景能否被返回


public static void JumpToScene(string scene,ViewType type=default(ViewType))
{
    JumpTo(scene, null, type);
}

完整代码
------------------------------------------------------------------------


public class ViewManager : MonoBehaviour {
   
    private static Stack ViewStack;
    private static Dictionary viewPool;
    private static string curScene;//当前场景名
    private static GameObject _viewRoot;
    private static GameObject viewRoot
    {
        get
        {
            if (_viewRoot == null)
                _viewRoot = GameObject.FindWithTag("ViewRoot");
            return _viewRoot;
        }
    }
    void Awake()
    {
        ViewStack = new Stack();
        viewPool = new Dictionary();
    }
    void Update()
    {
        //返回键
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            var curView = GetTopView();
            if(!string.IsNullOrEmpty(curView.name))
            {
                GetView(curView.name).BackView();
            }
        }
    }
    #region EnterView
    public static void JumpToScene(string scene,ViewType type=default(ViewType))
    {
        JumpTo(scene, null, type);
    }
    public static void JumpTo(string viewName, ViewType? type=null)
    {
        JumpTo(curScene, viewName, type);
    }
    public static void JumpTo(string scene,string viewName,ViewType? type=null )
    {
        if (!string.IsNullOrEmpty(curScene) && scene.Equals(curScene))
        {
            EnterView(viewName, type);
        } else
        {
            EnterScene(scene, delegate ()
            {
                EnterView(viewName, type);
            });
        }
    }
    static void EnterView(string viewName,ViewType? type)
    {
        //若将进入的界面存在且不是悬浮界面 则先退出当前界面即栈顶界面
        var curView = GetTopView();
        if (!string.IsNullOrEmpty(curView.name)&&type !=null&&(type & ViewType.levitate) <= 0 )
        {
            if (viewPool[curView.name] != null)
                viewPool[curView.name].Exit();
        }
        if (type == null)//设置默认界面类型 不过可能会在运行时被修改
        {
            IView view = GetView(viewName);
            type = view == null ? default(ViewType) : view.viewType;
        }
        ViewInfo kv = new ViewInfo(curScene, viewName, type.Value);
        PushView(kv);
        OpenView(kv);
    }
    static void OpenView(ViewInfo kv)
    {
        if (kv.scene.Equals(curScene))
        {
            IView view = GetView(kv.name);
            if (view != null)
            {
                view.SetViewType(kv.viewType);
                view.Enter();
            }
        }
        else
        {
            EnterScene(kv.scene, delegate ()
            {
                OpenView(kv);
            });
        }
    }
    static void EnterScene(string scene,System.Action callBack)
    {
        SceneLoadManager.Instance.LoadSceneAsync(scene, delegate ()
         {
             curScene = scene;
             if (callBack != null)
                 callBack();
         });
    }
    #endregion
   
    public static void BackView()
    {
        if(ViewStack.Count>1)
        {
            ViewInfo popView=PopView();
            ViewInfo peekView = GetTopView();
            //若当前界面为悬浮界面,则只退出当前界面,不用重进栈顶
            if(!string.IsNullOrEmpty(popView.name)&&(popView.viewType&ViewType.levitate)>0)
            {
                if (viewPool[popView.name] != null)
                {
                    viewPool[popView.name].Exit();
                }
                //若发生过场景切换或界面已被销毁 则重新打开一下
                if (!peekView.scene.Equals(curScene)||viewPool[peekView.name]==null)
                {
                    OpenView(peekView);
                }
            }
            else
            {
                //寻找一个可以返回的界面
                while(!string.IsNullOrEmpty(peekView.name)&&(peekView.viewType&ViewType.cantBack)>0)
                {
                    PopView();
                    peekView = GetTopView();
                }
                //若不需要跳转场景并且当前界面存在 则退出当前界面
                if(viewPool[popView.name]!=null&&popView.scene.Equals(curScene))
                    viewPool[popView.name].Exit();
                OpenView(peekView);
            }
#if UNITY_EDITOR
            LogStackInfo();
#endif
        }
#if UNITY_EDITOR
        else
        {
            Debug.LogError("Can't BackView:stack depth is 1");
        }
#endif
    }
    static ViewInfo PopView()
    {
        return ViewStack.Pop();
    }
    static void PushView(ViewInfo kv)
    {
       
        ViewStack.Push(kv);
#if UNITY_EDITOR
        LogStackInfo();
#endif
    }
    public static IView GetView(string name)
    {
        if (string.IsNullOrEmpty(name)) return null;
        if (viewPool.ContainsKey(name) && viewPool[name] != null)
        {
            return viewPool[name];
        }
        GameObject obj = null;//MemoryPool.GetObjInstanceForNGUI(name, viewRoot);
        IView view = null;
        if (obj != null)
        {
            view = obj.GetComponent();
        }
        if (!viewPool.ContainsKey(name))
            viewPool.Add(name, view);
        else
            viewPool[name] = view;
        return view;
    }
    public static int GetViewDepth()
    {
        return ViewStack.Count;
    }
    public static ViewInfo GetTopView()
    {
        return ViewStack.Peek();
    }
    public static void ClearViewStack()
    {
        ViewStack.Clear();
    }
  
    static void LogStackInfo()
    {
#if UNITY_EDITOR
        Debug.Log("----------------------------------------------------------------------------------------------------");
        Debug.Log("StackCount:" + ViewStack.Count);
        var stack = ViewStack.ToArray();
        for (int i = 0, Imax = stack.Length; i < Imax; i++)
        {
            Debug.Log(stack[i].ToString());
        }
        Debug.Log("----------------------------------------------------------------------------------------------------");
#endif
    }
    public struct ViewInfo
    {
        public ViewInfo(string scene,string name,ViewType viewtype)
        {
            this.scene = scene;
            this.name = name;
            this.viewType = viewtype;
        }
        public string scene;
        public string name;
        public ViewType viewType;
        public override string ToString()
        {
            string str="Scene:【" + scene + "】 Name:【" + (name == null ? "null" : name) + "】 viewType:";
            string usingViewType=string.Empty;
            foreach(ViewType type in System.Enum.GetValues((typeof(ViewType))))
            {
                if((viewType&type)>=type)
                {
                    usingViewType += type.ToString()+"|";
                }
            }
            str += "【" + usingViewType + "】";
            return str;
        }
    }
}
— 于 共写了5845个字
— 文内使用到的标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*