首页 > 代码库 > 第三章 按钮控件的创建

第三章 按钮控件的创建

一、前言

不知不觉一晃两个月过去,说来惭愧,在此期间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,这里为了方便,所有的工程都使用静态链接。

image

这个时候编译会出错:

image

在上一章中介绍过,如果使用“支持ID系统”的话,会生成winres.h文件,定义了系统中的控件ID,然后问题就出来了,大家可以先自己考虑下为什么

image

宏定义:#define 标识符 表达式

标识符中不能包含”-”(没看出什么问题的小伙伴们该补补C语言了)

这里我们把所有DUI-DEMO都替换为DUI_DEMO

image

编译成功!

image

此时,系统已经为我们生成一个按钮,接下来我们就对这个按钮进行改造

首先在布局文件(dlg_main.xml)里定位这个按钮:

image

我们可以看到控件的皮肤定义(图片)并未直接在布局中给出,但是我们可以看到很多控件都带有“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;

image

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" />

default_btn(按钮皮肤资源,对应按钮四种状态)

subwidth = 总长度/按钮数量 = 288/4 = 72

src=“皮肤资源ID”,在index.xml中定义,格式如下

2.定义皮肤资源

<resid type="IMGX" name="IDP_BTN_MYBTN"    file="image\mybtn.png"   />

image

3.替换原有class,查看效果

最后将btn_msgbox的class定义为btn_msgbox

image

重新编译,运行

image

还有一种方法同样可以。

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目录下的几篇文章

image

这里我们还是直接使用刚才定义的皮肤,效果如下

image

 

我们接着去掉skin再换上刚才的class试一下

image

这下是不是连字体都改变了,总上所述,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以外的其他属性都变更一下来看看效果:

这里将按钮文本颜色改成黑色,右对齐:

image

我们可以发现

为此我得出结论: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);}

image

刚才我们用的按钮控件在布局文件中的name标识来关联消息响应函数的,下面我们换一种方法。第一章中讲到,如果创建工程时,选择“支持ID系统”

系统会为我们生成winres.h文件,里面定义了我们定义控件的ID,下面我们通过此ID来关联一下:

image

如下代码所示:

		//DUI_NOTIFY_NAME_COMMAND("btn_msgbox2", OnMyBtnClick)		DUI_NOTIFY_ID_COMMAND(btn_msgbox2, OnMyBtnClick)

image

同样OK!

 

好了,这章的教程就到这里,go home.