细说PerGUI框架设计思路
本来想分析MarkUX框架的代码,但是需要购买,暂时就放下了,恰巧遇到PerGUI几乎雷同的设计思路。那就直接拿来分析吧。此文是我读此框架的笔记,外行看热闹内行看门道至于能理解多少就见仁见智了,如果我没理解的地方还请不吝指出。最后你可以在CodePlex上找到此开源代码。
此项目的介绍
基于XML解析的数据,生产出对应的Widget。如果你有WPF开发经验的话会发现,这就是同一种思路。配置(xml或者其他存储方式)文件 -> 映射(通过某种转换) -> UI表现 .且不说游戏领域的这套做法,其他领域已经玩的非常溜了。此源码总的来说极轻量级 非常容易定制,对于拿来主义来说还真是比较不错的选择(当然不是说我自己啦 哈哈)。
使用场景
关于这种UI的开发模式 肯定适用于热更新(这个还真需要脑补),其次适用于给非程序人员使用(甚至是策划),极大降低程序对UI表现改动的。好处显而易见。
亮点
通过配置创建出UI,支持嵌套,容易上手
框架实现思路
数据模型类(Model) = 代码(包括函数与属性) + xml工具(此处使用Microsoft System.Xml)
预实现UI类 + 数据模型类(Model) = PurGUI
预实现UI类 与 Model 之间的糅合 使用的是 属性映射。直接通过Model的属性映射到配置文件,序列化出来的就是所需组件。
核心:灵活利用xml反序列化特性抽象控件树加载逻辑。
代码目录结构及简要说明(通关攻略请配合源码使用)
Attributes
BindControlAttribute.cs 属性级别标记 标记出需要绑定的数据模型(Model)的当前属性用何种控件显示
ButtonClickEventAttribute.cs 函数级别标记 标记出需要绑定到数据模型(Model)的函数
ControlValueTargetAttribute.cs 属性级别标记 标记出需要绑定的数据模型(Model)的属性
WindowResourceAttribute.cs 类级别标记 标记出需要读取配置(XML)名
看到这几个文件名,基本上能猜出来 这个框架的套路了。通过新建一个 “数据模型(Model)”,也可称之为浑身都是标记的具体UI类,我们将 需要使用到的控件全部标记在属性上与函数上 我称之为 <控件与具体UI类强绑定> 。 通过类级别标记的值 映射(通过某种转换) 到配置 。
Cursor
PGCursor.cs 小彩蛋,关于鼠标的配置。
Helper
PGCursorListener.cs 彩蛋相关(覆写了鼠标点击的接口,配合PGCursor实现鼠标的姿态切换)
PGDraggable.cs 彩蛋x2 UI拖动组建(覆写了 IDragHandler 拖动接口 添加了拖动超屏判断)
1 | public void OnDrag(PointerEventData eventData) |
PGEditFixer.cs 对UGUI的bug修正,闪动光标的初始位置偏移问题(执行一次)
PGXmlLoader.cs 配置加载
以下配置加载是体现此框架的技巧性的部分之一了,这是一个加载规则(Rule) , <PreGUI.Widgets.PG[???]> 就是命名空间。利用 PreGUI.Widgets 命名空间下的已PG开头的类 作为反序列化对象。好处是能通过配置序列化出各种预定义的控件类型,抽象了具体的判断。(灵活利用Microsoft提供的Xml类库也是一种能力的体现方式)
1 | public static IPGControl XmlProcessChildControl(this XmlNode node) |
PreGUIExtensions.cs 里面几个扩展函数就比较有意思了(重要)。
//反射被绑定的类的事件实现 -> 此处直接定位到了 Model(数据模型) 标记ButtonClickEvent特性的 函数,属于函数映射(Mapping)
1 | public static void CallEventAction<T>(this object viewModel, string controlName, T source) |
//反射被绑定的类的属性(数值)实现 -> 此处直接定位到了 Model(数据模型) 标记ControlValueTarget特性的 属性(有点拗口),属于属性映射(Mapping)
1 | public static void UpdateControlValue<T>(this object viewModel, string controlName, T newValue) |
//此函数一般由Widget自己调用 用来给 Model(数据模型) 内 标记了 BindControl 的标签 赋值为当前Widget.
1 | public static void BindTo<T>(this T thisControl, string controlName, object viewModel) |
Interface
IPGControl.cs 抽象控件的标准化接口(这就是实现多种输入输出组件统一化的最终奥义)
此 LayoutControls(PreGui gui, GameObject parent, string windowName) 函数签名 告诉我们可以基于此接口创建出控件.
此 IPGControl GenerateFromXmlNode(XmlNode node) 函数签名告诉我们它是用来加载配置的.此处用了一个称之为FluentInterface的技巧(喂自己吃了颗语法糖)。既然有了配置的信息,那么自然可以通过上面的接口构造出组件。
1 | public interface IPGControl |
Widgets
PGButton.cs
PGCaptionLabel.cs
PGCheckBox.cs
PGHorizontalLayout.cs
PGLabel.cs
PGPanel.cs
PGScrollWindow.cs
PGTextEdit.cs
PGVerticalLayout.cs 此处以上文件皆遵循 IPGControl 接口
PGWindowTitle.cs 此文件意在添加一个自定义的Head,实际上亦可转化为 遵循 IPGControl 的组件
PGWindow.cs 控件制造机/控件工厂 随便怎么叫都行,它是一切控件的(LayoutControls接口)组装的指派者。
static PGWindow LoadFromXml (XmlNode node) 函数签名中截取代码 实现框架的关键函数
1 | for (var i = 0; i < node.ChildNodes.Count; i++) { |
void InitializeWindow (GameObject gameObject) 函数签名中截取代码 实现框架的关键函数
1 | if (Children != null) { |
这两个函数决定了框架的UI组件以深度优先的方式加载(因为xml可以嵌套,也就是说代码中无需重复造轮子).
Root(根目录) -> PurGUI.cs
包含 创建UI的入口(函数签名) -> void BindViewModel (string windowName, object viewModel)
包含 事件绑定 就是基于 Key - Value的消息触发模型。Value即为Action.
包含 PurGUI框架的基础配置
包含(重要) UI所需基础元素,如Camera,_canvas
PGWindow的直接操控者,此框架的大门,每当创建一个UI组件的时候,都会将自身引用 传递给 所创建的组件。这意味着 PurGUI与Widget 是一个一对多的关系。只要PurGUI发生更改 所有的组件都可以获取实时的变更。这种做的坏处是无法(面对复杂需求就跪了)进行多个摄像机的管理,如果需要新增多个相机或根节点(Canvas)则需要创建多个基础(PurGUI),当然这存在很大的优化余地 这里暂时就不讨论了。
本文标题:细说PerGUI框架设计思路
文章作者:Keyle
发布时间:2016-08-26
最后更新:2024-08-20
原始链接:https://vrast.cn/posts/c2b10b93/
版权声明:©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可