第三章 布局
布局用于定义容器如何组织内部子元素和控制子元素的大小。在一个应用程序中,作为定义容器的组织形式,布局是一个十分重要的组件。是显示单个子元素?还
是垂直或水平显示多个子元素?这些均由布局来定义。并且布局将占用应用程序大部分的呈现时间。extjs4中对布局进行了重大的修整。下面我们将学习并熟
悉extjs中的布局。
本章目录如下:
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/19/2917233.html#a3.1.">3.1. extjs 4 布局</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/19/2917233.html#a3.2.">3.2. container布局</a>
<a href="http://www.cnblogs.com/fsjohnhuang/archive/2013/02/19/2917233.html#a.3.4">3.4. 总结</a>
extjs4对布局进行了重大的修整。布局引擎已被重写,但api仍保持原样,从而使得布局是向后兼容的。当然所生成的html标签也作出了修整。
extjs2中引入了布局这一特性。当时其具有良好的性能和速度,但欠缺灵活性。后来的版本增强了灵活性却牺牲了性能(译者语:extjs3在性能方面
确实令不少人望而却步啊!)。在extjs4中对加载速度、性能和灵活性等方面都作出了改进,通过下面的图例我们可以看到改进结果:

除了上述内容,extjs4还引入两类型的布局:container 布局 和 component 布局。
component 布局:负责组织组件的html元素;
包括:dock布局、toolbar布局、field布局、triggerfield布局。
container 布局:负责容器内容extjs元素和调整extjs元素的大小。
包括:border布局、box布局、fit布局等等。
(译者语:如果接触过extjs旧有版本的朋友应该对container布局不陌生了,而component布局是extjs4独有的,但不由开发人员来设置、调用,而是框架中的组件已内置好的。)
首先我们来看看container布局的层级图吧!
当没有为容器(container或其子类实例)配置layout配置项时就会使用auto布局。
特点:
1. 容器子元素不随容器大小变化而变化;
2. 容器子元素按添加到容器的顺序自上而下排列。
实例:
var panel1 = ext.create('ext.panel.panel', {
title: 'panel 1',
html: 'panel 1',
height: 60,
width: 100
});
var panel2 = ext.create('ext.panel.panel', {
title: 'panel 2',
html: 'panel 2',
height: 80,
width: 60
var panel3 = ext.create('ext.panel.panel', {
title: 'panel 3',
html: 'panel 3',
height: 65,
width: 100
var panel4 = ext.create('ext.panel.panel', {
title: 'panel 4',
html: 'panel 4',
height: 70,
width: '90%'
var auto = ext.create('ext.window.window', {
title: 'auto layout',
width: 100,
height: 320,
//layout:'auto',
defaults: {
bodystyle: 'padding:15px'
},
items: [panel1, panel2, panel3, panel4]
});
上述实例创建了四个panel组件实例并作为window组件实例的子元素。
defaults:用于统一设置其子元素的配置项。
结果如下:
1. 如果我们放大或缩小window组件实例,其内部的panel1、panel2、panel3和panel4的大小将不会随之变化;
2. panel4中width值为90%表示该元素的宽度为父容器body宽度的90%。(在chrome中使用百分比来设置宽度会出现没有右边框的异常)
1. 容器子元素会随容器大小而变化;
2. 子元素按添加到容器的次序,自上而下的排列;
3. 子元素默认宽度为容器的body宽度。
4. 通过子元素的x、y配置项可设置子元素离原来位置的左边距和上边距(效果如同position:relative,top:...,left:....)
实例:
html: '100% 30%',
anchor:'100% 30%'
html: '80% 25%',
anchor:'80% 25%'
html: '-70 20%',
anchor:'-70 20%'
html: '-30 25%',
anchor:'-30 25%'
var anchor = ext.create('ext.window.window', {
title: 'anchor layout',
width: 250,
height:300,
layout:'anchor',
bodystyle: 'padding:10px'
anchor.show();
从
实例我们可以知道,除了容器的layout配置项设为anchor后,若要定制子元素的布局细节就必须设置设置子元素的anchor配置项。
(ext.panel.panel中有anchorsize这一配置项,但与布局中上述的anchor配置项功能不同,无法实现上述效果)
anchor值为字符串,若形如"80"、"-30"或"80%"表示设置子元素占容器的宽度;而形如"80 90"或"90 80%"等表示设置子元素占容器的宽度和高度。
80等正数表示子元素离父容器左内边框、上内边框的距离;
-30等负数表示子元素离父容器右内边框、下内边框的距离;
80%等百分数表示子元素占父容器宽度、高度的百分比。注意:对于高度使用百分比值时,每个子元素的最终的值都是“百分比*父容器的高度”。
改变容器大小后
absolutelayout是anchorlayout的子类,其继承了anchorelayout的所有特性。并且可以设置x和y配置项来相对与父容器来定位子元素。
1. 容器子元素会随容器大小而变化;
2. 子元素默认宽度为容器的body宽度;
3. 子元素默认位置为容器的左上角(x:0,y:0),就是不设置各子元素的x、y配置项时,所有子元素会在容器的左上角重叠;
4. 子元素通过x、y配置项来设置子元素离容器左内边框、上内边框的距离。
html: 'x: 10; y: 10 - anchor: 80% 80%', /*this config option will display the given text inside the panel*/
anchor:'80% 80%',
x: 10,
y: 10
var absolute = ext.create('ext.window.window', {
title: 'absolute layout',
width: 300,
height: 200,
layout:'absolute',
items: [panel1]
absolute.show();
注意:子元素的anchor属性,对于高度使用百分比值时,每个子元素的最终的值都是“百分比*父容器的剩余高度”,父容器的剩余高度=父容器的高度-较早添加到容器的子元素高度。所以子元素添加到容器的次序将在使用百分比设置子元素anchor属性高度时起作用。
hbox布局组织子元素在容器内水平排列。
1. 容器子元素会随容器大小而变化;
2. 通过三个可选的配置项来组织子元素相对于容器的宽和高。
配置项介绍:
1. flex:各个子元素的的flex配置项会相互作用来决定子元素在容器内的宽度。如子元素1的flex为1,而子元素2的flex为2,而容器宽度为90,那么子元素1的宽度就为30,而子元素2的宽度为60。
2. align:设置各个子元素的水平方向的对齐,可选值有:
top:默认值,顶对齐;
middle:中心线对齐;
strech:使各个子元素的高度为容器body的高度来对齐;
strechmax:以最高的子元素的高度作为其他各个子元素的高度来对齐。
3.pack:设置所有子元素组成的元素块是紧靠容器的左、中、右中哪个位置,可选值有:
start:默认值,靠容器的左边;
center:靠中间;
end:靠容器的右边。
1配置项为子元素的配置项;2和3为layout属性的配置项。
html: 'panel 1', //this text will be displayed on the panel body
flex: 1
html: 'panel 2', //this text will be displayed on the panel body
flex: 3
var hbox = ext.create('ext.window.window', {
title: 'hbox layout',
height:100,
layout: {
type: 'hbox',
align: 'stretch'
bodystyle: 'padding:10px'
items: [panel1, panel2]
hbox.show();
结果:
vbox布局与hbox相似,不同的是它用于组织子元素在容器内部垂直方向的布局。
1. 容器子元素会随容器大小而变化;
1. flex:各个子元素的的flex配置项会相互作用来决定子元素在容器内的高度。如子元素1的flex为1,而子元素2的flex为2,而容器高度为90,那么子元素1的高度就为30,而子元素2的高度为60。
2. align:设置各个子元素的垂直方向的对齐,可选值有:
left:默认值,左对齐;
center:中心线对齐;
strech:使各个子元素的宽度为容器body的宽度来对齐;
strechmax:以最宽的子元素的宽度作为其他各个子元素的宽度来对齐。
flex: 2
html: 'panel 2',
var vbox = ext.create('ext.window.window', {
title: 'vbox layout',
width: 82,
height: 300,
type: 'vbox',
bodystyle: 'padding:15px'
vbox.show();
accordion布局是vbox布局的子类。与vbox布局不同的是accordion布局仅仅会展现其中一个子元素而其余子元素将被折叠起来。
2. 仅仅会展现其中一个子元素而其余子元素将被折叠起来或仅仅展现部分子元素而其他元素被折叠。
1. multi:默认为false,true表示可以同时展现多个子元素;
2. collapsefirst:默认为false,false表示expand/collapse按钮在标题工具按钮栏的最左边,true表示在最后边。(关于标题工具按钮栏ext.panel.tool我们将在后面学习)
3. animate:默认为true,false为执行expand/collapse操作时没有动画效果
4. fill:默认为true,true表示expand的子元素高度将为整个容器剩余高度,false表示expand的子元素高度为自身的高度
1、4为layout配置项的属性,2、3为子元素的配置项。
(译者语:本人试了一下activeontop这一配置项,但未成功。)
html: '<b>panel 1</b>'
html: '<b>panel 2</b>'
html: '<b>panel 3</b>'
html: '<b>panel 4</b>'
var panel5 = ext.create('ext.panel.panel', {
title: 'panel 5',
html: '<b>panel 5</b>'
var accordion = ext.create('ext.window.window', {
title: 'accordion layout',
margins:'5 0 5 5',
split:true,
width: 210,
height:250,
layout:'accordion',
bodystyle: 'padding:35 15 0 50'
items: [panel1, panel2, panel3, panel4, panel5]
accordion.show();
使用table布局那么将生成html的table标签来作为布局的容器。
1. 可通过配置来设置容器子元素的大小是否随容器的大小变化
1. columns:设置表格布局的列数;
2. tableattrs:设置表格的属性(如style等)
3. trattrs:设置行的属性
4. tdattrs:设置单元格的属性
5. colspan:设置跨列数目
6. rowspan:设置跨行数目
1、2、3和4为layout配置项的属性,5、6为子元素的配置项。
注意:子元素的排列次序为由左至右,由上到下。
var table = ext.create('ext.window.window', {
title: 'table layout',
type: 'table',
columns: 3,
tableattrs: {
style: {
width: '100%',
height: '100%'
}
items:[{
html:'cell 1',
rowspan: 3 //this cell will span 3 rows
},{
html:'cell 2'
html:'cell 3'
html:'cell 4'
html:'cell 5'
html:'cell 6',
colspan: 2 //this cell will span 2 columns
html:'cell 7'
html:'cell 8'
html:'cell 9'
}]
table.show();
column布局为auto布局的子类,用于设置子元素的宽度。
1. 容器子元素的宽度会随容器宽度而变化;
2. 容器子元素的高度不随容器高度而变化;
1. columnwidth:设置子元素的宽度(值必须为百分数或小于1的小数)
1用作子元素的配置项。
html: '.25',
columnwidth: .25 //means 25%
html: '1/2',
columnwidth: 1/2 //means 50%
var column = ext.create('ext.window.window', {
title: 'column layout',
width: 400,
layout:'column',
items: [panel1, panel2, panel3]
column.show();
fit 布局是容器内只能显示一个子元素,若设置多个子元素,则只显示第一个子元素。
1. 容器内只能显示一个子元素,若设置多个子元素,则只显示第一个子元素。
2. 容器子元素随容器大小变化。
var panel1 = ext.create('ext.panel.panel', {
bodystyle: 'padding:15px',
html: 'fit content'
var fit = ext.create('ext.window.window', {
title: 'fit layout',
height: 150,
layout:'fit',
items: [panel1]
fit.show();
card布局是fit布局的子类。
1. 容器内只能显示一个子元素,若设置多个子元素,则只显示第一个子元素;
3. 容器子元素随容器大小变化。
var card = ext.create('ext.window.window', {
title: 'card layout',
layout: 'card',
activeitem: 0,
bodystyle: 'padding:70 50 0 150',
border:false
bbar: [{
id: 'prevbutton',
text: 'preivous step',
handler: navhandler,
disabled: true
'->',
{
id: 'nextbutton',
text: 'next step',
handler: navhandler
}],
items: [{
html: '<p>step 1 of 3</p>'
html: '<p>step 2 of 3</p>'
html: '<p>step 3 of 3</p>'
card.show();
var navhandler = function(btn) {
var activeitem = card.layout.activeitem;
var active = card.items.indexof(activeitem);
if (btn.id == 'nextbutton') {
active += 1;
else if (btn.id == 'prevbutton') {
active -= 1;
card.layout.setactiveitem(active);
var prev = card.dockeditems.items[1].items.items[0];
var next = card.dockeditems.items[1].items.items[2];
if (active == 0){
prev.setdisabled(true);
} else if (active == 1){
prev.setdisabled(false);
next.setdisabled(false);
} else if (active == 2){
next.setdisabled(true);
}};
border布局将容器分为五个区域:north、south、east、west和center。如下图:
其中north、south、east、west为选填项,center为必填项。另外我们不用设置center区域的高宽,浏览器可视工作区的高宽减去north、south、east和west高宽后剩余的空间便是center区域的高宽。
var border = ext.create('ext.window.window', {
width: 700,
height: 500,
title: 'border layout',
layout: 'border',
defaults:{
xtype: 'panel'
title: 'north region is resizable',
region: 'north',
height: 100,
split: true
title: 'south region is resizable',
region: 'south',
title: 'west region is collapsible',
region:'west',
width: 200,
collapsible: true,
layout: 'fit'
title: 'east region is collapsible',
region:'east',
title: 'center region',
region: 'center',
border.show();
(译者语:虽然原文中没有该节内容,但api文档有ext.layout.container.form类,为了知识的完整性,因此追加该内容)
1. 内部的子元素的宽度为容器body的宽度,并随容器的大小变化而变化;
2. 属于ext.form.field包下的内部子元素的padding属性将失效。
component布局负责组织组件内部的html元素。toolbars、headers和表单的fields中均有应用到。(译者
语:container布局用于组织组件之间的布局关系;component布局负责组织组件内部的html元素或子组件的关系,组件内部由多个部分
(html元素或子组件)组成,extjs4中对于组件中的子组件的组织形式是可定制的,相当灵活。另外虽然可通过组件的componentlayout
配置项配置component布局,但一般情况下我们不应该设置该配置项)
为提高灵活性,extjs4引进新的布局引擎dock布局,主要应用在panel类组件上。该布局已在组件内部设置并用于panel的headers和toolbars中。
下面我们通过将extjs3和extjs4作对比来学习dock布局。
extjs3中设置toolbar如下:
var html = '<div style="padding:10px;"><h1><center><span>body</ center></h1></div>';
var panel1 = new ext.panel({
collapsible:true,
width:400,
renderto: 'ext3-panel',
title: 'ext 3 panel - header',
html: html,
tbar: new ext.toolbar({
type: 'button',
text:'button - top toolbar'
}),
bbar: new ext.toolbar({
text:'button - bottom toolbar'
fbar: new ext.toolbar({
text:'button - footer toolbar'
})
上述代码片段中我们创建了一个标题为ext 3 panel - header的ext.panel实例,其中包含header(标题)和三个toolbar(分别位于ext.panel实例的上、下、页脚的位置),每个toolbar有一个按钮,结果如下:
下面我们来分析一下extjs3的ext.panel实例所生成的代码,最外层是一个用于封装整个组件的元素(el),然后是panel自身的header和body的封装体。在body的封装体内有是三个toolbar和panel body,具体如下图:
从上图我们可以看到extjs3中的panel的header和toolbar在panel内部的位置是固定的,不能作任何改变。
而extjs4的panel组件就不同了,我们先看一下从extjs3迁移到extjs4的实例吧:
renderto: 'ext4-panel',
title: 'ext 4 panel - header',
tbar: ext.create('ext.toolbar.toolbar',{
bbar: ext.create('ext.toolbar.toolbar',{
fbar: ext.create('ext.toolbar.toolbar',{
与extjs3的实例对比,我们可以发现除了用ext.create代替new来实例化对象外,其他均没有变化。那么究竟extjs4的header和toolbar有多灵活呢,下面我们看一个完整的例子吧:
border:true,
renderto: 'ext4-panel2',
headerposition: 'top',
dockeditems: [{
xtype: 'toolbar',
dock: 'top',
xtype: 'button',
text: 'button - top toolbar'
dock: 'bottom',
text: 'button - bottom toolbar'
xtype: 'component',
flex: 1 //will occupy 100% of the width of the panel
text: 'button - footer toolbar'
在extjs4中header已经独立成为panel下的一个子组件了,所属类为ext.panel.header。我们可以设置header的位置(top、bottom、left和right)
而toolbar也通过dockeditems配置项可以设置到top、bottom、left和right位置,并且同一个位置可以有0到n个toolbar。
下面是上述代码片段所生成的元素结构图:
(译者语:原文中对dock布局讲解的其余部分均为设置headerposition、dockeditems为不同的值时的效果描述,我想大家通过实践就会得到结论,所以在此就不在翻译了)
extjs4中的ext.panel.header中包含ext.panel.tool的0到n个实例a。而这些ext.panel.tool实例就是
通过tool布局来组织其关系。(译者语:in为在api文档中没有发现tool布局,所以本节内容将以介绍ext.panel.tool为主)
ext框架提供25种类型的ext.panel.tool,通过ext.panel.panel的tools配置项来设置ext.panel.tool
实例。要注意的一点是,ext框架提供的ext.panel.tool仅包含按钮图标而具体的点击事件处理函数需要我们自定义。具体实例如下:
width:500,
renderto: 'ext4-panel-tools',
title: 'tools - header',
tools: [{
type: 'close',
handler: function(){} //some logic inside handler
type: 'collapse',
type: 'down',
type: 'expand',
type: 'gear',
type: 'help',
type: 'left',
type: 'maximize',
type: 'minimize',
type: 'minus',
type: 'next',
type: 'pin',
type: 'plus',
type: 'prev',
type: 'print',
type: 'refresh',
itemid: "refresh",
hidden: true,
type: 'restore',
type: 'right',
type: 'save',
type: 'toggle',
type: 'unpin',
type: 'up',
type: "search",
handler: function(event, target, owner, tool){
// do search
owner.child('#refresh').show();
}
(译者语:原文中指出extjs4中已不支持formlayout,而是通过fieldlayout来替代formlayout。但api中仍然存在
ext.layout.container.form,而且formlayout属于container布局而fieldlayout属于
component布局,两者没有互斥关系,因此下面的内容以介绍field布局为主,并不讨论它语form布局的关系)
下面我们先来看一下在extjs3中是如何创建含两个字段的表单吧:
ext.form.field.prototype.msgtarget = 'side';
var simple = new ext.formpanel({
labelwidth: 75,
url:'save-form.php',
frame:true,
title: 'form - ext 3',
bodystyle:'padding:5px 5px 0',
width: 350,
renderto:'ext3-form',
defaults: {width: 230},
defaulttype: 'textfield',
fieldlabel: 'first name',
name: 'first',
allowblank:false
fieldlabel: 'last name',
name: 'last',
],
buttons: [{
text: 'save'
},{
text: 'cancel'
上面的代码片段中,我们创建了一个宽度为350px的表单,字段的标题宽度为75px,字段输入框宽度为230px的两个字段。字段不能为空,若为空则显示错误提示信息。结果如下:
在extjs3中我们需要为字段预留至少20px的空间来呈现错误提示信息图标,否则当字段验证失败时将无法看到错误提示信息图标。
下面我们看一下在extjs4中的实现吧:
var simple = ext.create('ext.form.panel', {
title: 'form - ext 4',
renderto:'ext4-form',
fielddefaults: {
msgtarget: 'side',
labelwidth: 75
defaulttype: 'textfield',
defaults: {
anchor: '100%'
text: 'save'
在extjs4上的实现与extjs3的差不多,但我们已经不用为显示错误提示信息图标而预留至少20px的空间了,因为当字段验证失败时字段将缩短字段框的长度来显示错误提示信息图标。效果如下:
除了上述的改进外,现在我们可以为表单配置不同的布局,如hbox:
var hboxform = ext.create('ext.form.panel', {
width: 600,
labelalign: 'top',
msgtarget: 'side'
border: false,
xtype: 'panel',
flex: 1,
layout: 'anchor'
layout: 'hbox',
xtype:'textfield',
anchor: '-10',
}, {
fieldlabel: 'phone number',
name: 'phone',
anchor: '100%',
chapter 3
[ 137 ]
fieldlabel: 'email',
name: 'email',
vtype:'email'
结果如下:
triggerfield布局是field布局的扩展。combox、datepicker等组件都是用triggerfield布局,因此均不用预留至少20px的空间来显示错误提示信息图标。
(译者语:原文余下的内容为证明上述内容的extjs3和extjs4对比实例,在此忽略了)
在本章我们学习了container和component布局。在学习ext的时候我们可能会更关注组件的使用而忽视了布局的学习,但布局是否使用得当往往是影响ui呈现速度的主要因素,因此充分理解布局的内容是十分重要的。