MVVM
MVVMModel-View-ViewModel
,它是MVC的改进版。
MVVM优点:
- 低耦合。视图(
View
)可以独立于Model
变化和修改,一个ViewModel可以绑定到不同的View
上,当View
变化的时候Model
可以不变,当Model
变化的时候View
也可以不变。 - 可重用性。你可以把一些视图逻辑放在一个
ViewModel
里面,让很多view重用这段视图逻辑。 - 独立开发。开发人员可以专注于业务逻辑和数据的开发(
ViewModel
)。 - 可测试。界面素来是比较难于测试的,测试可以针对
ViewModel
来写。
AWTK-MVVM
AWTK-MVVM 是一套用C语言开发的,专门为嵌入式平台优化的 MVVM 框架。它实现了数据绑定、命令绑定和窗口导航等基本功能,使用 AWTK-MVVM 开发应用程序,无需学习 AWTK 本身的 API ,只需学习绑定规则和模型的实现方式即可。
AWTK-MVVM无需手动编写和更新ViewModel
的代码,它提供一个主动代码产生器,只需在编写 Model
的代码时,按照固定的规则来编写注释(详细注释规则请点击查看model.md),就会根据Model
的代码自动生成更新ViewModel
。
前置步骤
一、编写Model
编写 Model
的代码,并按照model.md的规则来编写注释。
books_manager_model.h:
/**
* @class books_manager_model_t
*
* @annotation ["model"]
* 图书管理者模型。
*
*/
typedef struct _books_manager_model_t {
/**
* @property {int32_t} find_type
* @annotation ["readable", "writable"]
* 当前查找书籍方式。
*/
int32_t find_type;
/* private */
books_manager_t* books_manager;
} books_manager_model_t;
/**
* @method books_manager_model_create
* 创建books_manager_model对象。
*
* @annotation ["constructor"]
* @return {books_manager_model_t*} 返回books_manager_model对象。
*/
books_manager_model_t* books_manager_model_create(void);
/**
* @method books_manager_model_destroy
* 销毁books_manager_model对象。
*
* @annotation ["destructor"]
* @param {books_manager_model_t*} model books_manager_model对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t books_manager_model_destroy(books_manager_model_t* model);
二、利用工具生成ViewModel
请查看model.md的9.3小节:代码生成器。
三、注册ViewModel并通过导航器打开View
ret_t application_init(void) {
mvvm_init();
/* 其他初始化设置 */
/* ... */
/* 注册ViewModel,ViewModel名称为"books_manager_model" */
view_model_factory_register("books_manager_model", books_manager_model_view_model_create);
return navigator_to("home_page"); /* 通过导航器打开View */
}
四、绑定View与ViewModel
home_page.xml:
<window name="home_page" v-model="books_manager_model">
<!-- 省略... -->
</window>
数据绑定
基本数据绑定
- v-data 表示该属性是一个数据绑定规则。
- 第二部分(如示例中的value)是控件属性的名称,表示对该控件的哪个属性进行绑定。
- 属性的值放在'{'和'}'之间,里面是
Model
中变量的名称。
```xml
<window name="home_page" v-model="books_manager_model">
<combo_box_ex name="cmb_find" x="610" y="234" w="180" h="28" v-data:value="{find_type}"/>
<!-- 省略... -->
</window>
按条件进行数据绑定
通过嵌入表达式实现简单的逻辑判断。
当find_type等于4时,date_picker可见:
<window name="home_page" v-model="books_manager_model">
<date_picker name="date_picker" x="606" y="272" w="188" h="201" v-data:visible="{find_type == 4}">
<!-- 省略... -->
</date_picker>
<!-- 省略... -->
</window>
当find_type等于1时,edit_data控件的input_type等于2,否则等于0:
<window name="home_page" v-model="books_manager_model">
<edit name="edit_data" x="620" y="338" w="160" h="28" v-data:input_type='{if(find_type == 1, 2, 0)}'>
</edit>
<!-- 省略... -->
</window>
由于表达式中<>"等字符对于 XML 来说是特殊字符,需要转换成对应的实体 (entity),但是转换之后表达式不太直观,此时可以把属性提出来,放在 property 标签中,并用 CDATA 把它的值包起来。
当find_type大于0且小于4时,edit_data可见:
<window name="home_page" v-model="books_manager_model">
<edit name="edit_data" x="620" y="338" w="160" h="28">
<property name="v-data:visible"><![CDATA[ {(find_type > 0 && find_type < 4) ? true : false} ]]></property>
</edit>
<!-- 省略... -->
</window>
命令绑定
命令绑定规则也是一个控件属性:
属性的名称由两部分组成,两者之间用英文冒号分隔。
- v-on 表示该属性是一个命令绑定规则。
- 第二部分是控件事件的名称,表示对该事件触发时,自动执行指定的命令。
命令的名称放在'{'和'}'之间,命令对应Model
中的函数。
在view中关联"btn_add"控件的点击事件和add命令:
<window name="home_page" v-model="books_manager_model">
<button name="btn_add" x="610" y="115" w="180" h="36" tr_text="add" v-on:click="{add}"/>
<!-- 省略... -->
</window>
在model中添加add命令函数(注意:每次修改model都需要使用工具更新ViewModel代码!):
/**
* @method books_manager_model_add
* 添加图书。
*
* @annotation ["command:add"]
* @param {books_manager_model_t*} model books_manager_model对象。
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t books_manager_model_add(books_manager_model_t* model);
目前支持的事件有(以后更加需要增加):
事件 | |
---|---|
click | 点击事件 |
pointer_down | 指针按下事件 |
pointer_up | 指针松开事件 |
key_down | 按键按下事件 |
key_long_press | 按键长按事件 |
key_up | 按键松开事件 |
global_key_down | 全局按键按下事件 |
global_key_long_press | 全局按键长按事件 |
global_key_up | 全局按键松开事件 |
value_changed | 值改变事件 |
value_changed_by_ui | 值(通过 UI 修改)改变事件 |
详细内容请参考:
命令绑定:command_binding.md