布局管理器 GridBagLayout 的使用

Java布局管理器有FlowLayout, BorderLayout, GridLayout, CardLayout, GridBagLayout等,相比较而言,GridBagLayout的使用方式要复杂一些。但它的表现力也最丰富。之前我对GridBagLayout的使用也是一知半解,具体的用法不是十分的明了。这两天认真的看了这一块,就写了下来。

使用 GridBagLayout的主要工作就是设置组件的约束,通过GridBagConstraints的实例设置。

GridBagConstraints 主要有11个参数:gridx, gridy, gridwidth ,gridheight ,anchor ,fill ,weightx ,weighty ,insets ,ipadx ,ipady;
每个参数的意义可以参考java api上的说明。本文的最后附有anchor属性和fill属性的示例。实际使用中根据自己的需要设置特定的属性。

在Java api中给出了一个GridBagLayout的例子和实现,下面的是自己的实现方式。

[CODE]

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GridBagLayoutDemo extends Applet {
GridBagLayout gbaglayout;
GridBagConstraints constraints;

public void init() {
   gbaglayout = new GridBagLayout();  
   constraints = new GridBagConstraints();
   setLayout(gbaglayout);
  
   //Button 1
   Button btn1 = new Button("Button01");
   constraints.gridx = 0;
   constraints.gridy = 0;
   constraints.gridwidth = 1;
   constraints.gridheight = 1;
   constraints.anchor = GridBagConstraints.CENTER;
   constraints.fill = GridBagConstraints.NONE;
   constraints.weightx = 0.0;
   constraints.weighty = 0.0;
   constraints.insets = new Insets(0, 0, 0, 0);
   constraints.ipadx = 0;
   constraints.ipady = 0;
   makeButton(gbaglayout, constraints, btn1);
  
   //Button 2
   Button btn2 = new Button("Button02");
   constraints.gridx = 1;  
   makeButton(gbaglayout, constraints, btn2);
  
   //Button 3
   Button btn3 = new Button("Button03");
   constraints.gridx = 2;  
   makeButton(gbaglayout, constraints, btn3);
  
   //Button 4
   Button btn4 = new Button("Button04");
   constraints.gridx = 3;  
   makeButton(gbaglayout, constraints, btn4);
  
//Button 5
   Button btn5 = new Button("Button05");
   constraints.gridx = 0;
   constraints.gridy = 1;
   constraints.gridwidth = 4;
   makeButton(gbaglayout, constraints, btn5);
  
   //Button 6
   Button btn6 = new Button("Button06");
   constraints.gridx = 0;
   constraints.gridy = 2;
   constraints.gridwidth = 3;
   makeButton(gbaglayout, constraints, btn6);
  
   //Button 7
   Button btn7 = new Button("Button07");
   constraints.gridx = 3;
   constraints.gridy = 2;
   constraints.gridwidth = 1;
   makeButton(gbaglayout, constraints, btn7);
  
   //Button 8
   Button btn8 = new Button("Button08");
   constraints.gridx = 0;
   constraints.gridy = 3;
   constraints.gridwidth = 1;
   constraints.gridheight = 2;  
   makeButton(gbaglayout, constraints, btn8);
  
   //Button 9
   Button btn9 = new Button("Button09");
   constraints.gridx = 1;
   constraints.gridy = 3;
   constraints.gridwidth = 3;
   constraints.gridheight = 1;  
   makeButton(gbaglayout, constraints, btn9);
  
   //Button 10
   Button btn10 = new Button("Button10");
   constraints.gridx = 1;
   constraints.gridy = 4;
   constraints.gridwidth = 3;
   constraints.gridheight = 1;  
   makeButton(gbaglayout, constraints, btn10);
}

private void makeButton(GridBagLayout gbaglayout, GridBagConstraints constraints, Component component) {
   gbaglayout.setConstraints(component, constraints);
   add(component);
}

public static void main(String[] args) {
   Frame frame = new Frame("GridBagLayoutDemo");
   frame.addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
     System.exit(0);
    }
   });
  
   GridBagLayoutDemo demo = new GridBagLayoutDemo();
   demo.init();
  
   frame.add(BorderLayout.CENTER, demo);
   frame.setSize(270, 150);
   //frame.setSize(361, 254);
   frame.setVisible(true);  
}
}

[CODE]

显示窗口可以如图所示划分为 5 行和 4 列,每个按钮所在的单元可以很容易的确定,所以 gridx 和 gridy 的值都很容易确定,组件放置在的位置也就确定了。
然而,组件都有一个默认的大小,它和它所在的单元格大小一般情况下都是不同的,并且如果窗口发生变化时,组件做出什么样的改变就要用到fill ,weightx 和 weighty了。

结合具体的例子来看:

设置 Button 1

//Button 1
Button btn1 = new Button("Button01");

//所在单元格为(0, 0)
constraints.gridx = 0;
constraints.gridy = 0;

//占用一个长度长和一个长度宽
constraints.gridwidth = 1;
constraints.gridheight = 1;

//这里使用默认的 anchor
constraints.anchor = GridBagConstraints.CENTER;

//设置NONE填充
constraints.fill = GridBagConstraints.NONE;

//设置填充权重
constraints.weightx = 0.0;
constraints.weighty = 0.0;

//Insets, ipadx, ipady使用默认值
constraints.insets = new Insets(0, 0, 0, 0);
constraints.ipadx = 0;
constraints.ipady = 0;

//添加 Button01 按钮
makeButton(gbaglayout, constraints, btn1);

 


设置 Button02, Button03, Button04  

//Button 2
Button btn2 = new Button("Button02");
constraints.gridx = 1;  
makeButton(gbaglayout, constraints, btn2);

//Button 3
Button btn3 = new Button("Button03");
constraints.gridx = 2;  
makeButton(gbaglayout, constraints, btn3);

//Button 4
Button btn4 = new Button("Button04");
constraints.gridx = 3;  
makeButton(gbaglayout, constraints, btn4);

Button02, Button03, Button04和Button01的区别是,只需要设置他们的gridx值;其他的不变。

 


设置 Button05

//Button 5
Button btn5 = new Button("Button05");
//所在单元格为(0, 1)
constraints.gridx = 0;
constraints.gridy = 1;
//占用了所有的4个单元格宽度 ,一个单元格高,省略没写
constraints.gridwidth = 4;
makeButton(gbaglayout, constraints, btn5);

 


设置 Button06

//Button 6
Button btn6 = new Button("Button06");

//所在单元格为(0, 2)
constraints.gridx = 0;
constraints.gridy = 2;

//占用了所有的3个单元格宽度 ,一个单元格高,省略没写
constraints.gridwidth = 3;
makeButton(gbaglayout, constraints, btn6);

 


设置 Button07

//Button 7
Button btn7 = new Button("Button07");

//所在单元格为(3, 2)
constraints.gridx = 3;
constraints.gridy = 2;

//占用了所有的1个单元格宽度 ,一个单元格高,省略没写
constraints.gridwidth = 1;
makeButton(gbaglayout, constraints, btn7);

 


设置 Button08

//Button 8
Button btn8 = new Button("Button08");

//所在单元格为(0, 3)
constraints.gridx = 0;
constraints.gridy = 3;

//占用了所有的1个单元格宽度 ,2个单元格高
constraints.gridwidth = 1;
constraints.gridheight = 2;   
makeButton(gbaglayout, constraints, btn8);

 


设置 Button09

//Button 9
Button btn9 = new Button("Button09");

//所在单元格为(1, 3)
constraints.gridx = 1;
constraints.gridy = 3;

//占用了所有的3个单元格宽度 ,1个单元格高
constraints.gridwidth = 3;
constraints.gridheight = 1;   
makeButton(gbaglayout, constraints, btn9);

 


设置 Button10

//Button 10
Button btn10 = new Button("Button10");

//所在单元格为(1, 4)
constraints.gridx = 1;
constraints.gridy = 4;

//占用了所有的3个单元格宽度 ,1个单元格高,由于和Button09一样,也可以不写
constraints.gridwidth = 3;
constraints.gridheight = 1;   
makeButton(gbaglayout, constraints, btn10);

设置好10个按钮之后,看看结果如何:

和目标差别还很大,但它确实是按照期望的方式,在特定的单元格内显示了。

为什么有差别呢?原因是单元格比按钮默认的大小要大,没有设置fill方式。
由于需要Button01, Button02, Button03 和 Button04的长度充满单元格,可修改 fill 属性为:constraints.fill = GridBagConstraints.HORIZONTAL;
同时改变constraints.weightx = 1.0;使得窗口变化时按钮能随之伸长(如果不改变weightx值,改变窗口大小时,看看会有什么效果)。


显示结果:

可以看到 Button08有问题,更改相应部分:

//Button 8
Button btn8 = new Button("Button08");
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
constraints.gridheight = 2;
//设置Button08的 fill 为 BOTH
constraints.fill = GridBagConstraints.BOTH;  
constraints.weightx = 1.0;
constraints.weighty = 1.0;
makeButton(gbaglayout, constraints, btn8);

//Button09
Button btn9 = new Button("Button09");
constraints.gridx = 1;
constraints.gridy = 3;
constraints.gridwidth = 3;
constraints.gridheight = 1;
//不需要垂直变化
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1.0;
constraints.weighty = 0.0;
makeButton(gbaglayout, constraints, btn9);

//Button 10
Button btn10 = new Button("Button10");
constraints.gridx = 1;
constraints.gridy = 4;
constraints.gridwidth = 3;
constraints.gridheight = 1;  
//需要垂直变化
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1.0;
constraints.weighty = 1.0;
makeButton(gbaglayout, constraints, btn10);

好了,所有的属性设置好了。

 

 

完整代码:

[CODE]

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GridBagLayoutDemo extends Applet {
GridBagLayout gbaglayout;
GridBagConstraints constraints;

public void init() {
   gbaglayout = new GridBagLayout();  
   constraints = new GridBagConstraints();
   setLayout(gbaglayout);
  
   //Button 1
   Button btn1 = new Button("Button01");
   constraints.gridx = 0;
   constraints.gridy = 0;
   constraints.gridwidth = 1;
   constraints.gridheight = 1;
   constraints.anchor = GridBagConstraints.CENTER;  
   constraints.fill = GridBagConstraints.HORIZONTAL;
   constraints.weightx = 1.0;
   constraints.weighty = 0.0;
   constraints.insets = new Insets(0, 0, 0, 0);
   constraints.ipadx = 0;
   constraints.ipady = 0;
   makeButton(gbaglayout, constraints, btn1);
  
  //Button 2
   Button btn2 = new Button("Button02");
   constraints.gridx = 1;  
   makeButton(gbaglayout, constraints, btn2);
  
   //Button 3
   Button btn3 = new Button("Button03");
   constraints.gridx = 2;  
   makeButton(gbaglayout, constraints, btn3);
  
   //Button 4
   Button btn4 = new Button("Button04");
   constraints.gridx = 3;  
   makeButton(gbaglayout, constraints, btn4);
  
   //Button 5
   Button btn5 = new Button("Button05");
   constraints.gridx = 0;
   constraints.gridy = 1;
   constraints.gridwidth = 4;
   makeButton(gbaglayout, constraints, btn5);
  
   //Button 6
   Button btn6 = new Button("Button06");
   constraints.gridx = 0;
   constraints.gridy = 2;
   constraints.gridwidth = 3;
   makeButton(gbaglayout, constraints, btn6);
  
   //Button 7
   Button btn7 = new Button("Button07");
   constraints.gridx = 3;
   constraints.gridy = 2;
   constraints.gridwidth = 1;
   makeButton(gbaglayout, constraints, btn7);
  
   //Button 8
   Button btn8 = new Button("Button08");
   constraints.gridx = 0;
   constraints.gridy = 3;
   constraints.gridwidth = 1;
   constraints.gridheight = 2;
   constraints.fill = GridBagConstraints.BOTH;
   constraints.weightx = 1.0;
   constraints.weighty = 1.0;    
   makeButton(gbaglayout, constraints, btn8);
  
//Button 9
   Button btn9 = new Button("Button09");
   constraints.gridx = 1;
   constraints.gridy = 3;
   constraints.gridwidth = 3;
   constraints.gridheight = 1;  
   constraints.fill = GridBagConstraints.HORIZONTAL;
   constraints.weightx = 1.0;
   constraints.weighty = 0.0;
   makeButton(gbaglayout, constraints, btn9);
  
  //Button 10
   Button btn10 = new Button("Button10");
   constraints.gridx = 1;
   constraints.gridy = 4;
   constraints.gridwidth = 3;
   constraints.gridheight = 1;  
   constraints.fill = GridBagConstraints.BOTH;
   constraints.weightx = 1.0;
   constraints.weighty = 1.0;
   makeButton(gbaglayout, constraints, btn10);
}

private void makeButton(GridBagLayout gbaglayout, GridBagConstraints constraints, Component component) {
   gbaglayout.setConstraints(component, constraints);
   add(component);
}

public static void main(String[] args) {
   Frame frame = new Frame("GridBagLayoutDemo");
   frame.addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
     System.exit(0);
    }
   });
  
   GridBagLayoutDemo demo = new GridBagLayoutDemo();
   demo.init();
  
   frame.add(BorderLayout.CENTER, demo);
   frame.setSize(270, 150);
   //frame.setSize(361, 254);
   frame.setVisible(true);  
}
}

[CODE]

 

以下是 fill anchor 的示例:

fill的4种方式:

 

anchor的9种方式:

 
 
 
 
//===================================================
 

 利用Java来创建图形用户界面,最苦恼的事情莫过于选择用哪种布局管理器了。

     许多应用程序使用嵌套的Border和GridLayout来进行布局, 但是得到的结果要么变得太死板、太嵌套了以至于都无法工作了,要么就是窗口在变化的时候布局太不合理了,这样往往解决方法只有启用多个窗口,还有要么就是固定窗口的大小不允许改变。

    无论是那种选择都不是很好,因为它使得应用程序从界面看上去不太美观,同样也会让应用程序的界面在使用上受到很多的限制。

    但还好有一个工具能够解决所有的这些问题—那就是GridBagLayout。可惜很多人因为看到这个布局管理器感觉它太复杂了认为很难而不敢去学习,他们一直都不愿意尝试下去使用这个布局管理器。

     试着用用你会发现其实GridBagLayout真的能解决几乎所有界面布局的问题,窗口大小的随意改变也不会影响到整体布局,更重要的是它可以实现任何你想要的布局设计,只要你做到更有计划和更有耐心一点就行了。

三思而后行

    GridBagLayout 不是用于简单的示例程序界面。使用GridBagLayout搭建界面就像是在起居室中搭脚手架清除画钩一样。

对于简单的程序使用Boborderlayout和Gridlayout就绰绰有余了, 但如果要把程序提到实际应用上你就得考虑使用GridBagLayout。当然, 做复杂的应用程序时,一开始就使用GridBagLayout就会更有效率。

一旦你决定使用GridBagLayout,接下来一步便是要找一些纸和铅笔,只有你准确知道你的界面看上去需要成什么样子,你就可以敲键盘。这就是说,你应该在编码之前进行妥善规划。

    下面将介绍一个很小的应用程序来帮助我们学习GridBagLayout,这个例子是从一个Flickr RSS fead中显示一系列照片, 最后的界面就像下面这样:

JAVA:深入学习GridBagLayout

下面的是这个界面的一个原始草图:

JAVA:深入学习GridBagLayout

正如你所看到的,最终的结果看上去和计划的想法完全一样。

    你应该能看到在草图里有一些线,这些线是用来把总界面分成若干行和列的,这样你就很清楚每一个组件放置的格子位置。这就是GridBagLayout里"格"的那一部分,而图上的数字就是格的号码。

在某种意义上说, 我们可以把GridBagLayout想象成为早些年的HTML3和4,它们都是基于表的布局,Grid的概念就类似rowspan和colspan的意思,只不过换了个名字罢了。

随着我们的界面和表格的设置完成,是时候该进行界面布局并开始写代码了。

工作过程

这一节我假定你已经了解了基本的窗口和组件创建知识。

通过这篇文章我们最终能在一个frame中布局组件,我们将在以后的文章对界面进行改进使它更适用。因此,为了了解这整个工作的过程,我们列出了所有的目标代码。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GridBagWindow extends JFrame {
  private JButton searchBtn;
  private JComboBox modeCombo;
  private JLabel tagLbl;
  private JLabel tagModeLbl;
  private JLabel previewLbl;
  private JTable resTable;
  private JTextField tagTxt;
 public GridBagWindow() {
   Container contentPane = getContentPane();
   GridBagLayout gridbag = new GridBagLayout();
   contentPane.setLayout(gridbag);
   GridBagConstraints c = new GridBagConstraints();
   //setting a default constraint value
   c.fill =GridBagConstraints.HORIZONTAL;
   tagLbl = new JLabel("Tags");
   c.gridx = 0; //x grid position
   c.gridy = 0; //y grid position
   gridbag.setConstraints(tagLbl, c); //associate the label with a constraint object 
   contentPane.add(tagLbl); //add it to content pane
   
   tagModeLbl = new JLabel("Tag Mode");
   c.gridx = 0;
   c.gridy = 1;
   gridbag.setConstraints(tagModeLbl, c);
   contentPane.add(tagModeLbl);
   tagTxt = new JTextField("plinth");
   c.gridx = 1;
   c.gridy = 0;
   c.gridwidth = 2;
   gridbag.setConstraints(tagTxt, c);
   contentPane.add(tagTxt);
   String[] options = {"all", "any"};
   modeCombo = new JComboBox(options);
   c.gridx = 1;
   c.gridy = 1;
   c.gridwidth = 1;
   gridbag.setConstraints(modeCombo, c);
   contentPane.add(modeCombo);
   searchBtn = new JButton("Search");
   c.gridx = 1;
   c.gridy = 2;
   gridbag.setConstraints(searchBtn, c);
   contentPane.add(searchBtn);
   resTable = new JTable(5,3);
   c.gridx = 0;
   c.gridy = 3;
   c.gridwidth = 3;
   gridbag.setConstraints(resTable, c);
   contentPane.add(resTable);
   previewLbl = new JLabel("Preview goes here");
   c.gridx = 0;
   c.gridy = 4;
   gridbag.setConstraints(previewLbl, c);
   contentPane.add(previewLbl);
  addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
     System.exit(0);
    }
  });
}
 public static void main(String args[]) {
  GridBagWindow window = new GridBagWindow();
  window.setTitle("GridBagWindow");
  window.pack();
  window.setVisible(true);
 }
}

 

构造方法前的代码都不是很特殊,都是一些相当标准的import和变量定义。但是进入构造方法后,事情就变得有趣了。

Container contentPane = getContentPane();

GridBagLayout gridbag = new GridBagLayout();

contentPane.setLayout(gridbag);

    我们以GridBagWindow的内容面板作为开始来创建一个GridBagLayout对象,准确地说,这个方法与过去我们所创建GridLayout对象和BorderLayout对象的方法是一样的。那么,现在我们就开始来设置GridBagLayout对象使它作为内容面板的布局。

GridBagConstraints c = new GridBagConstraints();

    然后我要提到这整个进程中的一个独特的对象,那就是GridBagConstraints。这个对象在GridBagLayout中控制所有被安置在其中组件的约束。为了把一个组件增加到你的GridBagLayout中去,你首先必须将它与一个GridBagConstraints对象建立连接。

GridBagConstraints可以从11个方面来进行控制和操纵,也可以给你提供一些帮助。这些内容是:

  • Gridx——组件的横向坐标
  • Girdy——组件的纵向坐标
  • Gridwidth——组件的横向宽度,也就是指组件占用的列数,这与HTML的colspan类似
  • Gridheight——组件的纵向长度,也就是指组件占用的行数,这与HTML的rowspan类似
  • Weightx——指行的权重,告诉布局管理器如何分配额外的水平空间
  • Weighty——指列的权重,告诉布局管理器如何分配额外的垂直空间
  • Anchor——告诉布局管理器组件在表格空间中的位置
  • Fill——如果显示区域比组件的区域大的时候,可以用来控制组件的行为。控制组件是垂直填充,还是水平填充,或者两个方向一起填充
  • Insets——指组件与表格空间四周边缘的空白区域的大小
  • Ipadx—— 组件间的横向间距,组件的宽度就是这个组件的最小宽度加上ipadx值
  • ipady—— 组件间的纵向间距,组件的高度就是这个组件的最小高度加上ipady值

    可能对于一个组件的每一个实例你都需要为它建立一个单独的GridBagConstraints;然而,这种方法我们并不推荐使用。最好的方法是,当你调用它的时候把对象设置为默认值,然后针对于每一个组件改变其相应的域。

    这个方法具有通用性,因为在一些域中,比如insets、padx、pady和fill这些域,对于每一个组件来说一般都是相同的,因此这样对一个域进行设置就会更轻松了,也能更轻松的在另外的组件中改变某些域的值。

    如果在改变了某些域值之后,你想回到原始的域值的话,你应该在增加下一个组件之前进行改变。这种方法使你更容易明白你正在修改的内容,也能使你更容易明白在一连串对象中的这11个参数的作用。

    也许你现在对这些内容还是一知半解,不过事实上一旦你理解了GridBagConstraints,值得安慰的是你以后做再困难的工作都会游刃有余了。

所以,如果我们已经明白了GridBagConstraints的详细用法了,那么现在就让我们来看看在实际应用中应该如何来实现它:

tagLbl = new JLabel("Tags");
c.gridx = 0; //x grid position
c.gridy = 0; //y grid position
gridbag.setConstraints(tagLbl, c); //设置标签的限制

contentPane.add(tagLbl); //增加到内容面板

我们所做的是示例我们的标签、分配给它一个格位置,将它与一个约束对象联系起来并把它增加到我们的内容面板中。

tagModeLbl = new JLabel("Tag Mode");
c.gridx = 0;
c.gridy = 1;
gridbag.setConstraints(tagModeLbl, c);

contentPane.add(tagModeLbl);

  请注意,虽然我们已经在我们的约束对象中把gridx的值设置为0,但是在这里我们仍然要对它进行重新设置——这样做没有其它原因,只是为了增加可读性。

    下面,我们增加一个文本域以便能存储我们希望能搜索到的关键字,再增加一个组合框以便用来搜索多个关键字。除了我们希望的文本域有两列之外,这个概念其他的方面都与上面所说的是相同的,所以,我们需要在增加组合框之前重新设置文本域的值。

tagTxt = new JTextField("plinth");
c.gridx = 1;
c.gridy = 0;
c.gridwidth = 2;
gridbag.setConstraints(tagTxt, c);
contentPane.add(tagTxt);

String[] options = {"all", "any"};
modeCombo = new JComboBox(options);
c.gridx = 1;
c.gridy = 1;
c.gridwidth = 1;
gridbag.setConstraints(modeCombo, c);
contentPane.add(modeCombo);

      做了这些之后,我们再在内容面板中增加一些其余的简单组件,这时候我们就能够浏览它了;其余的代码应该不会出现任何问题了。

到这个阶段,我们应该已经得到了一个类似于我们先前所设计的界面了。

进一步学习

     当然,界面不是智能的。重新设置窗口的大小,看看将会发生些什么事情。为什么它会那样呢?那是因为我们设置了约束对象的weightx、weighty和fill的值。

关于其他类似的一些内容我们将在后面的章节中进行介绍,但是如果你希望能自己试试的话,参考GridBigLayoutGridBagConstraintsAPI文档会对扩充你的知识提供很好的帮助。

posted @ 2012-03-02 14:46  aflylove  阅读(1303)  评论(0编辑  收藏  举报