转载请说明原出处,谢谢~~
我使用duilib快3个月了。总体感觉duilib的使用还是较为简单的,仅仅是刚入门时可能有些摸不清头脑。今天写一篇关于duilib的入门日志,大致说一下duilib中的各个布局的作用,以及非常关键的相对布局与绝对布局的意义与使用方法。
希望能够帮到使用duilib的新手朋友们。duilib高手就能够直接省略这篇文章了!
我刚使用duilib的时候非常依赖duilib自带的设计器,用他能够拖拉控件。可视化的做出自己想要的界面。可是用一段时间就会发现原带的设计器有非常多bug,时不时会崩溃,支持的控件数量有限,属性数量也有限,导出的代码冗余。当时问了几个高手,大家建议不要使用设计器而应该自己手写xml代码。
起初手写时感觉特别麻烦,可是用几天后你会发现手写要比使用设计器好得多:你能够更加了解duilib,熟悉每一个控件的各个属性。对控件的控制也更加方便。而假设想称心如意的脱离设计器去编写xml文件,有非常有必要弄明白各个布局的使用方法和布局技巧。
我如今能够全然靠手写xml来做出一个程序的界面,相信用了一段duilib的朋友也是这样。
在这里提醒一下新手朋友,在duilib的根文件夹有一个属性列表.xml的文件,他包括了绝大多数控件的绝大多数属性的介绍,有不懂的属性记得时常翻看他。同一时候不得不说这个文件包括的属性的确是不全面的,想要知道最全面的属性信息,能够看每一个控件的源代码。在SetAttribute函数中能够看到最全面的属性信息。
要想手写xml,当然必须有一个编写工具。原则上仅仅要是能够编写文本的工具都能够。大家依据习惯自己挑选适合的工具。我眼下在使用的是sublime这款工具。感觉编写xml非常方便,使用界面也不错。
6大布局的作用:
duilib的Layout文件夹专门放置布局相关的容器控件。这6个布局分别为:Container、VerticalLayout、HorizaontalLayout、TileLayout、TabLayout、ChildLayout。容器之间能够随意相互嵌套,我分别说明他们的使用方法。
首先我要说明一下。以下介绍的时候,我都默认觉得每一个控件的float属性为false,也就是不使用绝对定位,这个属性会打破各个布局的作用。
Container:
Container布局是其它全部布局以及含有容器特性(如CList、CListContainerElement)的控件的基类,而实际上开发过程中非常少使用这个布局。仅仅用他来做其它更高级的布局的基类。由于Container布局中的全部控件都会自己主动填充满整个布局,全部的控件都叠到了一起,假如恰好有什么需求要让子控件都覆盖起来。并且能够随着容器的改变而自适应填充的话,Container就是不二之选。而除了这个效果之外。一般我们不使用它。
VerticalLayout、HorizaontalLayout:
VerticalLayout与HorizaontalLayout布局无疑是duilib中最常使用的两个布局,巧妙的使用这两个布局能够满足大多数的布局需求。
从单词的意思上不难看出VerticalLayout是纵向布局,HorizaontalLayout是横向布局。这门两个直接继承自Container布局。
VerticalLayout布局会让他包括的元素都纵向排列开,HorizaontalLayout布局会让给他包括的元素都横向排列开:如图
我有益没让控件填满整个容器。为了说明这两个布局不会强行让子元素的总和去填满容器,纵向布局会从上到下依据每一个控件的高度让他们排到一起,横向布局会从左到右依据每一个控件的宽度让他们排到一起。
另外能够看到,纵向布局仅仅关心子元素的高度,而不会强行让子元素的宽度等于容器的宽度。这点从图片能够看到,横向布局同理也是仅仅关心子元素的宽度。
而这两个布局经常会嵌套使用。例如以下效果:
能够看到我最外层使用了一个纵向布局,他包括了横纵横三个布局(分别为红绿蓝颜色),每一个横向布局里又分别包括了几个button。我们在编写界面时经经常使用到这种方法!以下是这个布局效果图相应的xml代码:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <Window size="300,226"> <VerticalLayout width="300" height="318" bkcolor="#FFFFFFFF"> <HorizontalLayout width="300" height="65" bkcolor="#FFFF0000"> <Button text="button測试" width="60" height="35" /> <Button text="button測试" width="60" height="63" /> <Button text="button測试" width="60" height="35" /> </HorizontalLayout> <VerticalLayout width="300" height="96" bkcolor="#FF00FF00"> <Button text="button測试" width="60" height="64" /> <Button text="button測试" width="60" height="75" /> <Button text="button測试" width="60" height="64" /> </VerticalLayout> <HorizontalLayout width="300" height="65" bkcolor="#FF0000FF"> <Button text="button測试" width="60" height="47" /> <Button text="button測试" width="60" height="64" /> <Button text="button測试" width="60" height="64" /> </HorizontalLayout> </VerticalLayout> </Window>
TileLayout:
TileLayout布局是用来做相似360工具箱的效果:
在前面的文章里,我写的《duilib中ListCtrl控件的实现》和《仿酷狗音乐播放器开发日志十三——左側功能块的完好》正是使用了这个布局完毕的。这个布局有有两个关键属性:
<Attribute name="columns" default="1" type="INT" comment="列数,如(4)"/> <Attribute name="itemsize" default="0,0" type="SIZE" comment="子项固定大小。如(128,128)"/>
columns和itemsize属性,这两个属性不能一起用。应该仅仅是用当中的一个。
使用columns属性能够来设置每行中包括的列数。他会自己主动把包括的元素从左到右从上到下依照columns属性的设置排列起来,他把每行的列数固定死了。而itemsize有两个字段,通过我读源代码,发现第二个字段是无效的。我们仅仅要使用第一个字段即可了,他会设置每一个元素所占的区域。比方容器的宽度是500,给itemsize设置为 100 x 10,那个每行就会容纳5个元素,当我们拉伸了窗口让容器宽度变为700。那么每行就会自己主动容纳7个元素,这意味着使用这个属性会让每行容纳的元素个数是自己主动可变的!这在非常多情况下是非常实用的属性。注意itemsize并非直接设置了子控件的大小。而仅仅是限制了子控件的最大区域。比方itemsize为100x100。而子控件设置为50x50。那么 终于的子控件大小为50x50,而子控件的位置会依照100x100来计算。这个希望读者自己实践一下来理解这个效果!
TabLayout:
TabLayout布局相同经常使用,他就像MFC的选项卡CTabCtrl控件。如图:
可是在duilib中TabLayout仅仅是以下的布局界面,而不包括顶端的选项卡button。所以经经常使用Option控件配合他一起使用,使用他时他会把他包括的下一级元素作为一个页面,所以我们通常在他里面放入横纵向布局来作为一个页面,在横纵向布局里再规划每一个页面的外观。
这个控件的具体使用方法大家能够看duilib自带的360demo,我就不赘述了!
ChildLayout:
ChildLayout布局比較少用,由于他的功能能够用其它布局来取代,他的作用就是从一个xml文件里载入布局来嵌入到ChildLayout布局所在的地方,使用这个布局一般仅仅须要指定xmlfile属性来设置xml文件位置就能够了。他的意义在于能够把繁杂的大量xml代码分隔开。比方他和TabLayout布局结合,让TabLayout布局包括5个ChildLayout布局,而每一个ChildLayout布局分别从5个xml文件载入自己的布局文件,这样就能够分块化的编写布局代码。
实际上有个比他更好用的标签,就是Include标签。Include不属于布局,但他的作用在布局方面非常相似ChildLayout,指定他的Source属性到某个xml文件就能够了。相对ChildLayout,Include的长处是能够自己主动识别自己定义控件,而ChildLayout不能够!
在这里要提一下360Demo的自己定义控件。这个demo的自己定义控件做法误导了非常多人,里面使用了自己定义控件的方法,把一个xml布局文件嵌入到界面里。这样的做法全然不是必需,直接使用Include标签,一句xml代码能够全然取代自己定义控件。
这里给出一个Include的使用方法:
<Include source="LoginScrollBar.xml" count="1" />
当中source属性指定xml文件的路径。count指定解析的次数。
绝对布局的意义与使用方法:
在知道了6大布局的使用方法之后,知道了各种样式的界面外观的大致布局方法,而这还远不够让我们写出美丽的布局外观。仅仅有配合相对布局与绝对布局才干够更好的控制界面的元素。
值得一提的是。我这里说的相对布局和绝对布局并非一个容器或者控件,这仅仅是一种技巧和使用方法,用在容器布局所包括的控件上,经常使用到横纵向布局中。
我先来介绍绝对布局,笼统上说绝对布局和相对布局事实上仅仅有一个区别。也就是我在前面提到的float属性。容器中包括的控件float属性为真就是绝对布局。为假就是相对布局。不要小看这一个属性,他带来的效果能够天差地别。
给控件的float属性设为真后,就使用了绝对布局,故名意思,绝对布局就是让控件的坐标绝对化,这样这个控件就不受他的容器的束缚而能够自己随意设置自己的位置!
比方在横纵向布局中给他们包括的子控件设置float属性,这个控件就不会被自己主动横纵向排列。而我的建议是。能不用绝对布局就别用绝对布局!
原因有三个:
1)绝对布局破坏了各个容器的特性。而不受容器的束缚。
2)绝对布局让控件的坐标固定,不利于控件自己主动调节位置。
3)后面提到的相对布局差点儿能够完毕绝对布局的全部特性。
那么为什么要用绝对布局,由于他的一个功能是相对布局无法完毕的。就是让控件或者布局重叠或者相交!
有的时候我们必须这样做来让控件组合起来达到一些效果。我能够明白的说。我在做仿酷狗播放器的过程中,整个xml布局代码仅仅用了2个绝对布局。一个是编写搜索栏《仿酷狗音乐播放器开发日志二——搜索栏的编写》,一个是编写电台控件。如图:
另外一个非常经典的使用绝对布局的样例就是我前几天写的《用duilib制作仿QQ2013动态背景登录器》。这些样例都是由于要让控件重叠起来组合出新的控件才使用了绝对布局。假设不让控件重叠或者没有特殊需求,最好别用绝对布局。
尽管不建议使用,但我也得说一下绝对布局相关的属性和使用技巧。
1)把float属性设置为真。
2)设置pos属性,这个属性在float为真时才有效,他包括四个字段。分别以为了控件的左上右下下个坐标的位置,可是建议仅仅指定前两个字段来设置控件的左上角的坐标。控件的宽度用width和height属性来控制。这样做的长处是避免了计算右下角坐标的繁琐!以后改动的时候也非常清晰。
举出一个样例:
<Label name="MusicName" float="true" pos="100,100" width="26" height="22" textcolor="#FF505050" endellipsis="true" />
这里设置了一个Label控件,左上角放到100,100的坐标上。控件高22。宽26
不得不提一个非常实用的属性,那就是relativepos属性,属性列表没有列出这个属性,这是我自己总结的
<Attribute name="relativepos" default="0,0,0,0" type="RECT" comment="设置相对移动。float为真时,分别表示横纵向位移值。横纵向缩放值,移动单位建议50或100"/>
用了这个属性,就能够让控件拥有相对布局的一部分特性,那就是依据容器的大小,自己能够调整位置和大小!这个特点我在《仿酷狗音乐播放器开发日志二——搜索栏的编写》用到了,是为了让搜索button能够自己移动。这个属性的前两个字段表示横纵向的位移值。后两个字段表示缩放值,具体效果大家应该自己实践一下!另外这个属性默认是有严重bug的,就是窗口最小化再恢复后有这个属性的控件会自己主动无规律偏移。这个bug我修复了。详见《仿酷狗音乐播放器开发日志二——搜索栏的编写》。
这样就介绍完了绝对布局。然后就是整片文章的最重要部分。相对布局。
相对布局的意义与使用方法:
我把相对布局的介绍放到最后。由于它非常重要。
在容器内部使用控件或者容器时。float属性设置为flase(duilib默觉得false)就是相对布局了。这是我非常推荐使用的,前面我也说了我在写仿酷狗的整个布局中,上百个控件中我仅仅给两个控件使用了绝对布局。其余都是相对布局。
他的长处例如以下:
1)布局和控件是能够依据窗口的大小改变而自己主动调整位置的,这点非常重要
2)不须要绝对布局那样麻烦的计算各个控件的位置
3)在容器中调整前一个控件的位置,后面的控件都会自己主动调整坐标
事实上总得来说使用相对布局意义就是使用布局控件的自己主动排列特性!
使用了相对布局后,就不用设置float属性和pos属性,一般仅仅设置甚至不设置width和height属性。
这点非常重要。假设你的控件或者布局的大小是固定的,那么就设置width和height属性。假设想让控件或者布局依据窗口的大小而自己主动调整大小和位置,就不设置这两个属性。假设仅仅设置了一个属性,比方width设置为100,而height不设置,那么他的高度是自己主动调整的而宽度是固定的。
父容器会自己主动安排他包括的元素,让含有width和height属性的控件占领相应的大小,把剩下的空间都分配给没有设置wieth和height属性的容器或者控件里。看以下一个样例:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <Window size="300,300"> <VerticalLayout name="Father"> <HorizontalLayout name="Sub1" height="50" > </HorizontalLayout> <VerticalLayout name="Sub2"> </VerticalLayout> <HorizontalLayout name="Sub3" height="50" > </HorizontalLayout> </VerticalLayout> </Window>
能够看到窗口的大小为300x300。而最外层的是一个名为Fahter的纵向容器,包括三个子容器。而Father没有指定width和height,所以当我改变了窗口的大小时,Father会自己主动调整自己的大小到和窗口大小相同,而三个子容器我都眉头指定width属性。所以三个子容器的宽度和Father是一样。也就是他们的宽度都是和窗口宽度一样,并且会自己主动调整。Sub1和Sub3的高度设置为50,所以他们的高度就固定了,而Sbu2的高度也没有指定,那他会自己主动占领了除了Sub1和Sub2的全部空间!事实上这个样例的布局是非经常见的界面布局样例。比方说酷狗。sub1和sub3分别表示标题栏和状态栏,sub2为程序的主界面:
关于酷狗的更加具体的布局分析能够看我前面写的博客《仿酷狗音乐播放器开发日志——总体框架分析》。差点儿每开发酷狗的一部分,我都会把布局分析一下写出来。
把这些知识综合起来。如今就能够写出一个自己主动调整大小的大致布局了,可是还没法精确控制每一个控件的位置。
利用上面介绍的自己主动占位的特性。我这里举一个标题栏编写的样例:
<HorizontalLayout name="background" height="30" bkimage="file='UIBKImage4.jpg' source='0,0,1000,30'" ><!-- 标题栏 --> <Label text="皮肤与窗口调整——Redrain播放器" textcolor="#FFFFFFFF" textpadding="5,0,0,0" font="1" autocalcwidth="false" width="300"/> <Control /> <Button name="closebtn" width="23" height="18" padding="0,6,0,0" normalimage="UI itleclose_normal.png" hotimage="UI itleclose_hover.png" pushedimage="UI itleclose_down.png" /> </HorizontalLayout>
这个样例是我的仿酷狗播放器的换肤窗口的标题栏。他的外层是一个横向布局。高度为30,宽度随窗口调整。让标题文字居于左側,关闭button在最右側,假设让窗口调整宽度后文字和关闭button自己主动调整位置。就须要把中间的空位占满。
这时我们就须要一个占位控件,不给他设置width和height属性。这样子他就会自己主动占领剩余的空间!就达到了相对布局的自己主动调整位置的效果。这个使用方法是相对布局里非经常常使用的!
而这个占位控件在没有什么其它要求时建议像我给出的样例那样。使用Control控件,由于他是全部控件和容器的祖先基类,代码和属性相对是最少的,这样有利于提高程序的效率。
在充分理解了占位的技巧后。再配合一些微调属性,就能够完美控制各个控件了,这几个微调属性各自是inset、padding、childpadding,这几个属性的介绍例如以下:
<Attribute name="padding" default="0,0,0,0" type="RECT" comment="外边距,如(2,2,2,2)"/> <Attribute name="inset" default="0,0,0,0" type="RECT" comment="容器的内边距,如(2,2,2,2)"/> <Attribute name="childpadding" default="0" type="DWORD" comment="子控件之间的额外距离,如(4)"/>
在float为假,也就是相对布局中,这几个属性才起效。
inset属性
这是给容器控件使用的。使用后他所包括的全部使用相对布局的元素。都会被限制在设置的范围内,适合对容器内所用元素进行总体的坐标控制。
比方在前面提到的做360工具箱时。我们使用TileLayout容器来存放每一个工具,我们首先设置inset属性。就能够让全部的工具项限制在一定范围内,样例例如以下:
<TileLayout name="listctrl" width="540" height="400" inset="20,20,0,20" childpadding="20" vscrollbar="true" columns="2" bkimage="CustomListList_bk.png" itemhotimage="CustomListListItem_bk_hover.png" itemselectedimage="CustomListListItem_bk_hover.png"> </TileLayout>
通过设置inset属性。让全部元素限制在一定范围内而不用反复设置每一个元素的属性。
childpadding属性:
childpadding属性设置容器内每一个元素之间的间距,这个比較easy理解。上面的样例中也用到了childpadding属性。这个属性在不同容器中代表不允许思,在横向布局中代表子控件之间的横向间隔,在纵向布局中代表子控件之间的纵向间隔。在TileLayout容器中代码行与行之间的间隔!
padding属性:
padding属性是相对布局中最经常使用的属性。用来设置相对于前一个控件的位置。这个属性的控件位置微调的关键。
一般仅仅用他的前两个字段,设置左边距和上边距,后两个字段是无效的,或者说存在问题(为什么会有问题?请看源代码)。只是使用这两个字段就够了。
这是我的仿酷狗播放器的状态的布局代码:
<HorizontalLayout height="30" inset="77,0,0,0" bkimage="UIstatusbarstatus_bk.png"><!-- 状态栏 --> <Button width="31" height="30" padding="0,0,0,0" normalimage="UIstatusbaradd_normal.png" hotimage="UIstatusbaradd_hover.png" pushedimage="UIstatusbaradd_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UIstatusbarlocate_normal.png" hotimage="UIstatusbarlocate_hover.png" pushedimage="UIstatusbarlocate_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UIstatusbarsearch_normal.png" hotimage="UIstatusbarsearch_hover.png" pushedimage="UIstatusbarsearch_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UIstatusbarsetting_normal.png" hotimage="UIstatusbarsetting_hover.png" pushedimage="UIstatusbarsetting_down.png" /> <Control /> <Label text="Redrain仿酷狗音乐盒DirectUI ^_^" textpadding="0,6,0,0" width="30" font="0" /> </HorizontalLayout>
使用padding属性,这是底部这四个button的相对位置。
假设我想总体让这四个控件向右位移10像素,那么我仅仅要设置第一个button的padding属性为padding="10,0,0,0",就能够了。其它布局全然不须要改动。
结束语
讲到这里也就把布局的知识总结得几乎相同了,对于duilib的新手朋友,建议多看看各个demo的布局文件,他们差点儿涵盖了全部知识点。然后结合我总结的东西自己手动写几个布局代码,相信非常快就能够熟练编写界面布局了。另外我的前面博客里经常会分析布局。大家也能够看看。
我使用duilib的时间也不算长。水平有限,文章中有什么错误或者描写叙述不清,请留言给我,我会及时纠正。
Redrain 2014.8.13