
前一阵子我在做一个资源受限的嵌入式设备项目屏幕不大界面也就是几个输入框、按钮、状态条但为了让用户能配置参数UI部分硬是让我折腾了不少时间。以前习惯用代码直接创建控件控件事一多位置调整、事件绑定、样式切换全混在一堆C代码里每次改版都跟拆炸弹似的。后来换用AWTK这套开源跨平台GUI引擎界面描述直接用XML写逻辑和界面彻底分开大部分界面调整只需要改XML文件就能完成这个体验让我决定好好整理一篇入门文章。这篇就专门讲AWTK的XML篇坐标系统怎么算、常用控件怎么写、布局怎么做、资源怎么引、以及我在实际使用中踩过的一些坑。适合刚接触AWTK、或者对声明式UI构建方式感兴趣的开发者。不需要你有很深的C语言基础能看懂简单代码就行毕竟AWTK的XML入门核心就一句话把界面当成一份结构化的配置文件去写。1. 为什么要用XML写UI——AWTK的设计思路1.1 声明式界面到底解决了什么痛点用代码写界面的老套路是这样的每创建一个控件都要调用对应的创建函数然后逐个设置属性。比如创建一个标签和一个按钮代码可能是这个样子widget_t* label widget_create(NULL, label, 0, 0, 100, 30); widget_set_text(label, Hello); widget_t* btn widget_create(NULL, button, 0, 40, 80, 40); widget_set_text(btn, OK);单个控件看起来还好但界面一旦复杂起来问题就来了几十个控件在代码里排列位置、大小、层级、样式、事件全部混在一起你要找一个按钮的位置得在代码里翻半天。更要命的是UI稍有调整就得改代码重新编译界面和业务逻辑耦合得越深后期维护就越痛苦。XML方案解决的就是这个问题。AWTK允许你用XML声明式地描述界面结构控件是什么类型、放在哪里、多宽多高、文本是什么这些信息全部写在XML文件里。业务逻辑代码只需要通过控件name去查找控件、绑定事件界面长得怎么样跟逻辑代码基本无关。这种感觉很像装修房子的时候你拿着一张设计图告诉工人哪里放沙发、哪里放电视柜而不是站在房间里手舞足蹈地指挥。这种分离还有一个额外的好处如果你有设计师或者做配置的人参与他们即使不写C代码也能通过调整XML来修改界面。哪怕没有别人参与单是改界面不动代码这一点在快速迭代原型的时候就足够值回票价了。1.2 一份XML是怎么变成屏幕上的控件的很多新手一开始会困惑XML文件到底是怎么被程序识别并显示出来的其实机制不复杂。AWTK的运行环境里有一套完整的控件对象系统XML文件会被当作资源打包进程序程序运行时通过窗口管理器加载XML解析成一颗控件树。每个XML标签对应一个控件实例标签里的属性对应控件对象的属性解析完成之后控件树就被挂到窗口上再经过绘制引擎渲染到屏幕。整个流程可以理解成读配置、建对象、挂窗口。对你来说实际需要关心的环节是把XML文件放到正确的资源目录运行资源生成脚本或工具把它打包进可执行环境然后在代码里调用窗口打开函数。这里我得特意强调一个初学者最容易忽略的点改完XML之后必须重新生成资源否则程序跑起来加载的还是旧资源。很多诡异的现象比如我明明改了按钮文本怎么界面上没变十有八九就是资源没有重新打包。记住改XML、重新生成资源、再运行这个闭环能帮你省掉大量排查时间。2. 认识AWTK的XML布局坐标系2.1 四个位置属性x/y/w/h的多写法AWTK里几乎所有控件都要设置位置和大小核心属性就是x、y、w、h。这四个属性的用法以及各种特殊写法是XML入门的第一课。x和y表示控件左上角相对于父容器的位置w和h表示控件的宽度和高度。属性值可以是固定像素也可以是百分比还可以是一些特殊标记。我整理了一个常用写法表实际用的时候对照查就行写法含义x10距父容器左边10像素xcenter 或 xc水平居中xcenter10水平居中后再向右偏移10像素xr:20距父容器右边20像素x30%距父容器左边30%的位置y10距父容器顶部10像素ymiddle 或 ym垂直居中ymiddle10垂直居中后再向下偏移10像素yb:20距父容器底部20像素w80%宽度为父容器宽度的80%h40高度40像素注意这里的父容器可能是整个窗口也可能是一个布局容器。后面讲布局的时候会再细化。我建议新手一开始只用三种写法就够了固定像素、百分比、以及center/middle这种居中标记。比如想让一个控件在窗口正中间最常用的就是xcenter ymiddle w80% h30这段代码表示控件水平居中、垂直居中宽度是父容器宽度的80%高度固定30像素。这种写法在搭建登录页、设置页的时候非常实用你不需要自己去算屏幕像素。2.2 最简窗口一个标签也能撑起一个窗口先给一个最简的AWTK窗口XML让大家对整体结构有个直观感受window namehello anim_hinthtranslate label xcenter ymiddle w80% h30 textHello AWTK/ /window这个XML文件里最外层是window标签表示一个普通窗口。窗口的name属性是hello后续在代码里可以通过window_open(hello)来打开它。anim_hint属性是窗口的动画效果这里填htranslate表示窗口打开时有一个水平平移的过渡动画。window标签内部挂了一个label控件就是那个显示文本的标签。它的位置通过xcenter ymiddle在窗口里居中宽度是窗口宽度的80%高度是30像素显示的内容是Hello AWTK。这个最简单的例子其实已经能把XML的运作逻辑讲清楚了每一个标签括号代表一个控件控件的属性写在尖括号内部。你后续写复杂界面无非是在window标签里挂更多的控件再把容器布局用起来而已。2.3 坐标系里容易忽略的三个细节第一个细节子控件的坐标永远相对父容器。很多人把控件坐标当成屏幕绝对坐标来算结果控件位置怎么摆都不对。实际上如果你在一个view容器里放按钮按钮的x10指的是距view容器左边缘10像素而不是距屏幕左边缘10像素。第二个细节控件超出父容器范围不会报错但也不会显示。这听起来像废话但实际操作中经常有人把坐标写大了然后盯着屏幕问我的按钮去哪了。AWTK不会帮你把越界的控件拉回来它会直接不绘制或者被裁剪掉。所以当你发现某个控件消失了第一个念头应该是去看它的坐标是不是超出可视区域。第三个细节控件的绘制顺序和遮挡关系。XML里写在后面的控件默认会绘制在上层也就是说后出现的控件会盖住先出现的控件。这个特性和Web的DOM层级有点类似。遮挡问题不只是影响视觉效果还可能影响点击事件一个透明或者很大的控件盖住了下面的按钮按钮永远收不到点击。后面讲问题排查的时候还会回到这一点。3. 常用控件的XML写法与属性细节3.1 label文本显示的基础操作label是AWTK里最基础的显示控件用来展示静态文本。最常用的属性是text直接填文本内容。如果文本是多行的可以用换行符具体写法看你项目的转义规则。label还有几个属性值得记一下。tr_text是用来做多语言翻译的键名如果项目需要国际化你会经常用到它。font_size指定字号比如font_size20就是20像素大小的文字。不过要注意字号能不能正确生效还取决于你的主题和字体资源支不支持这个在第5章会讲到。还有一个很多新手不知道的细节label虽然叫标签但它的文本也是可以动态修改的。你完全可以在代码里通过widget_set_text来改变它显示的内容把它当成一个动态文本区域用。所以如果你的界面里有个当前温度25度这种会自动变化的数据完全可以用一个label来承载。3.2 button别把事件写在XML里button按钮是所有交互界面的核心。XML里定义一个按钮很简单button nameok_btn xcenter ymiddle60 w160 h40 text确定/这里name属性特别重要它是你后面在C代码里查找这个按钮的唯一标识。比如这段XML里的name是ok_btn代码里就能通过widget_lookup(win, ok_btn, TRUE)找到它。按钮的事件绑定不是在XML里写的。AWTK的XML通常只负责描述界面结构事件响应逻辑依然放到C代码里这是很多新人会搞混的地方。XML里你只需要保证name属性命名清晰让代码能精准找到控件就行。命名规范我建议用功能_控件类型这种风格比如ok_btn、cancel_btn、user_edit时间长了你会感谢自己这个习惯。另外按钮在XML里还能控制一些状态相关属性比如enable用来设置是否可用。当enable设置为false时按钮会变成灰色且不响应点击。如果你想要一个禁用状态的按钮直接在XML里写enablefalse就行不用在代码里再做一次状态判断。3.3 edit输入框的几个实用属性edit是文本输入控件对应界面里的输入框。定义方式edit nameuser_edit xcenter ymiddle20 w80% h32 text/我实际使用中最常用到的属性有这几个input_type控制输入类型比如纯文本、数字、密码tips属性是输入为空时显示的灰色提示文字相当于HTML里的placeholdermax_length限制最大输入长度防止用户乱填read_only则可以让输入框变为只读状态。input_type这里要特别提一句AWTK的input_type在不同版本里支持的取值可能不同。有的版本用数字枚举有的版本支持英文字符串比如password。我个人的建议是在动手写之前先查一下你当前AWTK版本的头文件或者官方文档确认输入类型的写法避免写完才发现属性值不识别。tips属性建议多用它能让界面看起来友好很多。很多开发者一开始不知道有这个属性非得在输入框旁边放个label来提示用户该填什么界面愣是多了一堆控件布局也更复杂。实际上用tips就够了输入框自带灰色提示用户一点击输入就自动消失。3.4 image/progress_bar/slider快速参考除了上面三个AWTK还有一些常用控件我把它们的XML关键属性放在一起做个速查表控件标签名关键属性说明图片imageimagelogo引用图片资源名不带扩展名进度条progress_barvalue60 max100显示进度百分比滑块slidervalue30 min0 max100 step1可拖动调节数值复选框check_buttontext记住密码 valuetrue勾选状态图片控件比较常踩坑的地方是image属性填的资源名。AWTK的资源管理通常要求填不带扩展名的名字比如你的图片文件叫logo.pngXML里写imagelogo而不是imagelogo.png。如果你填了扩展名某些版本也能兼容但为了保险起见建议统一不写扩展名。progress_bar显示进度value填当前值max填最大值显示的时候会按比例换算。slider的使用也类似min和max定义了范围step决定拖动的最小变化量。这些控件的value属性都可以在代码里动态修改所以很适合用来做设备的实时状态显示。我第一次用的时候还闹过笑话以为进度条只有value这一个属性结果max默认是100我直接把value写成50显示50%看起来好像没问题。直到后来要显示一个范围0到1000的数值时才明白max才是决定进度显示比例的关键。4. 布局方式从手摆坐标到容器自动排列4.1 缺省布局什么时候适合手动算坐标AWTK默认的布局方式就是前面第2章讲的那样每个控件都用x/y/w/h手摆坐标。这种方式在控件少、界面简单的时候完全够用比如一个窗口就两三个控件手写坐标反而最直观、最容易控制。但控件一多手摆坐标的缺点就很明显了。最典型的是调整窗口大小时固定像素坐标不会跟着变化。做个屏幕适配你可能会发现按原分辨率排好的界面到了另一种尺寸的屏幕上控件位置全乱了。所以我的建议是小界面、快速原型、或者对布局灵活性要求不高的场景用缺省布局。一旦遇到需要动态适应屏幕尺寸、或者控件数量明显增多的情况就要开始考虑用容器布局了。这个判断标准你做到两三个窗口之后自然就会有感觉。4.2 hbox和vbox让子控件自动排队AWTK提供了hbox和vbox这两种线性布局容器。hbox表示水平排列子控件从左到右依次排开vbox表示垂直排列子控件从上到下依次排开。举个例子想做一个底部的按钮栏两个按钮水平排列hbox x0 y0 w100% h40 spacing5 button nameprev_btn w50% h100% text上一个/ button namenext_btn w50% h100% text下一个/ /hboxhbox容器设置了spacing5表示子控件之间的间距是5像素。两个按钮的宽度都是50%在水平容器里会被自动均分整体正好占满容器宽度。vbox的使用方式类似只是排列方向变成垂直。适合做那种上中下三段式的布局比如顶部标题栏、中间内容区、底部操作区用vbox就能很优雅地完成。使用容器布局时要记住一个变化放在容器里的子控件其位置对齐方式由容器的布局算法决定你再手动设置x和y通常不会生效或者说会被覆盖。所以不要在容器里的子控件上花太多心思调坐标你需要关注的是它的宽度、高度、以及容器提供的间距参数。4.3 网格对齐嵌套容器的实战写法如果你要做一个两行两列的设置项界面像这种用户名输入框 用户状态 IP地址输入框 设备ID最直接的想法可能是用一个grid网格控件或者网格布局参数。但不同版本的AWTK对网格的支持程度不一样所以我更推荐用嵌套容器的通用做法外层用vbox每一行再用一个hbox。这样做的好处是兼容性最好而且每一行的宽度分配你可以很直观地控制。比如每行hbox里有两个子控件各占50%宽度无论屏幕多宽它们都会按比例分配空间。这种嵌套容器的思路其实就是UI布局通用的降维打击技巧把复杂的多行多列问题拆成多次一维排列。嵌套层级不宜过深我的经验是一般三层以内就够用了。超过三层你查找和调整控件时会开始头疼而且XML结构也会变得冗长难读。如果发现嵌套变得很深优先考虑是不是可以把某些区域独立成一个单独的窗口或者视图。4.4 布局嵌套的通用套路这里把嵌套布局的通用套路总结一下方便你以后遇到实际界面直接套用。第一步先把界面按功能或视觉区域横向切分。比如顶部一个标题区、中间一个内容区、底部一个按钮区。这就是一个vbox结构。第二步对每个区域内部再做纵向切分。比如内容区可能要分成左右两栏那就在内容区里放一个hbox。第三步在每一栏里如果还有多个元素比如一个label下面跟一个edit那这一栏内部又是一个vbox。第四步给每个容器明确宽度和高度分配。用百分比能保证适配不同屏幕用固定像素则适合那些不希望拉伸的区域。用这个套路绝大多数界面都能拆解成清晰的树形结构。写XML的时候我习惯先搭空容器骨架确认布局基本符合预期再往各个容器里填充具体控件。这样做的好处是你能尽早发现问题而不是等几十个控件全部写完再一起调试到时定位布局问题会非常痛苦。5. 样式与资源让XML里的控件真正能看5.1 样式主题XML里的style只是引用很多刚开始用AWTK的人以为在XML里写一个style属性就能随便定义颜色、圆角、阴影像Web的CSS一样。实际上AWTK的样式体系不太一样样式的外观定义在主题资源文件里XML里的style属性只是一个引用名。比如你的主题文件里定义了一套名为primary的按钮样式设置了背景色、圆角、悬停效果等那么XML里写button nameok_btn xcenter ymiddle60 w160 h40 text登录 styleprimary/这个primary就是主题资源里某套样式的名字。如果你在XML里引用了一个主题里不存在的style名控件不会报错但会以默认样式显示也就是素颜状态。这个特性经常让人困惑因为界面上出现一个样式完全不对的按钮你却不知道问题出在样式名拼写还是主题加载失败。对于入门阶段我的建议是先别急着深入定制样式用AWTK默认的主题跑通一个完整流程再逐步学习主题资源的写法。样式这块的坑在于版本差异不同AWTK版本的主题资源格式有差别直接照抄网上旧代码不一定会成功遇到问题优先查你当前版本的文档和示例。5.2 图片资源怎么在XML里引用AWTK里的图片资源同样有固定的存放位置。通常把图片文件放到资源目录下的images文件夹里然后在XML中通过资源名引用。引用规则前面提过不带扩展名。比如你有一个文件叫bg_main.pngXML里写image x0 y0 w100% h100% imagebg_main/注意image控件里有两个image词很容易混淆。第一个image是控件类型表示这是一个图片控件第二个image是属性名用来指定图片资源名。初次接触的人经常会觉得这语法很奇怪但只要记住控件类型在前资源属性在后就不会乱了。图片资源如果加载失败通常表现为控件区域空白或者显示一个默认的占位图案。我个人遇到最多的情况其实就是资源文件忘了复制到目标目录或者图片格式不被当前编译环境支持。AWTK对不同图片格式的支持也跟编译配置有关你需要在项目里确认开了哪些图片解码能力否则格式正确也会加载不出来。5.3 字体与多语言容易被忽略的一环字体是很多新手的噩梦。AWTK默认可能只内置了英文字体如果你的界面上显示中文有可能出现一个个方框口口口。解决办法就是引入中文字体资源在主题或者具体控件上设置font_name来指定字体资源。我一般会在工程里单独放一个中文字体文件然后在XML控件里通过font_name属性指定它或者在全局主题里配置默认字体。涉及中文显示的界面建议一开始就把字体问题解决掉不要等项目做完才去发现中文全乱码。多语言场景下label和button用tr_text代替texttr_text填的是翻译条目的键名AWTK会根据当前语言环境自动显示对应语言的字符串。比如label xcenter ymiddle w80% h30 tr_textlogin_title/当语言环境是中文时login_title对应的中文文案登录会被显示出来切到英文环境时显示英文文案。这样界面语言切换就不需要改XML只需要改多语言资源文件。这个机制用起来确实方便不过我给你的建议是即便当前项目不做国际化也尽量把界面里需要动态展示的文本放在代码里设置不要在XML里写死太多文案否则后期改起来非常麻烦。6. 实操环节做一个带输入框和按钮的登录页Demo6.1 准备工程和资源目录讲再多的属性和布局不如亲手做一个完整小界面。下面带大家走一遍用AWTK做登录页的完整流程。新建工程时目录结构不同AWTK版本可能略有差异但核心资源目录基本是这几类ui目录放XML文件、images目录放图片、fonts目录放字体、styles目录放主题、strings目录放多语言字符串。我们这次只用到ui目录因为登录页不需要图片和特殊字体。工程里其他部分是AWTK框架的初始化代码包括主函数、平台适配层。这些直接用你当前版本提供的模板就好不用自己从零写。你要做的核心事情只有三件写XML、写C代码逻辑、运行资源生成脚本。6.2 编写login.xml登录页的需求很简单一个标题标签、两个输入框、一个登录按钮。XML我写成这样window namelogin anim_hinthtranslate label xcenter y30 w80% h30 font_size20 text登录/ edit nameuser_edit xcenter y80 w80% h32 input_typetext tips用户名/ edit namepass_edit xcenter y120 w80% h32 input_typepassword tips密码/ button nameok_btn xcenter y170 w80% h40 text登录/ /window这个界面用的是最简单的缺省布局每个控件的坐标都是手算的。标题在距顶部30像素的位置水平居中两个输入框分别在y为80和120的位置登录按钮在y为170的位置。宽度统一取80%保证在不同屏幕下都能有比较舒适的输入区域。注意两个输入框的input_type一个是text一个是password后者会在界面上把输入内容显示成掩码。tips属性分别写了用户名和密码这是输入框为空时显示的灰色提示文字能让界面更友好不用额外再放标签。我把文件名取为login.xml窗口的name属性也写成login这样代码里window_open(login)就能准确找到这个资源。这个习惯建议从一开始就养成。6.3 在C代码里打开窗口并绑定事件有了XML之后C代码的任务就是在程序初始化时打开这个窗口然后把登录按钮的点击事件绑定到处理函数上。核心代码大概是这样的#include awtk.h static ret_t on_ok_click(void* ctx, event_t* e) { widget_t* win (widget_t*)ctx; widget_t* user_edit widget_lookup(win, user_edit, TRUE); widget_t* pass_edit widget_lookup(win, pass_edit, TRUE); log_debug(user input: %s\n, widget_get_text(user_edit)); log_debug(pass input: %s\n, widget_get_text(pass_edit)); return RET_OK; } ret_t application_init(void) { widget_t* win window_open(login); widget_t* ok_btn widget_lookup(win, ok_btn, TRUE); widget_on(ok_btn, EVT_CLICK, on_ok_click, win); return RET_OK; }代码逻辑很清楚。window_open(login)负责打开login.xml对应的窗口返回窗口控件对象。widget_lookup在这个窗口下递归查找name为user_edit、pass_edit、ok_btn的控件。widget_on给按钮绑定EVT_CLICK事件点击按钮时就会触发on_ok_click回调ctx参数我们把win传了进去回调里就可以通过win继续查找其他控件。事件回调函数里的widget_get_textAWTK不同版本返回的字符类型可能存在差异。我在示例里简化处理了实际项目里如果需要把输入内容转成UTF-8字符串去使用建议查一下你当前版本提供的字符转换API。6.4 运行调试与常见卡点第一次跑这个demo有几个地方容易卡住。最常见的资源目录结构不对或者资源脚本没有执行window_open(login)会找不到窗口。如果程序能启动但等了两秒后没有反应去检查控制台输出通常会有资源加载失败的提示。还有一种是窗口打开了但控件全部挤在左上角。这种情况往往是XML文件的编码或者格式有问题标签没有闭合或者某个属性值写错导致解析中断控件树只建到一半。建议用带XML语法检查的编辑器编写文件至少保证标签闭合。控件位置错乱的卡点也很典型。如果你发现输入框位置不对先确认父容器是谁。比如你后面尝试把控件包进vbox容器却发现y坐标不生效了不要慌那是容器布局算法接管了子控件的位置。回到缺省布局y坐标才能手摆生效。还有一点界面里展现了中文但我们还没接中文字体很可能运行起来标题和按钮都是方框。遇到这种问题优先检查字体资源有没有正确打包进程序以及XML里有没有指定合适的font_name。7. 常见问题与排查技巧实录7.1 控件显示不出来优先查这三处界面里某个控件死活不显示这种问题几乎人人都会遇到。按我的经验按顺序查这三处基本都能定位。第一处资源有没有更新。这是AWTK里最经典的玄学问题。你改完XML没重新生成资源程序用的还是旧版本那界面当然不会变化。不用怀疑人生先跑一遍资源生成脚本。第二处坐标和尺寸。控件是否超出父容器可视范围宽度或者高度是否是0或者是否被一个透明控件遮挡住了。遮挡问题比较隐蔽因为你可能根本看不见那个挡路的控件。查的时候在代码里临时把这个控件的背景色加上或者直接禁用就能看出是不是它在捣乱。第三处控件名和类型是否写错。XML里的标签名不对解析器可能直接忽略它连报错都不给你。我建议写XML的时候先只写一个空控件跑通再逐步加属性这样即使出错也容易定位是哪一步引入的问题。7.2 文字图片不显示按这个顺序排查文字显示成方框或者图片区域空白这类资源问题的排查顺序其实很固定。先说文字。第一步查字体资源是否加载成功中文字体尤其要确认。第二步查控件属性里font_name是否指定没指定时会用默认字体。第三步查这个字号在当前字体里是否存在某些字体资源为了精简并没有全套字号。再说图片。第一步确认图片格式是否被当前编译环境支持。第二步确认image属性里的资源名是否正确且不带扩展名。第三步确认图片文件有没有拷贝到正确目录并参与资源打包。这三步走完九成图片问题都能解决。还有一个很常见的苍龙图片控件本身尺寸太小图片被按原始尺寸绘制结果只显示了左上角一小块区域。如果你想要图片拉伸显示或者居中显示还需要设置draw_type属性这就是为什么有人明明引用了图片却看不见——其实图片在只是没有按预期方式绘制而已。7.3 事件不触发多半是这四个原因代码里绑定了事件按钮点了没反应。我遇到过的情况基本是这四种。第一种控件name写错了。widget_lookup找不到控件返回NULL后续widget_on自然绑不上。给回调函数里加个断言快速定位是不是NULL。第二种控件被禁用或者不可交互。按钮enablefalse或者sensitive设置为false的时候界面不响应点击。如果你拿不准临时删掉这些属性再试。第三种控件被其他控件盖住。前面讲过XML里写在后面的控件绘制在上层如果上层有一个透明区域面积很大的控件比如一个透明view就会把下面的按钮完全挡住。可以用widget_set_enable或者调整z序的方式测试。第四种事件类型绑定错了。按钮点击是EVT_CLICK按键是EVT_KEY_DOWN这两个很容易搞混。确认一下回调绑定的事件类型和实际触发动作是否匹配。7.4 避坑清单最后把这个阶段常用的避坑点整理成清单方便你以后快速对照XML文件保存成UTF-8编码避免中文乱码。窗口name和XML文件名保持一致。资源文件修改后必须重新生成资源。子控件坐标相对父容器计算不要当成屏幕绝对坐标。容器布局下子控件的x、y会被布局算法接管手动写不生效。图片资源名不带扩展名。XML里的style名字必须存在于主题资源文件中。事件绑定前先确认widget_lookup找到了控件。中文字体一定要提前解决不要拖到界面做完才处理。复杂界面优先用容器嵌套不要硬算坐标。我个人的习惯是无论界面多简单都会先把窗口名和文件名统一然后每加一个控件就立即跑一次确认位置、层级、事件都正常最后再统一做样式美化。AWTK的XML本来就跟写配置文件一样轻别把它当成需要一次写完整的大工程迭代着来反而更省时间。