我想应该好多人都和我一样吧,对GridBagLayout莫名的抵触。呵呵,我想可能是由于我们一接触Swing的时候就被告知,“恩,GridBagLayout是布局器中最复杂的一种,如果你用到了GridBagLayout,那么这说明你就要使用管理布局的工具了。”什么意思,如果用不到GridBagLayout,不用学了,如果用到了GridBagLayout呢,那也不用学,因为这时你就要用布局工具了。
其实未必,GridBagLayout 有11个参数,但是如果搞懂了各个的意思,其实这种布局并不难,而且这种布局作出的效果也是最好的。
好了,学习了下GridBagLayout,写下来,希望可以对大家有点帮助。
文章翻译自:Manning - Java Swing 2nd Edition (2005)
4.1.8
GridBagLayout
alass java.awt.GridBagLayout, class java.awt.GridBagConstraints
这种Layout是constraints-based 的GridLayout。这种Layout将容器的空间分成相等的矩形的小块(就像是墙上的砖块一样),然后把每个组件放到一个或者多个小矩形中(跨越几个小矩形 由gridwidth, gridheight控制;当然小块在容器改变大小时,所改变的比例则由weightx, weighty来控制;组件在一个或者多个小块内部是如何放置则由ancher, fill来控制)。你要为每个组件都创建一个GridBagConstraints对象,填充这个对象的所有属性,然后让这个GridBagConstraints来告知GridBagLayout应该如何放置这个组件,以及告知GridBagLayout当容器大小改变时,如何让这个组件相应的进行变化。
如果在容器大小改变时对组件的大小位置等的变化没有特殊的要求,那么GridBagLayout在放置组件时是非常有效的。不过,天下没有免费的午餐,这同时也是最复杂的一种Layout,你需要自己写一个辅助的类来帮助你来管理某个GridBagConstraints的属性。可用性分析和GridBagLayou专家,James Tan,在4.3节中会带领我们全面的了解这个Layout。他还会给我们一个工具类来减轻我们处理GridBagConstaints属性时的繁重工作。
4.3 Using GridBagLayou
This section was written by James Tan, a systems analyst with United Overseas Bank
Singapore
(jtan@coruscant.per.sg)
在所有的Swing和AWT的Layout中,GridBagLayout是迄今(注:可能以后还会有更复杂的Layout,不过现在还是没有)最复杂的一种Layout。这一节中,我们将详细讲解GridBagLayout所依赖的GridBagConstraints,然后我们会列出一些代码来说明如何使用GridBagConstraints。我们后面将通过一个综合的input dialog,当然这个dialog使用到了所有的GridBagConstraints的属性,来深入讨论GridBagConstraints中的各个变量的使用。最后我们讲解一个我们自己设计的用来方便我们使用GridBagLayout的辅助类来结束我们GridBagLayout的学习。
4.3.1
Default behavior of GridBagLayout
简单的设置一个容器的Layout为GridBagLayout,然后把组件加到这个容器中,效果是这样的:
1、这些组件排成一行
2、每个组件都是自己的preferred size
3、这些组件仅仅的靠在一起(tightly packed)
4、所有的这些组件位于容器的中央
实际上默认情况下的GridBagConstraints是这样的
gridx = RELATIVE;// 横向上,此组件位于上个组件的右边
gridy = RELATIVE; // 纵向上,此组件上位于上个组件的右边
gridwidth = 1;// 横向上,此组件跨越一个cell
gridheight = 1; // 纵向上,此组件跨越一个cell
weightx = 0;// 容器大小变化时,横向上,此组件cell不变
weighty = 0; // 容器大小变化时,纵向上,此组件cell不变
anchor = CENTER;// 组件位于cell的中央
fill = NONE;// cell大小变化时,组件大小不会变
insets = new Insets(0, 0, 0, 0);// 此组件的四周没有任何空间(如果和其他组件相邻时)
ipadx = 0;// 横向上,此组件没有任何的附加大小
ipady = 0; // 纵向上,此组件没有任何的附加大小
不像FlowLayout,GridBagLayout的容器的边界可以切割其内的组件,也就是说(当容器大小变化,导致一行放不下某个组件时)容器不会把其内的组件移动到下一行。下面的代码展示了这个特性。
见代码:
/**
* Default Behavior
*/
4.3.2
Introducing GridBagConstraints
如果一个容器被分配了GridBagLayout的Layout,那么当我们直接向这个容器添加组件时,Layout Manager会根据默认的GridBagConstraints对象来放置我们加上的组件。就像上面我的例子那样。通过自己创建一个GridBagConstraints对象,然后给这个对象设置属性值,然后把这个GridBagConstraits对象在add组件时也当作add()方法的参数,这样我们就可以非常有弹性的来控制我们组件的位置的放置。
下面列出的是我们可以设置GridBagConstraints的所有的属性,以及这些属性的默认值,这些属性的行为我们会在下面的例子中来一一阐述。
public int gridx = GridBagConstraints.RELATIVE;
public int gridy = GridBagConstraints.RELATIVE;
public int gridwidth = 1;
public int gridheight = 1;
public double weightx = 0.0;
public double weighty = 0.0;
public int anchor = GridBagConstraints.CENTER;
public int fill = GridBagConstraints.NONE;
public Insets insets = new Insets( 0, 0, 0, 0 );
public int ipadx = 0;
public int ipady = 0;
4.3.3
Using the gridx, gridy, ipadx, and ipady constraints
gridx, griy属性用来确切的指定,我们希望我们的组件在cell中所处的具体的位置。组件的初始放置位置从容器的左上角开始,gridx, gridy的初始值是0。如果给它们指定负值,那么就相当于指定它们为RELATIVE,这时的意思是:这个将要加上的组件位于上一个组件的后面(即位于上一个组件的右面或者下面)。
insets属性会围绕着这个组件,在这个组件的外部加上个不可见的夹垫(padding),如果是负值,则意思是使得这个组件变得比起所在cell更大。
ipadx, ipady属性会给这个组件加上个内部夹垫(padding),这时此组件的preferred size的值会计算入这个内部夹垫。非常明确的是,ipadx属性会把此组件的preferred width的值加上ipadx*2,同样ipady属性会把此组件的preferred height的值加上ipady*2(*2是因为这个夹垫(padding)会在此组件的两边都加上)。
在下面的例子中,我们把按钮”Wonderful” ”World”放到第一行,其他的两个按钮放到第二行。我们也给每个按钮都分配insets,这样这些按钮看起来就不会那么拥挤了。按钮的大小也是不一样(由ipdax, ipady所改变的)
见代码:
/**
* gridx, ipadx, insets
*/
我们首先创建了一个GridBagConstraints对象,然后设置这个对象的各个属性,然后利用这个对象来把第一个按钮加给容器。然后我们还用这个对象,改变这个对象的相应的值,然后再利用这个对象把其他的组件加到容器内。这样节省了内存,而且我们也避免了在设置其他不需要修改的值。
4.3.4
Using the weightx and weighty constraints(如何给cell分配多余的空间,默认0,0)
在上面的例子中,当容器的大小改变时,容器内的组件遵循我们所设置的约束条件而变化(the components respect the constrains we have assigned),但是整个容器内的组件整体却始终都位于容器的中间。那么,问题来了:为什么我们的组件变化,从而按一定的比例来占据他们周围的空间呢?答案就在于weightx, weighty 属性,这两个属性当GridBagConstraints在初始化时都是0。
这两个属性用来指定(当容器的大小变化时)如何把容器内的多余的空间分配到每个组件的cell中。属性weightx用来指定使用这个GridBagConstraints的组件如何占据水平的多余的空间。类似地,属性weighty用来指定使用这个GridBagConstraints的组件如何占据垂直的多余的空间。这两个约束被分配的值是0.0----1.0。
举例来说,我们有两个按钮A和B分别放在row 0,column0和column1。如果我为A指定了weightx=1.0,为B指定weightx=0,那么当我们改变容器的大小时,所有的空间将分配给按钮A的cell(注意:空间分给了cell,而不是cell内的组件)。(当然,这里,至于按钮A在这个cell内位于什么方位,以及空间比以前大了后,A又是如何变化的,那都取决于ancher, fill属性。)B按钮则被尽可能远的推倒了容器的右边。
回到我们的”Wonderful World Of Swing!!!”例子,我们修改我们的程序,使得当容器增大时,所有的按钮的cell都得到相等的多出来的空间(由于容器增大而多出来的空间)。指定weightx, weighty都是1.0。然后当加任何一个组件时都使用同样的约束,这样GridBayLayout就会把所有的可用的空间平均分配到每个cell上。
见程序
/**
* 3. weightx
*/
4.3.5
Using the gridwidth and gridheight constraints(使组件跨越多个cell,默认值:1,只占据一个cell)
GridBagLayout也允许我们让一个组件跨越多个cell,这时我们就用到了gridwidth, gridheight。为了展示这个示例,我们修改我们的程序,让我们的按钮”Wonderful”占用两个行,让我们的”World”按钮占用两个列。注意:如果占用了多个cell,那么就会基于当前的容器的大小而创建多个行或列,这样我们后面再设置gridx/gridy属性时则还要计算上此处的gridwidth或gridheight。
见程序:
/**
* gridwidth, occupy more column
*/
4.3.6
Using anchor constraints(默认值:GridBagConstraints.CENTER)
我们可以控制一个组件在它所属的cell(s)内是如何摆放(aligned)的,这时就用到了GridBagConstraints的anchor属性,默认的是GridBagConstraints.CENTER,所以默认情况下组件就被放置到了这个组件所占据的cell(s)的中间,关于anchor有下列的9种设置:
GridBagConstraints.NORTH
GridBagConstraints.SOUTH
GridBagConstraints.EAST
GridBagConstraints.WEST
GridBagConstraints.CENTER
GridBagConstraints.NORTHEAST
GridBagConstraints.NORTHWEST
GridBagConstraints.SOUTHEAST
GridBagConstraints.SOUTHWEST
下面的代码中,我们修改anchor属性,从而将”Wonderful”按钮放到它自己所在的cell的NORTH,”World”按钮放到它自己所在的cell的SOUTHWEST。”Of” 和”Swing!!!”按钮放到他们自己的cell的CENTER。
见代码:
/**
* ancher in a cell
*/
4.3.7
Using fill constraints(默认值:GridBagConstraints.NONE)
要跨越多个组件的原因就是我们希望包含在某个cell(s)内的组件占据多余的这些cell的空间。要作到这个,我们可以像上面介绍的那样使用gridheight/gridwidth约束,以及我们将要介绍的fill约束。fill约束可以使用下面的四个值之一:
GridBagConstraints.NONE
GridBagConstraints.HORIZONTAL
GridBagConstraints.VERTICAL
GridBagConstraints.BOTH
注意:使用fill属性时,必须设置weightx, weighty皆>0否则,fill属性没有作用的。
在下面的代码中,我们修改代码,使得”Wonderful”按钮占据所有的(自己cell内的)可用的空间,包括横向和纵向;”World”按钮则占据(自己cell内的)所有的水平空间,而纵向上则还是用按钮自己的preferred vertical size;”Of”按钮不使用fill约束,也就是说,使用默认值NONE,这样在横向和纵向上此按钮的大小都是preferred size;”Swing!!!”按钮则使用(自己cell内的)所有的纵向空间,而横向上还是使用按钮自己的preferred horizontal size。
见代码:
/**
* fill in a cell
*/
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
/** */ /**
* This Demo is tidid up from a demo from Manning - Java Swing 2nd Edition (2005)
* wish it is useful for you to learn GridBagLayout
* @author Marvin
*
*/
public class GridBagDemo extends JFrame ... {
public GridBagDemo() ...{
super("GridBagDemo");
setSize(800, 600);
JDesktopPane desktop = new JDesktopPane();
getContentPane().add(desktop);
/** *//**
* Default Behavior
*/
JInternalFrame dft = new JInternalFrame("Default Behavior", true, true);
dft.setBounds(5, 5, 300, 140);
Container contentPane = dft.getContentPane();
contentPane.setLayout(new GridBagLayout());
contentPane.add(new JButton("Wonderful"));
contentPane.add(new JButton("World"));
contentPane.add(new JButton("Of"));
contentPane.add(new JButton("Swing!!!"));
desktop.add(dft, 0);
dft.show();
/** *//**
* gridx, ipadx, insets
*/
JInternalFrame gridx_ipadx_insets = new JInternalFrame(
"gridx_ipadx_insets", true, true);
gridx_ipadx_insets.setBounds(5, 150, 300, 140);
contentPane = gridx_ipadx_insets.getContentPane();
contentPane.setLayout(new GridBagLayout());
GridBagConstraints constrains = new GridBagConstraints();
constrains.insets = new Insets(2, 2, 2, 2);
constrains.gridx = 0;// column 0
constrains.gridy = 0;// row 0
constrains.ipadx = 5;// Increase component width by 10 pixels
constrains.ipady = 5;// Increase component height by 10 pixels
contentPane.add(new JButton("Wonderful"), constrains);
constrains.gridx = 1;
constrains.ipadx = 0;// Reset the padding to 0
constrains.ipady = 0;// Increase component height by 10 pixels
contentPane.add(new JButton("World"), constrains);
constrains.gridx = 0;// column 0
constrains.gridy = 1;// row 1
contentPane.add(new JButton("Of"), constrains);
constrains.gridx = 1;// column 1
contentPane.add(new JButton("Swing!!!"), constrains);
desktop.add(gridx_ipadx_insets, 1);
gridx_ipadx_insets.show();
/** *//**
* weightx
*/
JInternalFrame weightx_weighty = new JInternalFrame("weightx", true,
true);
weightx_weighty.setBounds(5, 295, 300, 140);
contentPane = weightx_weighty.getContentPane();
contentPane.setLayout(new GridBagLayout());
constrains = new GridBagConstraints();
constrains.insets = new Insets(2, 2, 2, 2);
constrains.weightx = 1.0;
constrains.weighty = 1.0;
constrains.gridx = 0;// column 0
constrains.gridy = 0;// row 0
contentPane.add(new JButton("Wonderful"), constrains);
constrains.gridx = 1;// column 1
contentPane.add(new JButton("World"), constrains);
constrains.gridx = 0;// column 0
constrains.gridy = 1;// row 1
contentPane.add(new JButton("Of"), constrains);
constrains.gridx = 1;// column 1
contentPane.add(new JButton("Swing!!!"), constrains);
desktop.add(weightx_weighty, 2);
weightx_weighty.show();
/** *//**
* gridwidth, occupy more column
*/
JInternalFrame gridwidth_occupyMoreColumn = new JInternalFrame(
"gridwidth_occupyMoreColumn", true, true);
gridwidth_occupyMoreColumn.setBounds(350, 5, 300, 140);
contentPane = gridwidth_occupyMoreColumn.getContentPane();
contentPane.setLayout(new GridBagLayout());
constrains = new GridBagConstraints();
constrains.insets = new Insets(2, 2, 2, 2);
constrains.gridx = 0;
constrains.gridy = 0;
constrains.weightx = 1.0;
constrains.weighty = 1.0;
constrains.gridheight = 2;// Span accross 2 row
contentPane.add(new JButton("Wonderful"), constrains);
constrains.gridx = 1;
constrains.gridheight = 1;// Remember to set back to 1 row
constrains.gridwidth = 2;// Span accross 2 column
contentPane.add(new JButton("World"), constrains);
constrains.gridy = 1;
constrains.gridwidth = 1;// Remember to set back to 1 column
contentPane.add(new JButton("Of"), constrains);
constrains.gridx = 2;
contentPane.add(new JButton("Swing!!!"), constrains);
desktop.add(gridwidth_occupyMoreColumn, 3);
gridwidth_occupyMoreColumn.show();
/** *//**
* ancher in a cell
*/
JInternalFrame ancher_in_cell_position = new JInternalFrame(
"ancher_in_cell_position", true, true);
ancher_in_cell_position.setBounds(350, 150, 300, 140);
contentPane = ancher_in_cell_position.getContentPane();
contentPane.setLayout(new GridBagLayout());
constrains = new GridBagConstraints();
constrains.insets = new Insets(2, 2, 2, 2);
constrains.weightx = 1.0;
constrains.weighty = 1.0;
constrains.gridx = 0;
constrains.gridy = 0;
constrains.gridheight = 2;
constrains.anchor = GridBagConstraints.NORTH;
contentPane.add(new JButton("Wonderful"), constrains);
constrains.gridx = 1;
constrains.gridheight = 1;
constrains.gridwidth = 2;
constrains.anchor = GridBagConstraints.SOUTHWEST;
contentPane.add(new JButton("World"), constrains);
constrains.gridy = 1;
constrains.gridwidth = 1;
constrains.anchor = GridBagConstraints.CENTER;
contentPane.add(new JButton("Of"), constrains);
constrains.gridx = 2;
contentPane.add(new JButton("Swing!!!"), constrains);
desktop.add(ancher_in_cell_position, 4);
ancher_in_cell_position.show();
/** *//**
* fill in a cell
*/
JInternalFrame fill_the_cell = new JInternalFrame("fill_the_cell",
true, true);
fill_the_cell.setBounds(350, 295, 300, 140);
contentPane = fill_the_cell.getContentPane();
contentPane.setLayout(new GridBagLayout());
constrains = new GridBagConstraints();
constrains.insets = new Insets(2, 2, 2, 2);
constrains.weightx = 1.0;
constrains.weighty = 1.0;
constrains.gridx = 0;
constrains.gridy = 0;
constrains.gridheight = 2;
constrains.fill = GridBagConstraints.BOTH;
contentPane.add(new JButton("Wonderful"), constrains);
constrains.gridx = 1;
constrains.gridheight = 1;
constrains.gridwidth = 2;
constrains.fill = GridBagConstraints.HORIZONTAL;
contentPane.add(new JButton("World"), constrains);
constrains.gridy = 1;
constrains.gridwidth = 1;
constrains.fill = GridBagConstraints.NONE;
contentPane.add(new JButton("Of"), constrains);
constrains.gridx = 2;
constrains.fill = GridBagConstraints.VERTICAL;
contentPane.add(new JButton("Swing!!!"), constrains);
desktop.add(fill_the_cell, 5);
fill_the_cell.show();
WindowListener wndCloser = new WindowAdapter() ...{
public void windowClosing(WindowEvent e) ...{
System.exit(0);
}
};
addWindowListener(wndCloser);
setVisible(true);
}
public static void main(String argv[]) ...{
new GridBagDemo();
}
}