首页 > 代码库 > 第三章 按钮控件的创建
第三章 按钮控件的创建
一、前言
不知不觉一晃两个月过去,说来惭愧,在此期间alterto一直没有再研究DuiEngine。主要是因为DuiEngine的作者现在构建一个新的界面库soui,而笔者也一直处于观望状态,因为DuiEngine的作者说了以后可能就不维护DuiEngine了,要把主要的经历放在SOUI上。alterto顿时犹豫了,既然这样我还为DuiEngine做什么教程啊。于是这两个月的时间里一直都很犹豫,也没有再出DuiEngine相关的教程。现在看来这种焦躁对于一个优秀的程序猿来说着实不应该,这也正暴露了出了国内很多和我一样的初学者所经常抱有的心态:”1.XXX语言还有没有前途 2.我是学XXX好还是学XXX好”。对于一个优秀的程序猿来说去抱怨这些问题是没有什么意义的,回归正题就算作者要转做SOUI,首先SOUI刚刚起步开始做,你永远不要妄想一个好的界面库在几个月内就成形,就拿DuiEngine来说,原本就是借鉴了金山开源界面库而来的,其后又经过了作者多次打磨与BUG提交才到现在的逐渐完善,这里我想说的是SOUI也同样是这样,前天DOWN了一个最新版本,发现BUG很多,毕竟几乎是由作者一个人完成的,疏漏之处在所难免;其次,DuiEngine的作者也说过SOUI在编程方法上会尽可能的与原来DuiEngine类似,也就说如果在一两年以后SOUI逐渐成型,我们可以轻易的从DuiEngine赚到SOUI上来。好了不发牢骚了,DuiEngine再次搞起,也希望SOUI在不远的将来能带给我们更多惊喜。
从今天开始,Alterto将带大家把DuiEngine界面库中所有的基本控件的创建过一遍。
下面带来第一个控件---按钮。
二、DuiEngine按钮初识
按钮控件,基本是所界面体系中最长用到的一类控件,而按钮又分为多种类型:标准按钮、单选按钮、复选按钮。
创建一个工程ButtonDemo,这里为了方便,所有的工程都使用静态链接。
这个时候编译会出错:
在上一章中介绍过,如果使用“支持ID系统”的话,会生成winres.h文件,定义了系统中的控件ID,然后问题就出来了,大家可以先自己考虑下为什么
宏定义:#define 标识符 表达式
标识符中不能包含”-”(没看出什么问题的小伙伴们该补补C语言了)
这里我们把所有DUI-DEMO都替换为DUI_DEMO
编译成功!
此时,系统已经为我们生成一个按钮,接下来我们就对这个按钮进行改造
首先在布局文件(dlg_main.xml)里定位这个按钮:
我们可以看到控件的皮肤定义(图片)并未直接在布局中给出,但是我们可以看到很多控件都带有“skin”和“class”属性,字面意思即“皮肤”和“样式”
这两个属性都在init.xml中给出定义
init.xml基本格式如下:
<DuiEngine> <font face="宋体" size=20><!--字体定义--> <string> <!--字符串定义--> <s id="1">按钮控件</s> </string> <skins> <!--为控件定义皮肤,目前笔者已知的有如下类型--> <!--单幅图片--> <imgframe name="dlg_frame" owner="DUI-DEMO" cache="1" src="http://www.mamicode.com/IDP_MAIN_DLG_FRAME" left="5" right="5" top="40" bottom="40"/> <!--多幅图片--> <imglst name="tabskin_default" src="http://www.mamicode.com/IDP_TAB_COMMON" subwidth="94"/> <!--普通按钮--> <button name="normalbtn" border="7D9EBC" bg="FBFCFD" bgup="FEFEFE" bgdown="C6E2FD" bguphover="FEFEFE" bgdownhover="DBEDFE" bguppush="C6E2FD" bgdownpush="FEFEFE"/> <!--水平滚动条--> <scrollbar name="sb_common" src="http://www.mamicode.com/IDP_SCROLL" margin="3" hasgripper="0"/> <!--垂直滚动条--> <vscrollbar name="sb_vertical" src="http://www.mamicode.com/IDP_VSCROLLBAR" margin="3"/> <border name="skin_menuborder" src="http://www.mamicode.com/IDP_MENUBORDER" left="2" top="2" border="2,2,2,2" key="FF00FF" alpha="100"/> <!--gif图片--> <gif name="face0" src="http://www.mamicode.com/IDP_GIF_FACE0"/> </skins> <!--样式定义,对控件外观进行定义,当然也可以在其中嵌套皮肤,有点类似于CSS中的类选择器--> <style> <class name="normalbtn" skin="normalbtn" font="0000" crtext="385e8b" crdisabled="91a7c0" textmode="25" cursor="hand" x-margin="0"/> ... ... </style> <!--为标准控件定义默认皮肤、样式--> <objattr> <button class="normalbtn"/> <check skin="btncheckbox"/> <radio skin="btnRadio"/> <imgbtn class="linkimage"/> <tabctrl tabskin="tabskin_default" crtext="000000" align="top" tabwidth="70" tabheight="38" tabspacing="0" tabpos="10" dotted="1"/> <divex sbskin="sb_common" sbwid="16"/> <edit transparent="1" x-margin="2" y-margin="2" ncskin="skin_editnc"/> <treectrl toggleskin="TreeToggle" checkskin="TreeCheckBox" critembg="FFFFFF" critemselbg="000088" critemtext="000000" critemseltext="FFFFFF" indent="17" itemmargin="4"/> </objattr> </DuiEngine>
我们看到可以看到msg box中class被定义为normalbtn,我们在<style>…</style>中找到他
<class name="normalbtn" skin="normalbtn" font="0000" crtext="385e8b" crdisabled="91a7c0" textmode="25" cursor="hand" x-margin="0"/>
其中定义了:
1.皮肤skin:nomarlbtn;
2.字体font:0000(表示用默认字体,见上述init.xml第二行);
3.crtext:文本字体颜色
4.crdisabled:按钮无效时文本字体颜色
5.textmode:文本样式,参考DrawText的Format Flags,
这里的textmode=”25” 25(十六进制) = DT_CENTER|DT_VCENTER|DT_SINGLELINE(水平垂直居中单行);
我查了下MSDN将各个flag所代表的意思和属性值在这里列一下:具体值和组合,大家可以自己查
值 说明 DT_BOTTOM 将正文调整到矩形底部。此值必须和DT_SINGLELINE组合。 DT_CALCRECT 决定矩形的宽和高。
如果正文有多行,DrawText使用lpRect定义的矩形的宽度,并扩展矩形的底部以容纳正文的最后一行。
如果正文只有一行,则DrawText改变矩形的右边界,以容纳下正文行的最后一个字符。
出现上述任何一种情况,DrawText返回格式化正文的高度,而不是绘制正文。DT_CENTER 使正文在矩形中水平居中。 DT_EDITCONTROL 复制多行编辑控制的正文显示特性。
特殊地,为编辑控制的平均字符宽度是以同样的方法计算的,此函数不显示只是部分可见的最后一行。DT_END_ELLIPSIS 对于显示的文本,如果结束的字符串的范围不在矩形内,它会被截断并以省略号标识。
如果一个字母不是在字符串的末尾处超出了矩形范围,它不会被截断并以省略号标识。
字符串不会被修改,除非指定了DT_MODIFYSTRING标志。DT_EXPANDTABS 扩展制表符,每个制表符的缺省字符数是8。
DT_WORD_ELLIPSIS, DT_PATH_ELLIPSIS和DT_END_ELLIPSIS不能和此参数一起使用DT_EXTERNALLEADING 在行的高度里包含字体的外部标头,通常,外部标头不被包含在正文行的高度里。 DT_HIDEPREFIX 忽略正文中的前缀字符(&),并且前缀字符后面的字母不会出现下划线。其它前缀字符的调用方式不受影响。
输入的字符串: "A&bc&&d"
正常: "Abc&d"
DT_HIDEPREFIX: "Abc&d"DT_INTERNAL 用系统字体来计算正文度量。 DT_LEFT 正文左对齐。 DT_MODIFYSTRING 修改给定的字符串来匹配显示的正文。
此标志必须和DT_END_ELLIPSIS 或 DT_PATH_ELLIPSIS同时使用。DT_NOCLIP 无裁剪绘制。当DT_NOCLIP使用时DrawText的使用会有所加快。 DT_NOFULLWIDTHCHARBREAK 在宽字符的字符串中防止行断开,因此折行规则相当于单字符的字符串。
例如,我们可以用在棒子版的windows中,为图标标签提供更好的可读性。
除非指定DT_WORDBREAK,否则此值没有作用。DT_NOPREFIX 关闭前缀字符的处理。
通常DrawText解释助记前缀字符,&为给其后的字符加下划线,解释&&为显示单个&。指定DT_NOPREFIX,这种处理被关闭。DT_PATH_ELLIPSIS 对于显示的正文,替换字符串在椭圆中的字符,以确保结果能在合适的矩形内。
如果该字符串包含反斜杠(\)字符,DT_PATH_ELLIPSIS尽可能的保留最后一个反斜杠之后的正文。
字符串不会被修改,除非指定了DT_MODIFYSTRING标志。DT_PREFIXONLY 仅仅在(&)前缀字符的位置下绘制一个下划线。不绘制字符串中的任何其他字符。
输入的字符串: "A&bc&&d"
正常: "Abc&d"
DT_PREFIXONLY:" _ "DT_RIGHT 正文右对齐。 DT_RTLREADING 当选择进设备环境的字体是希伯来文或阿拉伯文字体时,为双向正文安排从右到左的阅读顺序都是从左到右的。 DT_SINGLELINE 显示正文的同一行,回车和换行符都不能折行。 DT_TABSTOP 设置制表,参数uFormat的15"C8位(低位字中的高位字节)指定每个制表符的字符数,每个制表符的缺省字符数是8。
注意:DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL,
DT_NOCLIP, DT_NOPREFIX值不能和DT_TABSTOP值一起使用。DT_TOP 正文顶端对齐。 DT_VCENTER 使正文在矩形中垂直居中。
(DreamSmart注:此值必须和DT_SINGLELINE连用,否则GDI无法计算目的矩形)DT_WORDBREAK 断开字。当一行中的字符将会延伸到由lpRect指定的矩形的边框时,此行自动地在字之间断开。一个回车一换行也能使行折断。 DT_WORD_ELLIPSIS 截短不符合矩形的正文,并增加省略号。
6.cursor:光标移动到按钮上时的显示类型
7.x-margin:x方向外边距(具体应该可以参考CSS盒子模型)
由于按钮默认就是normalbtn样式,所以布局文件中我们将msg box中的class=“normalbtn去掉也无影响
三、自定义一种按钮class
1.创建新的按钮class:mybtn(<style></style>中添加)
<class name="mybtn" skin="mybtn" font="0000" crtext="385e8b" crdisabled="91a7c0" textmode="25" cursor="hand" x-margin="0"/>
为按钮定义皮肤,因为按钮有多种状态,一张图片肯定是放不下的所以这里我们使用<imglst></imglst>
<imglst name="mybtn" src=http://www.mamicode.com/"IDP_BTN_MYBTN" subwidth="72" />
(按钮皮肤资源,对应按钮四种状态)
subwidth = 总长度/按钮数量 = 288/4 = 72
src=“皮肤资源ID”,在index.xml中定义,格式如下
2.定义皮肤资源
<resid type="IMGX" name="IDP_BTN_MYBTN" file="image\mybtn.png" />
3.替换原有class,查看效果
最后将btn_msgbox的class定义为btn_msgbox
重新编译,运行
还有一种方法同样可以。
3.其他
在布局文件中 “msg box”不是使用class = “mybtn”改成 skin=”mybtn”这样就直接为按钮使用”mybtn”皮肤
而去掉了class “mybtn”中的一些额外属性。
四、使用<imgbtn/>标签创建按钮
细心的朋友可以发现在布局文件中使用<button></button>标签创建的按钮在点击后都会带有一个虚边框,很多时候这样是不允许的,其实DuiEngine中真正的图片按钮有
自己单独的标签<imgbtn>按钮名称</imgbtn>,<button/>一般只是用作普通按钮的定义,下面我们就用<imgbtn />标签创建一个按钮。
1.在布局文件dlg_main.xml中添加一个imgbtn,略掉class直接使用刚才定义的skin
<imgbtn pos="|-250,|-15,|-200,|15" name="btn_msgbox2" skin="mybtn">msg box2</imgbtn>
这里我简单介绍一下imgbtn的各个属性
pos:坐标体系,具体定义大家参照DuiEngine工程doc目录下的几篇文章
这里我们还是直接使用刚才定义的皮肤,效果如下
我们接着去掉skin再换上刚才的class试一下
这下是不是连字体都改变了,总上所述,skin只是为控件定义皮肤,而class可以为控件定义额外的属性(如字体)
下面我们做一个实验,来验证一个问题:init.xml中除skin属性以外的其他属性,是否会对<button/>造成影响?
此时我们的刚刚用到的<button/>和<imgbtn>都是通过class来定义自身样式的,我们将先前init.xml中定义的“mybtn”class属性
<class name="mybtn" skin="mybtn" font="0000" crtext="000000" crdisabled="91a7c0" textmode="02" cursor="hand" x-margin="0"/>除skin以外的其他属性都变更一下来看看效果:
这里将按钮文本颜色改成黑色,右对齐:
我们可以发现
为此我得出结论:class中除skin外的其他属性对<button/>无影响
注意:这里的这个论证并不完全,大家有时间可以把其他属性也验证一下。
最后我们来做下按钮的消息响应,弹出一个windows的MessageBox
模板程序中,将对MainDlg的所有消息响应放到了UIHander中去,所以我们为mybtn2添加消息响应也该放到这里
这里我们在DUI_NOTIFY_MAP_BEGIN()—END()间添加消息映射
DUI_NOTIFY_MAP_BGIN()
DUI_NOTIFY_NAME_COMMAND("btn_msgbox", OnMsgBtnClick)DUI_NOTIFY_NAME_COMMAND("btn_msgbox2", OnMyBtnClick)
DUI_NOTIFY_MAP_END()这里用到了一个宏定义DUI_NOTIFY_NAME_COMMAND(按钮name属性,响应函数)
来完成对按钮的消息映射。
接着我们自定义消息响应函数:
//声明void OnMyBtnClick();//定义void CUIHander::OnMyBtnClick(){ ::MessageBox(NULL,L"Enjoy DuiEngine",L"提示",MB_OK);}
刚才我们用的按钮控件在布局文件中的name标识来关联消息响应函数的,下面我们换一种方法。第一章中讲到,如果创建工程时,选择“支持ID系统”
系统会为我们生成winres.h文件,里面定义了我们定义控件的ID,下面我们通过此ID来关联一下:
如下代码所示:
//DUI_NOTIFY_NAME_COMMAND("btn_msgbox2", OnMyBtnClick) DUI_NOTIFY_ID_COMMAND(btn_msgbox2, OnMyBtnClick)
同样OK!
好了,这章的教程就到这里,go home.