反正做都做了,再做一个用XML来布局并生成界面的
运行前它是这个样子:
运行TestXMLloadUI.cs脚本后,就生成了一个UI:
其中,加载XML布局的代码如下:
using UnityEngine; using System.Collections; public class testXMLloadUI : MonoBehavIoUr { // Use this for initialization void Start () { UICreator.GetInstance().LoadFromXML(@"E:\work\cardTools\Assets\XMLFile1.xml"); } }
XMLFile1.xml的内容如下:
<?xml version="1.0" encoding="utf-8" ?> <NGUILayout xmlns:UIyang ="983323204"> <UIRoot name ="UI_TEST" depth ="0" parentName ="ui_1"> <Sprite adpClass = "SpriteAdp" objName ="tSprite" depth ="2" atlasName ="xxx" spriteName = "Emoticon - Frown" width = "100" height = "100" collider = "false" updateColByCOM = "true" cX = "10" cY = "10"> <Layout alignParentTop="" alignParentBaseLineX =""> </Layout> </Sprite> <Label adpClass = "LabelAdp" objName ="tLabel" depth ="3" width ="100" height ="100" collider ="false" text ="xxxx" updateColByCOM = "true" cX = "10" cY = "10"> <Layout alignParentBottom="" alignParentBaseLineX=""> </Layout> </Label> <Texture adpClass = "NormalCOMAdp" objName ="tTexture" depth ="1" width ="10" height ="10" collider ="false" updateColByCOM = "true" cX = "10" cY = "10"> <Layout fillParent=""> </Layout> </Texture> <MeteDate adpClass = "MeteDateAdp" objName ="md" depth ="4"> <Layout centerHorizontal="" centerVertical=""> </Layout> </MeteDate> </UIRoot> </NGUILayout>
格式是我自己定义的,每个组件有它自己的属性和一个布局,其中布局的string是和上一篇中的布局enum一致的,并且在代码中有互相转换的地方
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Xml; using System; using yang.UI; using System.Reflection; public class UICreator : MonoBehavIoUr { static UICreator instance; public static UICreator GetInstance() { return instance; } void Awake() { instance = this; } public UIWidget[] template; public string LastError; public UIWidget ui; public List<UIWidget> LoadFromXML(string url,Func<string,string> fileHandle = null){ string xmlPath; if(fileHandle!=null){ xmlPath = fileHandle(url); }else{ xmlPath = url; } List<UIWidget> lst_R_N = null; //try { XmlTextReader xTR = new XmlTextReader(xmlPath); xTR.WhitespaceHandling = WhitespaceHandling.None; while(xTR.Read()){ xTR.MoveToElement(); int nd = xTR.Depth; if (xTR.NodeType == XmlNodeType.EndElement) continue; //Debug.Log("current: " + xTR.Name+",depth:"+nd); switch (nd) { case 0://XML声明,不处理 break; case 1://UIRoot if (lst_R_N != null) return null; else { //生成实例 GameObject uiroot = Instantiate(ui.gameObject) as GameObject; UIWidget R = uiroot.GetComponent<UIWidget>(); //获得属性 string uiName = xTR.GetAttribute("name"); string depth = xTR.GetAttribute("depth"); string parentName = xTR.GetAttribute("parentName"); //放入场景 uiroot.name = uiName; R.depth = Int32.Parse(depth); UIRect pHook = getUIParent(parentName); uiroot.transform.parent = pHook.transform; uiroot.transform.localScale = Vector3.one; Dictionary<Layout,string> hLayout = new Dictionary<Layout,string>(); hLayout.Add(Layout.fillParent,""); UILayoutTool.LayoutUI(R,null,hLayout); lst_R_N = new List<UIWidget>(); lst_R_N.Add(R); } break; case 2://widget if (lst_R_N == null) { Debug.Log("return"); return null; } else { string nodeName = xTR.Name; WidgetType wt; try { wt = (WidgetType)Enum.Parse(typeof(WidgetType),nodeName); } catch { Debug.Log("转换失败:" + nodeName); wt = WidgetType.None; } //生成实例 int index = (int)wt; GameObject inst = Instantiate(template[index].gameObject) as GameObject; UIWidget w = inst.GetComponent<UIWidget>(); //获得适配类名并反射执行 string adpClass = xTR.GetAttribute("adpClass"); //Debug.Log(xTR.Name+","+adpClass+","); Type adp = Type.GetType(adpClass); var CInfo = adp.GetConstructor(Type.EmptyTypes); var objAdp = CInfo.Invoke(null); MethodInfo minfo = adp.GetMethod("Init"); minfo.Invoke(objAdp,new object[] {xTR,w}); //放入场景 GameObject uiroot = lst_R_N[0].gameObject; inst.transform.parent = uiroot.transform; inst.transform.localScale = Vector3.one; lst_R_N.Add(w); } break; case 3://layout if (lst_R_N == null) { return null; } else { UIWidget currentWidget = lst_R_N[lst_R_N.Count - 1]; Dictionary<Layout,string> layout = new Dictionary<Layout,string>(); int ac = xTR.AttributeCount; for (int i = 0; i < ac; i++) { xTR.MoveToAttribute(i); string k = xTR.Name; string v = xTR.Value; layout.Add((Layout)Enum.Parse(typeof(Layout),k),v); } UILayoutTool.LayoutUI(currentWidget,layout); //xTR.MoveToElement(); } break; default://其它层节点,不处理 break; } } return lst_R_N; //} catch (Exception e){ // Debug.Log(e.ToString()); // return null; //} } public UIRect defaultRoot; /// <summary> /// 寻找UI挂接点 /// </summary> private UIRect getUIParent(string name){ return defaultRoot; } } //UI组件 public class COMAdp { public virtual void Init(XmlTextReader xTR,UIWidget widget) { string comName = xTR.GetAttribute("objName"); string depth = xTR.GetAttribute("depth"); widget.gameObject.name = comName; widget.depth = Int32.Parse(depth); } } public class NormalCOMAdp : COMAdp { public override void Init(XmlTextReader xTR,UIWidget widget) { base.Init(xTR,widget); string width = xTR.GetAttribute("width"); string heigth = xTR.GetAttribute("height"); string collider = xTR.GetAttribute("collider"); string updateColByCOM = xTR.GetAttribute("updateColByCOM"); string cX = xTR.GetAttribute("cX"); string cY = xTR.GetAttribute("cY"); widget.width = Int32.Parse(width); widget.height = Int32.Parse(heigth); if(Boolean.Parse(collider)){ var col = widget.gameObject.AddComponent<BoxCollider>(); if (Boolean.Parse(updateColByCOM)) { widget.autoResizeBoxCollider = true; } else { col.size = new Vector3(Int32.Parse(cX),Int32.Parse(cY)); } } } } public class SpriteAdp : NormalCOMAdp { public override void Init(XmlTextReader xTR,widget); string atlasName = xTR.GetAttribute("atlasName"); string spriteName = xTR.GetAttribute("spriteName"); var s = (UISprite)widget; s.spriteName = spriteName; } } public class LabelAdp : NormalCOMAdp { public override void Init(XmlTextReader xTR,widget); string text = xTR.GetAttribute("text"); var l = (UILabel)widget; l.text = text; } } public class MeteDateAdp : COMAdp { public override void Init(XmlTextReader xTR,widget); } }
同时在调试的过程中发现上一篇的UILayoutTool.cs是存在问题的,就是上次是布局已经存在于场景中的UIWidget,而这一次是从prefab生成的,所以72-75行的获取组件父物体方法是失败的,大概原因是prefab的脚本缓存了错误的父物体,改为如下则没有问题了:
<span style="white-space:pre"> </span>if (NGUITools.FindInParents<UIRect>(widget.transform.parent.gameObject).GetType() == typeof(UIPanel)) { ptarget = (UIPanel)NGUITools.FindInParents<UIRect>(widget.transform.parent.gameObject); } else { target = (UIWidget)NGUITools.FindInParents<UIRect>(widget.transform.parent.gameObject); }
写得比较粗糙,目前还存在的问题有:
XML的格式要求比较严格,没做缺省参数检查,必须写完所有参数;在XML中也不支持注释等
Sprite没有做加载图集部分,现在只能用prefab指定的图集
Textrue同样没有做设置mainTexture的部分,需要手动在其它地方设置
不过,LoadFromXML函数返回了UI的根和所有的组件的list,可以利用它做更多的事情,也可以结合一些动态脚本什么的,做更多东西
哦,对了,还有个UICreatorEditor.cs,就是UICreator的面板显示脚本:
using UnityEngine; using System.Collections; using UnityEditor; [CustomEditor(typeof(UICreator),true)] public class UICreatorEditor : Editor { protected UICreator mUICreator; void OnEnable() { mUICreator = target as UICreator; if(mUICreator.template == null) mUICreator.template = new UIWidget[10]; } public override void OnInspectorGUI() { //base.OnInspectorGUI(); serializedObject.Update(); mUICreator.defaultRoot = (UIRect)EditorGUILayout.ObjectField("默认UI挂接点",mUICreator.defaultRoot,typeof(UIRect)); mUICreator.ui = (UIWidget)EditorGUILayout.ObjectField("UI根",mUICreator.ui,typeof(UIWidget)); EditorGUILayout.Separator(); if (NGUIEditorTools.DrawHeader("prefab")) { NGUIEditorTools.BeginContents(); mUICreator.template[0] = (UIWidget)EditorGUILayout.ObjectField("MeteDate(自定义)",mUICreator.template[0],typeof(UIWidget)); mUICreator.template[1] = (UIWidget)EditorGUILayout.ObjectField("Sprite(精灵)",mUICreator.template[1],typeof(UIWidget)); mUICreator.template[2] = (UIWidget)EditorGUILayout.ObjectField("Label(字符串)",mUICreator.template[2],typeof(UIWidget)); mUICreator.template[3] = (UIWidget)EditorGUILayout.ObjectField("Texture(纹理)",mUICreator.template[3],typeof(UIWidget)); mUICreator.template[4] = (UIWidget)EditorGUILayout.ObjectField("Button(按钮)",mUICreator.template[4],typeof(UIWidget)); mUICreator.template[5] = (UIWidget)EditorGUILayout.ObjectField("Toggle(选择框)",mUICreator.template[5],typeof(UIWidget)); mUICreator.template[6] = (UIWidget)EditorGUILayout.ObjectField("ProgressBar(进度条)",mUICreator.template[6],typeof(UIWidget)); mUICreator.template[7] = (UIWidget)EditorGUILayout.ObjectField("Slider(滑动条)",mUICreator.template[7],typeof(UIWidget)); mUICreator.template[8] = (UIWidget)EditorGUILayout.ObjectField("Input(输入框)",mUICreator.template[8],typeof(UIWidget)); mUICreator.template[9] = (UIWidget)EditorGUILayout.ObjectField("ScrollBar(滚动条)",mUICreator.template[9],typeof(UIWidget)); NGUIEditorTools.EndContents(); } EditorGUILayout.Separator(); serializedObject.ApplyModifiedProperties(); } }