IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]UI编码指南

    kun1234567发表于 2015-11-25 00:18:55
    love 0

    UI编码指南

    kun

    2015.11.24

    设计思想

    界面逻辑采用MVC设计思想。在实践过程中,舍弃MVC对重用部分的理解,只使用对业务责任划分的理解。

     

    核心概念:Module

    在我们的代码里,各种各样的Manager就是Module。这些M 负责数据的加载、保存、更新、删除等一系列偏向数据层的操作。M的数据来源一般来自于和服务器通讯。我们的Module 既有“使用GameEvents 系统来做数据推送通知”,也有Controller层通过自身Tick、OnLoaded、OnShowImpl这些时机函数自发的拉取数据。需要根据具体情况来进行选择。

     

    核心概念:Controller

    代码里的UIController及其子类就是Controller。这些C的责任有三个方向:

    1、对数据层来说(上层),Controller是数据层的用户,负责实现“逻辑行为如何使用数据层”这个事情。这可能是一个行为的表达(一个函数,被动的等人调用),也可能是一个时机的表达(注册一个事件,等待时机推送)。

    2、对Window层来说(下层),Controller是将逻辑数据“转换”成Window理解的数据。(比如“角色的名字”是一个数据层概念,对于Window来说,应该是Label_Name的Text字段)。

    3、对Controller层来说(同级),是一个不同业务之间有关联的逻辑的互相调用。

     

    核心概念:Window

    代码里的UIWindow 及其子类就是Window。这些W负责GUI的具体显示任务。主要是对控件的操作功能封装、一些界面内在的显示逻辑(动画啊,不同的SubControl的显示逻辑、位置操作、颜色操作等)。最重要的责任之一是提供界面元素的控制接口。另外一个重要的责任是负责向系统反馈用户的交互输入(事件)。

     

    (接下来要谈的事情,仅代表我的看法)

    深入理解Controller的责任很重要,因为它处于一个承上启下的角色。

    常见的误区在于对数据层的直接访问跳过了Controller层,直接写在Window层的代码里了。

    比如“点击按钮就改变角色外形”这个例子:

    改变模型的能力由生物表达层Character来提供,外形和模型的对照关系由CharacterData提供。点击按钮这个事件由Window层提供,而“点击按钮修改CharacterData的数据”这件事情,便是Controller的责任,Controller负责衔接两个不同层次之间的交互。

    如果没有Controller层,直接将逻辑写在Window的OnClickButton函数里,可不可以?

    从结果上讲,没有错误。

    从设计上讲,将逻辑数据和Window控件的关联性强加给了Window.

    从自身的特性上讲,Window本身是具有资源的对象,势必参与资源管理活动(加载时机、卸载时机等),数据层自己也有数据的拉取、更新这些和时机相关的事情。而Controller是一个纯粹的内存对象,封装的也只是操作逻辑,自身是没有时机相关的事情的。这个“不变”,让Controller很适合做两个层次的中继者这个角色。

    目录结构

    LuaScripts/Client  放Module层的地方,各种Manager

    LuaScripts/Client/UI 放 Controller 和 Window 的地方。我们将V和C放一起.

    LuaScripts/UIEngineUIEngine中关于 Window 和 Controller 的各种重要基类,必看。

    阅读代码前的必备知识

    1、  UIController在我们的代码里,是每类型唯一。只应该通过UIControllerManager来获取和销毁。(注意和单件的区别,单件在程序运行时是不销毁的)。

    2、  除了UIController里直接使用UIWindow,代码其他任何地方不操作Window.所以对Window资源的管理,都通过对Controller的创建和销毁来完成。

    3、  由于Window加载有时间,而Controller是按需立即创建,在Window加载完成之前,Controller是可能收到一些数据变化的通知的,由于这些时机是瞬时的,所以等Window创建好,早就错过了更新自身状态的时机。尤其是采用Controller向Window推送数据这种实现时,处理这种不同步尤为重要(Window主动拉取数据的话会自动刷新)。为此Controller有一个 _onSyncContent 接口,用来规定一次强制的Controller和Window的同步。

    4、  还是由于Window的创建有时间,Controller内部需要在任何使用Window的时候都小心判断_window是否不为空。(甚至在一些特定的开发过程中,Window都可以不存在,我们只在Controller里打Print就可以搞定逻辑层的事情)

    5、  有一部分Window/Controller代码是在C#里的,不是全部都在Lua里。这是历史遗留。

    6、  为了避免内存和加载的峰值,我们采用“按需加载”这个大原则,如无必要不加载。但是请谨慎的处理销毁时机,不要遗忘了。

    7、  按需加载使用UIControllerManager的LoadOrShowController接口,这个函数接受的回调参数是onCreate,不是onShow,请注意不要被坑了。这只是一个语法糖式的便捷接口。

    8、  Window Layer Management 策略参考了Mac OS。这里有一篇介绍性的文章

    http://blog.csdn.net/kun1234567/article/details/47783681

    9、  由于Lua的特性,我们在模拟函数重载时,有一个特定写法,原理可以阅读Class.lua。例子如下:

     

     

    继承

    local X = UIWindow:extend

    {

        --静态变量放置区

    }

     

     

    构造函数

    function X:__init(gameObject, controller)

          ------注意这里必须是直接父类(不能是祖先类)

          -------必须是点,不能是冒号调用

          -------必须传递self.

               UIWindow.__init(self,gameObject, controller)

    end

     

    析构函数

    function X:__release()

    --- release your self

    --- base type relase

    ---也是点,也是直接父类,也是self!

    UIWindow.release(self)

    end

     

     

    核心类

    UIControl (不是Controller,请仔细看)

    控件的基类,负责在所有层面上都通用的操作的表达,比如visible。Window也可以视为一种特殊的Control

     

    UIWindowBase

    UIWindow

    Window概念的表达,负责一些基于窗口的行为的表达。也提供获取子控件_getControl、_onShow、_onHide这些必备功能。

     

    UIWindowManager

    主要是管理窗口资源的加载、卸载事务。

     

    UIController

    Controller的基类,本身没啥特别的。请注意和window创建相关的代码,这些代码揭示了两者的协作关系和声明周期关系。

     

    UIControllerManager

    Controller 的创建和销毁

     

    UIControllerLevel

    Layer Management相关.我们使用一个类似栈的结构,略有不同。参见LinkedList。

    历史原因类

    UICSController

    UICSWindow

     

    这是为了在Lua里也能从一定程度上控制c#的Window和Controller而设计的。是妥协出来的代码。

     

    结束

    请注意,

    1、  上述类的操作接口请确定自己掌握了,包括一些通过 callInterface 来进行模拟virtual 函数的地方,这些都是充分控制Window和Controller的关键。

    2、  就算你没仔细看这些代码,也请一定要看前面的阅读代码前的必备知识,无数先烈在这些概念上被坑过。



沪ICP备19023445号-2号
友情链接