unity游戏框架学习-UI模块
1.界面的加载、卸载
2.打开、关闭、隐藏、显示界面,这边隐藏是指界面被遮挡的意思,一般来说,界面被遮住时,应该关闭界面的更新
3.界面栈的管理,主要是用于场景切换时需要回到上一个场景打开的界面栈
4.需要的功能:图片镜像(节省资源)、滑动列表(复用)、模糊背景等
注意点:
1.界面的生成:class的生成、预制体的实例化,类和实例的关联。
业务打开一个界面需要传入界面的标识(枚举、或者字符串),如何通过这个标识找到预制体并实例化go、如何生成指定的界面view,如何绑定view和go
如何销毁一个界面,清除ab缓存、清除引用关系、destory go
2.界面的层级关系:每次打开一个新的UI,都将它堆入栈,关闭时出栈。这个栈是一个特殊的栈,例如它可以实现,某个不在栈顶的UI,可以“TOP”到栈顶。
打开一个界面:1.从已打开界面搜索,避免重复打开界面。2.从缓存界面搜索,避免重复加载。3.隐藏栈顶界面。4.打开新界面
关闭一个界面:1.关闭界面并加入缓存。2.从已打开界面栈顶取出一个界面,显示该界面。3.重复步骤2直到打开一个全屏界面。
退出场景:1.所有界面入栈,当再次回到场景时可以恢复界面栈。2.关闭界面
进入场景:1.如果需要恢复界面栈,从界面栈取出界面并打开显示该界面。2.重复1步骤直到打开一个全屏界面。3.不需要恢复:打开当前场景的界面。
3.界面间的通信:最好不要有界面之间的通信,界面的更新通过数据(逻辑)类发送消息通知给界面。例如使用道具后界面的更新,数据类接收到服务器道具使用成功后,发送道具更新消息BAG_DATA_UPDATE,需要更新的界面监听BAG_DATA_UPDATE消息并刷新界面。
4.界面打开动画控制。统一使用Animator(Animation),且每个界面最多只有一个Animator(Animation),界面获取焦点时调用Animator的Play方法播放动画。
5.界面特效控制:游戏会有很多进入界面播放一次的特效,如果你的界面关闭不是使用SetActive处理的(例如设置layer、移到屏幕外等),那么在你的界面再次打开时特效不会再次被播放。
6.界面内使用的对象怎么获取?(例如要修改界面的某个text)
每个需要在脚本内加载的对象都挂载一个UIExportItem对象,在界面初始化时统一收集这些对象,并存储在一个map里给界面使用
7.界面的模糊效果怎么做?可以参考:https://blog.csdn.net/zhaoguanghui2012/article/details/51462284,原理就是使用一个shader均值周围的像素。
8.弹窗适配,例如实现以下效果:弹窗显示与item的某条边对齐
如上所示,w为target的包围盒宽度,例如绿色框的长度,h为包围盒的高度,例如绿色框的高度。包围盒大小可以使用unity的RectTransformUtility.CalculateRelativeRectTransformBounds接口获取。
那么target的坐标怎么结算呢?
target.x = anchor.x + anchor.w/2 + offect.x + target.w/2
target.y - target.h/2 - offect.y = anchor.y - anchor.h/2 即 target.y = anchor.y - anchor.h/2 + target.h/2 + offect.y
当然对齐方式不一样,计算方法也不一样,例如左上、左下等,而且还需要考虑界面布局是否会超框,超框如何处理等。
具体可以参考:https://blog.csdn.net/SnoopyNa2Co3/article/details/50429604
UI模块分以下几部分:
1.界面类:负责界面的逻辑,提供生命周期方法供业务使用,如OnOpen、OnClose等。负责界面的生成、销毁(以及界面ab包的加载、卸载)。子窗口、子item的管理(生成、缓存、销毁)
2.管理类:提供界面的生命周期管理,如打开、关闭、显示(获得焦点,显示在最上层)、隐藏(失去焦点,可以理解成被其他界面挡住了)一个界面、打开、关闭一堆界面(场景切入、切出时)。缓存界面。维护窗体中间的层级关系。
3.配置类:负责配置界面的预制体路径、类、界面类型等参数。
4.功能类:滑动列表、图片镜像、模糊、弹窗适配、通用的标题、通用的tab等。
5.item类,例如界面的滑动列表的子项。
导出的层级
详细代码如下:
一、ViewDefine:定义类的配置:这边主要是一些界面定义以及界面的配置,通过配置类名可以用反射实例化界面类,通过配置的路径可以加载并实例化预制体。
using System.Collections.Generic; using UnityEngine; public class ViewDefine { public enum ViewType { MAIN = 1, // 主窗口(全屏) POPUP = 2, // 弹窗 FIXED = 3, // 固化窗口 SCENE = 4, // 场景UI窗口 GUIDE = 5, // 引导UI窗口 } public enum ViewPopModal { Blur = 1, // 带有模糊效果的模态弹窗 Lucency_ImPenetrable = 2, // 无模糊,不可穿透 Lucency_Penetrate = 3, // 无模糊, 可穿透 } public enum ViewLoadStateDefine { NONE = 0, LOADING = 1, LOADED = 2, } public enum ViewOnLoadDefine { Cache = 1, Destroy = 2, } public enum ViewAlignmentType { UpperLeft = 0, UpperCenter = 1, UpperRight = 2, MiddleLeft = 3, MiddleCenter = 4, MiddleRight = 5, LowerLeft = 6, LowerCenter = 7, LowerRight = 8, } public enum ViewID { TOAST, // 吐司界面 TOAST_BATTLE, // 战斗中吐司界面 NETWAIT, // 网络等待界面 NETWORK_TIPS, // 网络异常提示 } private static Dictionary<int, string> _viewConfig = new Dictionary<int, string> { { (int)ViewID.TOAST, "ToastView,Prefab/Common/ToastPanel" }, { (int)ViewID.TOAST_BATTLE, "ToastBattleView,Prefab/Common/ToastBattlePanel" }, { (int)ViewID.NETWAIT, "NetwaitView,Prefab/Common/NetwaitPanel" } }; public static string GetViewType(ViewID viewID) { string config = _viewConfig[(int)viewID]; if (string.IsNullOrEmpty(config)) { Debug.LogErrorFormat("未配置界面路径 : {0}", viewID); return null; } string[] split = config.Split(','); return split[0]; } public static string GetViewPath(ViewID viewID) { string config = _viewConfig[(int)viewID]; if (string.IsNullOrEmpty(config)) { Debug.LogErrorFormat("未配置界面路径 : {0}", viewID); return null; } string[] split = config.Split(','); return split[1]; } }
二、UIBase ,ui元素基类,主要提供go的销毁以及导入界面需要引用的对象并保存在_viewObj里,业务可以通过_viewObj["对象名"]访问对象而不用去定义参数。
using System.Collections.Generic; using UnityEngine; /// <summary> /// item、view的基类,提供预制体的生成、卸载(ab包的维护),提供子节点的生成 /// </summary> public class UIBase { protected GameObject gameObject; protected Transform transform; public string Name { get; set; } protected Dictionary<string, Object> _viewObj = new Dictionary<string, Object>();//导出的界面对象 public virtual void Ctor(GameObject obj, Transform parent) { if (obj != null) { gameObject = obj; transform = obj.transform; ExportHierarchy(); if (parent != null) { transform.SetParent(parent); transform.localPosition = Vector3.zero; transform.localScale = Vector3.zero; } } } protected virtual void OnLoad() { } public void SetActive(bool isShow) { gameObject.SetActive(isShow); } public virtual void Dispose() { if (gameObject != null) { GameObject.Destroy(gameObject); transform = null; gameObject = null; } } protected void ExportHierarchy() { if (gameObject) { UIHierarchy hierarchy = gameObject.GetComponent<UIHierarchy>(); if (hierarchy) { foreach (var item in hierarchy.widgets) { _viewObj.Add(item.name, item.item); } foreach (var item in hierarchy.externals) { _viewObj.Add(item.name, item.item); } } } } }
三、UIItemBase ,item类,主要是提供OnItemOpen、OnItemClose方法,方便item在界面打开和关闭时监听(移除)事件
/// <summary> /// 界面item /// </summary> public class UIItemBase:UIBase { protected ViewBase parentView; protected bool isItemOpen = false; public virtual void OnItemOpen() { isItemOpen = true; } public virtual void OnItemClose() { isItemOpen = false; } public bool IsItemOpen() { return isItemOpen; } public void SetParentView(ViewBase parent) { parentView = parent; } }
四、PanelBase ,子界面、界面的基类。主要是提供子item的生成、维护。
using System.Collections.Generic; using UnityEngine; /// <summary> /// 所有界面的基类,包括子窗口、所有的界面 /// 这个类主要功能是:提供给子界面生个生成、维护item的接口 /// </summary> public class PanelBase:UIBase { protected bool isOpen = false; protected Dictionary<string, Queue<UIItemBase>> childItemPool = new Dictionary<string, Queue<UIItemBase>>(); //缓存item的池子 protected List<UIBase> subItems = new List<UIBase>();//维护子对象,包括子窗口、子item /// <summary> /// UIModule调用,用于打开一个界面。如果该界面还未加载,会调用Load加载界面 /// </summary> /// <param name="args"></param> 界面参数 public virtual void Open(params object[] args) { } public virtual void Close() { } public bool IsOpen() { return isOpen; } /// <summary> /// 生成一个子item /// </summary> /// <param name="className"></param> 子item的类名 /// <param name="prefabs"></param> 子item的预制体 /// <param name="parent"></param> 子item的父节点 /// <returns></returns> protected UIItemBase GenerateItem(string className, GameObject prefabs, Transform parent) { Queue<UIItemBase> pool = childItemPool[className]; if (pool != null && pool.Count > 0) { return pool.Dequeue(); } UIItemBase item = InstantiateItem(className, prefabs, parent); AddSubItem(item); return item; } protected void GenerateItemList(string className, GameObject prefabs, Transform parent, int count, ref List<UIItemBase> container) { while (container.Count < count) { UIItemBase item = GenerateItem(className, prefabs, transform); container.Add(item); } while (container.Count > count) { UIItemBase item = container[container.Count]; container.Remove(item); RecyleItem(item); } } protected UIItemBase InstantiateItem(string className, GameObject prefabs, Transform parent) { GameObject obj = GameObject.Instantiate(prefabs, parent); UIItemBase item = (UIItemBase)UIModule.Instance.CreateUIClass(className); item.Ctor(obj, parent); item.Name = className; return item; } /// <summary> /// 回收子item /// </summary> /// <param name="item"></param> protected void RecyleItem(UIItemBase item) { RemoveSubItem(item); Queue<UIItemBase> cachePool = childItemPool[item.Name]; if (cachePool == null) { cachePool = new Queue<UIItemBase>(); } cachePool.Enqueue(item); item.SetActive(false); if (item.IsItemOpen()) { item.OnItemClose(); } } protected void RecyleItemList(int count, ref List<UIItemBase> container) { while (container.Count > count) { UIItemBase item = container[container.Count]; RecyleItem(item); container.Remove(item); } } protected void ClearCache() { foreach (var pool in childItemPool.Values) { foreach (var item in pool) { item.Dispose(); } pool.Clear(); } childItemPool.Clear(); } /// <summary> /// 添加子对象,包括item、childview /// </summary> /// <param name="item"></param> protected void AddSubItem(UIBase item) { if (subItems.Contains(item)) { Debug.Log("item已存在"); return; } subItems.Add(item); } protected void RemoveSubItem(UIBase item) { if (subItems.Contains(item)) { subItems.Remove(item); } } protected void RemoveAllSubItem() { foreach (var item in subItems) { item.Dispose(); } subItems.Clear(); } protected void OnOpenSubItem() { foreach (var item in subItems) { if (item is UIItemBase) { (item as UIItemBase).OnItemOpen(); } } } protected void OnCloseSubItem() { foreach (var item in subItems) { if (item is UIItemBase) { (item as UIItemBase).OnItemClose(); } } } public override void Dispose() { base.Dispose(); RemoveAllSubItem(); ClearCache(); } }
五、ChildViewBase :重写了Open、Close方法
public class ChildViewBase:PanelBase { /// <summary> /// UIModule调用,用于打开一个界面。如果该界面还未加载,会调用Load加载界面 /// </summary> /// <param name="args"></param> 界面参数 public override void Open(params object[] args) { if (!isOpen) { SetActive(true); OnOpenSubItem(); isOpen = true; } } public override void Close() { if (isOpen) { OnCloseSubItem(); SetActive(false); isOpen = false; } } }
六、ViewBase :所有界面基类,主要是提供了界面的加载、卸载(注意ab的加、卸载),子界面的添加、维护。
using System.Collections.Generic; using UnityEngine; /// <summary> /// 界面基类,可以生成ChildView /// </summary> public class ViewBase: PanelBase { protected object[] openParam; protected ViewDefine.ViewType viewType; protected ViewDefine.ViewLoadStateDefine loadState = ViewDefine.ViewLoadStateDefine.NONE; protected bool isHide = false; protected float closeTime = 0;//用于回收计时 public ViewDefine.ViewID ViewID { get; set; } /// <summary> /// 打开界面,一次OnOpen对应一次OnClose,子类实现 /// </summary> /// <param name="args"></param> protected virtual void OnOpen(params object[] args){ } /// <summary> /// 用于刷新界面,每次调用OpenView都会调用,避免业务重复打开界面 /// </summary> /// <param name="args"></param> protected virtual void OnRefreshView(params object[] args) { } /// <summary> /// 获得焦点。1.打开界面。2.上一层界面被关闭,重新获得焦点 /// </summary> protected virtual void OnEnabled() { } public virtual void Update(float dt) { } /// <summary> /// 失去焦点。1.关闭界面。2.有新的界面打开。 /// </summary> protected virtual void OnDisable() { } protected virtual void OnClose() { } /// <summary> /// UIModule调用,用于打开一个界面。如果该界面还未加载,会调用Load加载界面 /// </summary> /// <param name="args"></param> 界面参数 public override void Open(params object[] args) { openParam = args; if (loadState == ViewDefine.ViewLoadStateDefine.LOADED) { DoRealOpen(); } else { Load(); } } /// <summary> /// 加载界面,包括界面的ab包,ab的依赖包,最终返回一个Prefab用于实例化界面 /// </summary> private void Load() { if (loadState != ViewDefine.ViewLoadStateDefine.NONE) return; loadState = ViewDefine.ViewLoadStateDefine.LOADING; ResManager.Instance.LoadPrefab(ViewDefine.GetViewPath(ViewID), "", LoadFinish); } private void LoadFinish(object prefab) { loadState = ViewDefine.ViewLoadStateDefine.LOADED; gameObject = GameObject.Instantiate((GameObject)prefab, UIModule.Instance.GetViewRoot(viewType)); transform = gameObject.transform; ExportHierarchy(); OnLoad(); DoRealOpen(); } private void DoRealOpen() { if (!isOpen) { SetActiveEx(true); OnOpen(openParam); OnOpenSubItem(); } OnRefreshView(openParam); ShowView(); } /// <summary> /// 显示、隐藏界面,这边使用的方式是将界面移到屏幕外。 /// 另外几种做法是:1.SetActive直接隐藏go。2.设置Scale为0。3.设置layer out /// </summary> /// <param name="isActive"></param> 是否显示 public void SetActiveEx(bool isActive) { if (transform) { if (isActive) { transform.localPosition = Vector3.zero; } else { transform.localPosition = new Vector3(10000, 10000, 0); } } } public virtual void ShowView() { SetActiveEx(true); transform.SetAsFirstSibling(); if (!isHide) { isHide = true; OnEnabled(); } } /// <summary> /// 界面失去焦点,如果是打开弹窗,不隐藏该界面。 /// </summary> /// <param name="keepShow"></param> public virtual void HideView(bool keepShow = false) { SetActiveEx(keepShow); if (isHide) { isHide = false; OnDisable(); } } public override void Close() { if (isOpen) { UIModule.Instance.CloseView(ViewID); } } public virtual void CloseView() { HideView(); if (isOpen) { OnClose(); OnCloseSubItem(); CloseSubPanel(); closeTime = Time.realtimeSinceStartup; } } public float GetCloseTime() { return closeTime; } public bool IsPopView() { return viewType == ViewDefine.ViewType.POPUP; } public bool IsMainView() { return viewType == ViewDefine.ViewType.MAIN; } public bool IsFixedView() { return viewType == ViewDefine.ViewType.FIXED; } public bool IsShow() { return !isHide; } public bool IsOnLoadDestroy() { return loadState == ViewDefine.ViewLoadStateDefine.LOADING; } protected List<ChildViewBase> childView = new List<ChildViewBase>(); protected ChildViewBase AddChildPanel(string className, GameObject obj, Transform parent) { ChildViewBase view = (ChildViewBase)UIModule.Instance.CreateUIClass(className); view.Ctor(obj, parent); childView.Add(view); AddSubItem(view); return view; } protected void CloseSubPanel() { foreach (var item in childView) { if (item.IsOpen()) { item.Close(); } } } }
七、UIHierarchy:保存了业务导出的引用对象。ExportPanelHierarchy寻找UIExportItem 元素并保存到UIHierarchy
public class UIHierarchy : MonoBehaviour { [System.Serializable] public class ItemInfo { public string name; public Object item; public ItemInfo() { } public ItemInfo(string _name, Object _item) { name = _name; item = _item; } } // 控件 public List<ItemInfo> widgets; public void SetWidgets(List<ItemInfo> data) { if (data.Count == 0) return; if (widgets == null) { widgets = new List<ItemInfo>(); } widgets.Clear(); widgets.AddRange(data); } // 外部引用 public List<ItemInfo> externals; } using UnityEngine; [DisallowMultipleComponent] public class UIExportItem : MonoBehaviour { public string FieldName; }
using UnityEngine; using UnityEngine.UI; using UnityEditor; using System.Collections.Generic; public class ExportPanelHierarchy { /// <summary> /// 导出组件优先级 /// </summary> private static System.Type[] ms_componentTypes = { typeof(Button), typeof(InputField), typeof(ScrollRect), typeof(Dropdown), typeof(Image), typeof(RawImage), typeof(Scrollbar), typeof(Slider), typeof(Text), typeof(Toggle), typeof(GridLayoutGroup), typeof(HorizontalOrVerticalLayoutGroup), typeof(LayoutElement), typeof(CanvasGroup), typeof(ToggleGroup), typeof(TextMesh), typeof(Animation), typeof(Camera), typeof(SpriteRenderer), }; static Object FindComponent(GameObject go) { Object component = null; for (int i = 0; i < ms_componentTypes.Length; ++i) { component = go.GetComponent(ms_componentTypes[i]); if (component != null) { break; } } return component; } //生成嵌套UI层级 public static void ExportNested(Object obj) { GameObject root = obj as GameObject; if (root == null) return; UIHierarchy hierarchy = root.GetComponent<UIHierarchy> (); if(hierarchy==null) { hierarchy = root.AddComponent<UIHierarchy>(); } //生成根节点层级 List<UIHierarchy.ItemInfo> fields = new List<UIHierarchy.ItemInfo>(); GetChildComponentUtilHierarchy (root.transform, fields); hierarchy.SetWidgets(fields); //生成子panel层级 UIHierarchy[] childHierarchys = root.GetComponentsInChildren<UIHierarchy>(true); for(int i=1; i<childHierarchys.Length; i++) { UIHierarchy childHrcy = childHierarchys [i]; List<UIHierarchy.ItemInfo> childUIItem = new List<UIHierarchy.ItemInfo>(); GetChildComponentUtilHierarchy (childHrcy.transform, childUIItem); childHrcy.SetWidgets(childUIItem); } EditorUtility.SetDirty(root); AssetDatabase.SaveAssets(); } //导出传入节点的层级,直到某个子节点挂有UIHierarchy组件 private static void GetChildComponentUtilHierarchy(Transform transRoot, List<UIHierarchy.ItemInfo> fields) { for(int i=0; i<transRoot.childCount; i++) { Transform trans = transRoot.GetChild (i); UIHierarchy hrchy = trans.GetComponent<UIHierarchy> (); if(hrchy!=null) { fields.Add (new UIHierarchy.ItemInfo(hrchy.name, hrchy)); continue; } UIExportItem uiItem = trans.GetComponent<UIExportItem>(); if (uiItem != null) { Object fieldItem = FindComponent(uiItem.gameObject); if (fieldItem == null) { fieldItem = uiItem.transform; } fields.Add(new UIHierarchy.ItemInfo(uiItem.name, fieldItem)); } GetChildComponentUtilHierarchy (trans, fields); } } }
八、UIModule:核心管理类,提供给全局唯一的打开界面方法,维护层级栈。维护界面缓存。
using System.Collections.Generic; using System.Reflection; using UnityEngine; public class UIModule { #region Instance private static UIModule m_Instance; public static UIModule Instance { get { return m_Instance ?? (m_Instance = new UIModule()); } } #endregion private const float CACHE_TIME = 30;//界面缓存时间 private int curSceneID = 0;//当前打开的场景id,每个场景都有自己的层级栈 private int curViewID = 0;//当前打开的界面id private int lastViewID = 0;//上一个界面id private List<int> openViewList;//当前打开的界面列表,按顺序 private Dictionary<int, ViewBase> cacheView;//缓存区,等待销毁,从缓存区取要移除 private Dictionary<int, List<int>> naviStack;//场景的层级栈,key为场景id,用于维护场景 private Dictionary<int, ViewBase> viewPool;//保存所有ViewBase的引用,包括缓存区的,界面销毁时需要移除。 private Dictionary<ViewDefine.ViewType, Transform> viewRoot;//界面实例化出来的父节点,不同界面的父节点不一样。 private Transform uiRoot;//ui根节点 private float lastCheckCacheTime;//上次检查缓存时间,一秒检查一次 public void Init() { openViewList = new List<int>(); cacheView = new Dictionary<int, ViewBase>(); naviStack = new Dictionary<int, List<int>>(); viewPool = new Dictionary<int, ViewBase>(); uiRoot = GameObject.Find("UIRoot").transform; viewRoot = new Dictionary<ViewDefine.ViewType, Transform> { { ViewDefine.ViewType.MAIN, uiRoot.Find("main") }, { ViewDefine.ViewType.POPUP, uiRoot.Find("main") }, { ViewDefine.ViewType.FIXED, uiRoot.Find("fixed") }, { ViewDefine.ViewType.GUIDE, uiRoot.Find("guide") } }; lastCheckCacheTime = Time.realtimeSinceStartup; } public Transform GetViewRoot(ViewDefine.ViewType viewType) { if (viewType == ViewDefine.ViewType.SCENE) { return null; } else { return viewRoot[viewType]; } } public void Update(float dt) { int openCount = openViewList.Count; ViewBase view; for (int i = 0; i < openCount; i++) { int viewId = openViewList[i]; if (viewPool.TryGetValue(viewId, out view)) { view.Update(dt); } } //清楚缓存时间到了的界面 float curTime = Time.realtimeSinceStartup; if (curTime - lastCheckCacheTime > 1) { foreach (var item in cacheView) { if (curTime - item.Value.GetCloseTime() > CACHE_TIME) { item.Value.Dispose(); cacheView.Remove(item.Key); viewPool.Remove(item.Key); } } } lastCheckCacheTime = curTime; } /// <summary> /// 外部调用 打开一个窗口的唯一方式 /// 如果上一个界面lastView存在,需要把lastView入栈 /// </summary> /// <param name="viewID"></param> /// <param name="param"></param> 界面打开参数,给业务使用的 /// <returns></returns> public ViewBase OpenView(ViewDefine.ViewID viewID, params object[] param) { int viewKey = (int)viewID; ViewBase view = FindOpenView(viewKey); if (view == null) { view = CreateView(viewID); if (view == null) { Debug.LogErrorFormat("OpenView CreateView Fail..viewID:", viewID); return null; } view.ViewID = viewID; AddOpenView(viewKey); } if (curViewID == viewKey) { view.Open(param); return view; } if (view.IsPopView() || view.IsMainView()) { lastViewID = curViewID; curViewID = viewKey; if (lastViewID > 0) { OnBackstage(lastViewID); } } view.Open(param); return view; } private ViewBase FindOpenView(int viewKey) { ViewBase view = null; if (openViewList.Contains(viewKey)) { viewPool.TryGetValue(viewKey, out view); } return view; } private void AddOpenView(int viewKey) { if (openViewList.Contains(viewKey)) { Debug.LogFormat("界面已打开:{0}", viewKey); return; } openViewList.Add(viewKey); } private void RemoveOpenView(int viewKey) { if (openViewList.Contains(viewKey)) { openViewList.Remove(viewKey); } } /// <summary> /// 创建新的view,先从缓存里面找 /// </summary> /// <param name="viewID"></param> /// <returns></returns> private ViewBase CreateView(ViewDefine.ViewID viewID) { int viewKey = (int)viewID; ViewBase view = GetViewFromCache(viewKey); if (view == null) { string viewName = ViewDefine.GetViewType(viewID); //加载程序集,创建程序集里面的 命名空间.类型名 实例 object ect = CreateUIClass(viewName); view = (ViewBase)ect;//类型转换并返回 viewPool.Add((int)viewID, view); } return view; } public object CreateUIClass(string calssName) { return Assembly.GetExecutingAssembly().CreateInstance(calssName); } private ViewBase GetViewFromCache(int viewKey) { ViewBase view = null; if (cacheView.TryGetValue(viewKey, out view)) { cacheView.Remove(viewKey); } return view; } private void AddViewToCache(int viewKey, ViewBase view) { cacheView[viewKey] = view; } private int GetTopViewOfStack() { List<int> stack = naviStack[curSceneID]; if (stack == null || stack.Count == 0) { return 0; } return stack[stack.Count]; } private ViewBase PopViewFormStack() { List<int> stack = naviStack[curSceneID]; if (stack == null || stack.Count == 0) { return null; } int index = stack.Count; int viewId = stack[index]; ViewBase view = GetViewByKey(viewId); stack.RemoveAt(index); if (view.IsPopView()) { for (int i = index-1; i > 0; i--) { ViewBase temp = GetViewByKey(stack[i]); temp.SetActiveEx(true); if (temp.IsMainView()) break; } } curViewID = viewId; view.ShowView(); AddOpenView(viewId); return view; } /// <summary> /// 向栈里添加元素 /// </summary> /// <param name="viewKey"></param> /// <param name="isForce"></param>true时,栈里存在会先移除在加入,否则栈里存在就不处理了 private void AddViewToStack(int viewKey, bool isForce = true) { List<int> stack = naviStack[curSceneID]; if (stack == null) { stack = new List<int>(); } if (stack.Contains(viewKey)) { if (isForce) { stack.Remove(viewKey); } else { return; } } stack.Add(viewKey); } private void RemoveFromStack(int viewKey) { List<int> stack = naviStack[curSceneID]; if (stack == null || stack.Count == 0) { return; } if (stack.Contains(viewKey)) { stack.Remove(viewKey); } } /// <summary> /// 进入后台 /// </summary> /// <param name="viewKey"></param> private void OnBackstage(int viewKey) { AddViewToStack(viewKey); ViewBase lastView = GetViewByKey(lastViewID); if (lastView.IsPopView()) { lastView.HideView(true); } else { lastView.HideView(false); } } /// <summary> /// 关闭界面,如果是当前打开界面,需要从栈顶弹出新的界面 /// </summary> /// <param name="viewKey"></param> private void InsertClose(int viewKey) { RemoveView(viewKey); if (viewKey == curViewID) { curViewID = 0; PopViewFormStack(); } else { RemoveFromStack(viewKey); } } /// <summary> /// 移除界面,从打开列表移除,添加到缓存 /// </summary> /// <param name="viewKey"></param> private void RemoveView(int viewKey) { RemoveOpenView(viewKey); RemoveFromStack(viewKey); ViewBase view = GetViewByKey(viewKey); view.CloseView(); AddViewToCache(viewKey, view); } private ViewBase GetViewByKey(int viewKey) { return viewPool[viewKey]; } public void CloseView(ViewDefine.ViewID viewID) { int viewKey = (int)viewID; if (openViewList.Contains(viewKey)) { InsertClose(viewKey); } } public void CloseCurView() { if (curViewID > 0) { CloseView((ViewDefine.ViewID)curViewID); } } /// <summary> /// 进入新的场景 /// </summary> /// <param name="sceneID"></param> 场景id /// <param name="isNative"></param> 是否需要打开ui栈,isBack=true时,从当前场景的栈顶弹出界面 public void EnterScene(int sceneID, bool isBack) { curSceneID = sceneID; if (naviStack[sceneID] == null) { naviStack[sceneID] = new List<int>(); } if (isBack) { PopViewFormStack(); } } /// <summary> /// 退出当前场景 /// </summary> /// <param name="pushToStack"></param> 是否压栈,用于场景返回时恢复ui层级 public void ExitScene(bool pushToStack) { int count = openViewList.Count; ViewBase temp = null; int viewKey = 0; for (int i = count; i > 0; i--) { viewKey = openViewList[i]; temp = GetViewByKey(viewKey); if (temp != null && (temp.IsMainView() || temp.IsPopView())) { if (pushToStack) { RemoveOpenView(viewKey); AddViewToStack(viewKey, false); } else { RemoveView(viewKey); } } } List<int> stack = naviStack[curSceneID]; if (stack != null) { for (int i = 0; i < stack.Count; i++) { ViewBase view = GetViewByKey(stack[i]); view.HideView(); } } curViewID = 0; lastViewID = 0; } }