java 网格袋布局

网格袋布局是网格布局管理器的延伸。网格袋布局和网格布局有下列区别:
· 组件可以占据网格内的多个单元
· 不同行和列之间的比例可以不相同
· 网格单元内部的组件可以用不同的方式安排
为了创建网格袋布局,要使用GridBagLayout类和一个辅助类GridBagConstraints。GridBagLayout是布局管理器,GridBagConstraints用于定义放置在单元中的每个组件的属性----它的位置、大小、对其方式等。网格袋之间的关系、限制和每个组建定义了整个布局。
最常用的形式中,创建网格袋布局涉及下列步骤:
(1)创建GridBagLayout对象,并作为当前布局管理器来定义它,就像任何其他布局管理器那样
(2)创建GridBagConstratints的一个实例。
(3)设置组件的限制
(4)告诉布局管理器关于组件和它的限制
(5)添加组件到容器
下面的例子添加了一个按钮到实现网格袋布局的容器:
// set up layout
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
getContentPane().setLayout(gridbag);

// define constraints for the button
JButton btn = new JButton("Save");
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 30;
constraints.weighty = 30;
constraints.fill = GridBagConstraints.NONE;
constraints.adchor = GridBagConstraints.CENTER;
//attach constraints to layout, add button
gridbag.setConstraints(btn, constraints);
getContentPane().add(b);
从上面的例子中可以看见,必须为添加到面板之中的每个组建设置所有的约束。通过给出大量的约束,可以帮助你进行计划,并一次处理各种约束。

1.设计网格
在网格袋布局中的第1件事情是在纸面上进行的。预先绘出用户界面设计----甚至在编写代码之前----将在未来很长时间内极大的帮助指出所有的东西。将编辑器放在一边,拿起一张纸和一支笔,然后建立网格。
当绘制网格时,应当记住每个组件有他自己的单元。你不能在相同的单元中放置多个组件。然而,一个组件可以在x或者y方向上跨越多个单元。
当你仍然在纸面上工作时,请标出单元的x和y坐标。这将在以后对你有所帮助。他们并不是象素坐标。相反,他们是单元坐标。左上单元是(0,0)。定行中它右边的单元就是(1,0)。一个右边的单元是(2,0)。转移到下一行,最左边的单元是(0,1),行中的下一个单元是(1,1)等。在纸面上用这些数字标注单元,稍后,当你为这个例子编码时你将需要它们。

2。创建网格
现在我们返回到Java,并开始实现你刚刚在纸面上绘制的布局。最初,你将把注意力完全集中在布局上----建立网格和合适的比例。虽然如此,使用按钮作为布局中实际元素的占位符是比较容易的。它们易于创建,而且它们明确的定义了组件在布局管理器----或者正在使用的管理器中所占据的空间。当正确设置了所有东西之后,可以用正确地元素代替按钮。
为了减少键盘输入量,你必须设置所有那些限制,你可以通过定义一个帮助方法开始,它带有几个值,而且为那些值设置限制。bulidConstraints()方法有7个参数:一个GridBagConstraints对象和6咯整数,分别表示GridBagConstraints的实例变量gridx、gridy、gridwidth、gridheight、weightx和weighty。
void buildConstraints(GridBagConstraints gbc, int gx, int gy,
int gw, int gh, int wx, int wy) {
gbc.gridx = gx;
gbc.gridy = gy;
gbc.gridwidth = gw;
gbc.gridheight = gh;
gbc.weightx = wx;
gbc.weighty = wy;
}
我们来看一看应用程序的构造函数方法,所有的布局将在这里发生。这里是基本方法定义,在这里,你定义GridBagLayout是最初的布局管理器,并创建一个限制对象(GridBagConstraints的实例):
public NamePass() {
super("Username and Password");
setSize(290, 110);
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
JPanel pane = new JPanel();
pane.setLayout(gridbag);
setContentPane(pane);
constraints.fill = GridBagConstraints.BOTH;
}
有一点需要注意:最后一行,它设置了constraints.fill的值。它似的组件可以填充包含它们的整个单元,这将帮助你了解正在进行的工作。
在布局中加入按钮占位符(记住,现在要把注意力集中在基本网络组织上,所以将使用按钮作为添加的实际用户界面元素的占位符)。我们从一个简单的按钮开始,这样可以感觉如何设置它的限制。整段代码将位于高造函数方法中setLayout行之后:
// Name label
buildConstraints(constraints, 0, 0, 1, 1, 100, 100);
JButton label1 = new JButton("Name:");
gridbag.setConstraints(label1, constraints);
add(label1);
这4行设置了对象的约束,创建一个新按钮,并在按钮上施加约束,然后将其添加到面板上。注意,组件的约束存储在GridBagConstraints对象中,所以组件甚至不用退出就可以设置它底约束。
现在遇到了一个细节问题,在帮助方法buildConstraints()中底约束设定了什么值?
前面底两个整数参数是约束底gridx和gridy值。它们是包含这个组件单元的单元坐标。你在第1个步骤中是如何在纸面上绘制那些组件底?在纸面上明确标明单元编号的情况下,所需要的全部工作就是填写正确地数值。注意,如果某个组件跨越多个单元,则单元坐标就是左上角那个单元的坐标。
这个按钮位于左上角,所以它的gridx和gridy(buildConstraints()的前两个参数)分别是0和0。
后两个整数参数是gridwidth和gridheight。它们并不是单元得象素高度和宽度。相反它们是这个组件所跨越得单元数量:gridwidth代表列,gridheight代表行。这里是一个仅仅跨越一个单元得组件,所以两个值都是1。
最后得两个整数参数是weightx和weighty。它们用于设置行和列得比例----就是说,它们将有多宽或者多深。权重(weight)非常容易让人迷惑,所以现在,我们将两个值都设置为100。
在建立限制之后,可以使用setConstraints()方法将它们追加到对象上。setConstraint()是在GridBagLayout中定义的方法,它有两个参数:组件(这里是一个按钮)和那个组件得约束。最后,你可以在面板上添加按钮。在设置和将约束赋予组件之后,可以再次使用GridBagConstraints对象来设置下一个对象得限制。因此,你将为王各种得每个组件重复这4行操作,但buildConstraints方法的值不同。为了节省空间,我们对最后4个单元仅仅说明buildConstraints()方法。
要添加的第2个单元是保存名称的文本框。单元坐标是1,0(第2列,第1行)。它也仅仅占据一个单元,而权重都是100:
buildConstraints(constraints, 1, 0, 1, 1, 100, 100);
下面的两个组件是一个标签和一个文本域,几乎和前面的两个一样。唯一的差别在于它们的单元坐标。口令标签是0,1(第1行,第2列),口令文本域是1,1(第2列,第2行):
buildConstraints(constraints, 0, 1, 1, 1, 100, 100);
buildConstraints(constraints, 1, 1, 1, 1, 100, 100);
最后我们需要OK按钮,它是占据面板的底部行中的两个单元组件。在这里,单元坐标是最左边和最下边的单元,从(0,2)开始。这里和前面的组件不同,因为这个单元跨越多列,我们将设置gridwidth和gridheight为1以外的某个值。gridwidth是2(它跨越了两个单元),而gridheight是1(它仅仅跨越1行):
buildConstraints(constraints, 0, 2, 2, 1, 100, 100);
我们已经为添加到网格布局中的所有组件设置了布局限制,还需要将每个组建的限制指定给布局管理器,然后将每个组件添加到面板上。注意,在这里我们并不关心确切的比例,或者确保对其所有的东西。此时,应该跟踪的就是确保网格可以使用,有正确地行数和列数,跨越是正确的,而且没有人和奇怪的事情(单元位置错误,单元重叠等等诸如此类)。

3.确定比例
下一个步骤就是确定行和列的比例,例如,在这个例子中,我们希望标签(名称和口令)所占据的空间比文本框少,希望底部的OK按钮仅仅有它上面的两个文本框高度的一半。我们可以使用weightx和weighty限制来在布局内安排单元的比例。
考虑weightx和weighty的最简单的方法就是,它们的值或者是面板的总体宽度和高度的百分比,或者,如果其他某些单元已经设置了宽度或者高度,则它们是0。因此,所有组建的weightx和weighty的值相加应该等于100。
哪一个单元得到值,哪一个单元得到0?跨越多航或者多列的单元应该在它们所跨越的方向上设置0。除此之外,决策非常简单,挑选一个单元赋予值,然后那行或者列中的所有其他单元都应该是0。
我们研究一下前面步骤中建立的对buildConstraints()方法的5次调用:
buildConstraints(constraints, 0, 0, 1, 1, 100, 100); //name
buildConstraints(constraints, 1, 0, 1, 1, 100, 100); //name text
buildConstraints(constraints, 0, 1, 1, 1, 100, 100); //password
buildConstraints(constraints, 1, 1, 1, 1, 100, 100); //password text
buildConstraints(constraints, 0, 2, 2, 1, 100, 100); //OK button
我们将把每次对buildConstraints的调用中的最后两个参数修改为某个值或者0。从x方向开始(列的比例),它是前面列表中的第2个到最后一个参数。
注意,第2列比的一列要大的多。如果准备选择那些列的理论百分比,你可能说第一个是10%,而第2个是90%(这是一个猜想,你所做的全部也就是猜想)。利用这两个猜想,可以将它门赋予单元。因为OK按钮单元跨越了两列,而百分比没有任何利用,因此你并不需要为OK按钮单元指定任何值。将它们添加到前面的两个单元:名称标签和名称文本域:
buildConstraints(constraints, 0, 0, 1, 1, 10, 100); //name
buildConstraints(constraints, 1, 0, 1, 1, 90, 100); //name text
剩余的两个单元(口令标签和文本域)的值如何?因为名称标签和域已经设置了列的比例,你不能在这里重新设置他们。为这些单元以及OK框提供0值:
buildConstraints(constraints, 0, 1, 1, 1, 0, 100); //password
buildConstraints(constraints, 1, 1, 1, 1, 0, 100); //password text
buildConstraints(constraints, 0, 2, 2, 1, 0, 100); //OK button
注意,在这里,0并不意味着单元的宽度是0。这些值是比例,而不是象素值。0仅仅意味着比例已经在别的什么地方设置了。0的含义就是“拉伸它,以适合比例”。
现在所有的weightx限制的总和是100,你可以转移到weighty参数。在这里,你有3行。看一看绘制的网格,似乎按钮占据了20%,而文本域占据了剩下的(每个是40%)。和x值一样,我们不得不设置每行每个单元的值(两个标签和按钮),所有其他单元的weightx应该是0。
这里是使用了权重的对buildConstraints()的最终5次调用:
buildConstraints(constraints, 0, 0, 1, 1, 10, 40); //name
buildConstraints(constraints, 1, 0, 1, 1, 90, 0); //name text
buildConstraints(constraints, 0, 1, 1, 1, 0, 40); //password
buildConstraints(constraints, 1, 1, 1, 1, 0, 0); //password text
buildConstraints(constraints, 0, 2, 2, 1, 0, 20); //OK button
在这个步骤中,目标就是提出一些基本的比例,它们安排了如何在屏幕上放置行和列。在你所期望的各种不同组件大小的基础上,可以进行一些估计,但在此过程的这个部分中需要利用试错法进行大量的测试修改。
4.添加和安排组件
在有布局和比例之后,现在可以用实际的标签和文本替换按钮占位符。因为你已经设置了所有东西,它应该很好的工作,不是吗?当然,一定是这样的。
现在,我们所缺少的东西就是安排单元内组件的限制条件。有两个限制:fill和anchor。
fill限制决定了----对于可以在任何方向上拉伸组件,以在两个方向上填充单元:
· GridBagLayout.BOTH,它将拉伸组件,以在两个方向上填充单元
· GridBagLayout.NONE,这将导致以最小的大小显示组件
· GridBagLayout.HORIZONTAL,它将在水平方向向上拉伸组件
· GridBagLayout.VERTICAL,它将在垂直方向上拉伸组件
默认情况下,所有组件的fill约束都是NONE。如果是这种情况,为什么文本域和标签填充了单元,如果回到这个例子的编码开始,在init()方法中加入了这行:
constraints.fill = GridBagConstraints.BOTH;
影响组件在单元内出现方式的第2个先知就是anchor。这个限制仅仅适用于不能填充整个单元的组件,它告诉Java在单元内的什么地方放置组件。anchor显示的可能值是GridBagConstraints.CENTER,它将在单元内从垂直和水平方向上同时居中组件,或者下面的8个方向之一:
GridBagConstraints.NORTH
GridBagConstraints.NORTHEAST
GridBagConstraints.EAST
GridBagConstraints.SOUTHEAST
GridBagConstraints.SOUTH
GridBagConstraints.SOUTHWEST
GridBagConstraints.WEST
GridBagConstraints.NORTHWEST
anchor的默认值是GridBagConstraints.CENTER。
我们按照与其他限制相同的方式来设置这些限制:通过修改GridBagConstraints对象中的实例变量。在这里,可以修改GridBagConstraints()的定义来得到另外两个参数(它们是整数),或者可以在init()方法的内部设置它们。在这个项目中使用了后一种方法。
要注意默认值。记住,应为你正在为每个组件重复使用相同的GridBagConstraints对象,当完成某个组件之后,可能会留下某些值。另一方面,如果来自一个对象的fill或者anchor和前面的一个是一样的,则没有必要重新设置那个对象。
对于这个例子,将对组件的fill和anchor值进行3个改动:
· 标签将没有fill,而且居中方式是EAST(这样,它们将停留在单元的最右边)
· 文本域将水平填充(这样,它们从1行高度开始,但拉伸到单元的宽度)
· 按钮将没有fill,而且是中心对齐。

5.调整
当使用自己的程序和网络袋布局时,将会注意到,产生的布局通常要进行修补。你可能需要使用限制的不同值,以使得界面有正确的形式。那没有任何错误----前面步骤的目的就是让所有东西尽可能的接近最终的位置,但并不是每次都能得到完美的结局。
程序清单3.4说明了建立布局的完整代码。如果在跟随讨论达到这个结果的过程中有问题,你可以发现逐行研究代码是有用的,以确保你理解了各个部分。
程序清单3.3 完整的Border.java源代码
--------------------------------------------------------------------------------
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class NamePass extends JFrame {
void buildConstraints(GridBagConstraints gbc, int gx, int gy,
int gw, int gh, int wx, int wy) {
gbc.gridx = gx;
gbc.gridy = gy;
gbc.gridwidth = gw;
gbc.gridheight = gh;
gbc.weightx = wx;
gbc.weighty = wy;
}
public NamePass() {
super("Username and Password");
setSize(290, 110);
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
JPanel pane = new JPanel();
pane.setLayout(gridbag);
// Name label
buildConstraints(constraints, 0, 0, 1, 1, 10, 40);
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.EAST;
JLabel label1 = new JLabel("Name:", JLabel.LEFT);
gridbag.setConstraints(label1, constraints);
pane.add(label1);
// Name text field
buildConstraints(constraints, 1, 0, 1, 1, 90, 0);
constraints.fill = GridBagConstraints.HORIZONTAL;
JTextField tfname = new JTextField();
gridbag.setConstraints(tfname, constraints);
pane.add(tfname);
// password label
buildConstraints(constraints, 0, 1, 1, 1, 0, 40);
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.EAST;
JLabel label2 = new JLabel("Password:", JLabel.LEFT);
gridbag.setConstraints(label2, constraints);
pane.add(label2);
// password text field
buildConstraints(constraints, 1, 1, 1, 1, 0, 0);
constraints.fill = GridBagConstraints.HORIZONTAL;
JPasswordField tfpass = new JPasswordField();
tfpass.setEchoChar('*');
gridbag.setConstraints(tfpass, constraints);
pane.add(tfpass);
// OK Button
buildConstraints(constraints, 0, 2, 2, 1, 0, 20);
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.CENTER;
JButton okb = new JButton("OK");
gridbag.setConstraints(okb, constraints);
pane.add(okb);
// Content Pane
setContentPane(pane);
}
public static void main(String[] arguments) {
NamePass frame = new NamePass();
ExitWindow exit = new ExitWindow();
frame.addWindowListener(exit);
frame.show();
}
}
class ExitWindow extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
--------------------------------------------------------------------------------
3.5 单元填充和边距
在我们结束网格袋布局之前,还需要注意两个其他限制:ipadx和ipady。这两个限制控制了填充(单个组件周围的额外空间)。默认情况下,任何组件周围都没有额外空间(这很容易在填充他们单元的组件中看见)。
ipadx在组件的两侧添加空间,而ipady在上下添加空间。
当创建新的布局管理器(或者在网络袋布局中使用ipadx和ipady的时候),将出现水平吓垂直间隙,这些间隙用于确定面板中组件之间的空间。然而,边距用于确定面板本身周围的空间。insets类包含顶、底、左和右边距底几个值,然后,当绘制面板本身底时候,将使用它们。
边距决定了面板边缘之间和面板底组件之间的空间。
为了在布局中包含inset,要重载Java1.02的insets()方法,或者Java2的getInsets()方法。这些方法完成相同的工作。
在insets()或者getInsets()方法内部,创建新的Insets对象,在这里Insets类的构造函数方法有4个整数值,表示面板顶部、左边、底部和右边的边距。insts()方法应该返回那个Insets对象。这里是为网格布局添加边距的一些代码:顶部和底部是10,左边和右边是30。
public Insets insets() {
return new Insets(10, 30, 10, 30);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值