反正做都做了,再做一个用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一致的,并且在代码中有互相转换的地方
那么,重要部分来了,解析XML并生成UI的代码如下:
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, null, 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, UIWidget widget) {
base.Init(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, UIWidget widget) {
base.Init(xTR, widget);
string text = xTR.GetAttribute("text");
var l = (UILabel)widget;
l.text = text;
}
}
public class MeteDateAdp : COMAdp {
public override void Init(XmlTextReader xTR, UIWidget widget) {
base.Init(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的部分,需要手动在其它地方设置
MeteData类型的UI组件要自己写XML属性提取
不过,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();
}
}