技术开发 频道

Java图形用户界面设计

  3.1 FlowLayout/GridLayout/BorderLayout的应用实例

  3.1.1应用背景

  假设我们要编写一个简单的计算器JApplet,其基本界面如下:

  3.1.2解决方法

  通过其界面要求可知,我们可以通过将"BackSpace"和"Clear"JButton放置在一个JPanel(1)中,采用FlowLayout布局;将显示结果的JTextField和该JPanel一起放置到另外一个JPanel(2),采用GridLayout布局;而将其它的JButton则放置在另外一个JPanel(3)中,采用GridLayout布局;再将JPanel(2)和JPanel(3)加入该JApplet,即可实现界面需求。具体实现方法如下:

  这样,就一切OK啦。具体的实现代码可参见附件中的CalculateApplet.java文件。

  3.2 带工具栏和状态栏的GridLayout/BorderLayout应用实例

  3.2.1实际问题

  在很多情况下我们需要动态设置工具栏和状态栏,看下面的应用实例:

  以上是在视图的工具栏和状态栏都被复选的时候,以下分别为某一个没选或都未选的情况。

  3.2.2解决方法

1 /**工具栏JToolBar采用从左开始的FlowLayout布局*/
2 JToolBar toolBar = new JToolBar();    
3 toolBar.setBorderPainted(false); //不画边界
4     toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
5     
6 /**窗体采用动态的BorderLayout布局,通过获取工具栏或状态栏的复选标记进行界面的动态调整*/
7 JSplitPane splitPane = new JSplitPane();
8 splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); //设置统计窗口分隔条的方向
9 splitPane.setDividerLocation(300);     //设置分隔条的位置
10 splitPane.setOneTouchExpandable(true);
11 JCheckBoxMenuItem toolBarItem = new JCheckBoxMenuItem("工具栏(T)", true);
12     JLabel statusLabel = new JLabel("当前统计目标:");
13     JCheckBoxMenuItem statusBarItem = new JCheckBoxMenuItem("状态栏(S)", true);
14 /**设置系统窗体布局并动态设置工具栏和状态栏*/
15     private void setLayout()
16     {
17         if (toolBarItem.getState() &&' statusBarItem.getState())
18         {
19             this.getContentPane().add(BorderLayout.NORTH, toolBar);
20             this.getContentPane().add(BorderLayout.CENTER, splitPane);
21             this.getContentPane().add(BorderLayout.SOUTH, statusLabel);
22         }
23         else if (toolBarItem.getState() && !statusBarItem.getState())
24         {
25             this.getContentPane().add(BorderLayout.NORTH, toolBar);
26             this.getContentPane().remove(statusLabel);
27         }
28         else if (statusBarItem.getState() && !toolBarItem.getState())
29         {
30             this.getContentPane().add(BorderLayout.SOUTH, statusLabel);
31             this.getContentPane().remove(toolBar);
32         }
33         else if (!toolBarItem.getState() && !statusBarItem.getState())
34         {
35             this.getContentPane().remove(toolBar);
36             this.getContentPane().remove(statusLabel);
37         }
38         this.show(); //添加或移去组件后刷新界面
39     }
40

  通过该方法即可实现界面的动态刷新与调整。

  3.3 GridBagLayout应用实例

  3.3.1实际问题

  GridBagLayout是Java API提供的一个较复杂的布局管理器,利用好它可以解决许多实际编程中的令人烦恼的界面设计问题。看下面的界面应用实例:

        

  3.3.2解决方法

1 /**系统的界面布局实现*/
2 GridBagConstraints gridBag = new GridBagConstraints();
3 gridBag.fill = GridBagConstraints.HORIZONTAL;  //以水平填充方式布局
4     gridBag.weightx = 0;  //行长不变
5     gridBag.weighty = 0;  //列高不变
6     fromLabel.setForeground(Color.blue);
7     fromLabel.setFont(new Font("Alias", Font.BOLD, 16));
8     this.add(fromLabel, gridBag, 0, 1, 1, 1);  //指定发信人标签位置
9     receiveLabel.setForeground(Color.blue);
10     receiveLabel.setFont(new Font("Alias", Font.BOLD, 16));
11     this.add(receiveLabel, gridBag, 0, 2, 1, 1); //指定收信人标签位置及大小
12     ccLabel.setForeground(Color.blue);
13     ccLabel.setFont(new Font("Alias", Font.BOLD, 16));
14     this.add(ccLabel, gridBag, 0, 3, 1, 1); //指定抄送人标签位置及大小
15     subjectLabel.setForeground(Color.blue);
16     subjectLabel.setFont(new Font("Alias", Font.BOLD, 16));
17     his.add(subjectLabel, gridBag, 0, 4, 1, 1); //指定主题标签位置及大小
18     accessoryLabel.setForeground(Color.blue);
19     accessoryLabel.setFont(new Font("Alias", Font.BOLD, 16));
20     this.add(accessoryLabel, gridBag, 0, 5, 1, 1); //指定附件标签位置及大小
21     gridBag.weightx = 100; //行自适应缩放
22     gridBag.weighty = 0;//列高不变
23     fromField.setText("admin@watermelon.com");
24        this.add(fromField, gridBag, 1, 1, 2, 1); //指定发信人文本域(JTextField)位置及大小
25     this.add(receiveField, gridBag, 1, 2, 2, 1); //指定收信人文本域(JTextField)位置及大小
26     this.add(ccField, gridBag, 1, 3, 2, 1); //指定抄送人文本域(JTextField)位置及大小
27     this.add(subjectField, gridBag, 1, 4, 2, 1); //指定主题文本域(JTextField)位置及大小
28     accessoryArea.setEditable(false);
29 //设置不显示水平滚动条(该JTextArea置于JScrollPane中)
30 accessoryScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
31     this.add(accessoryScroll, gridBag, 1, 5, 2, 1); //指定附件文本区(JTextArea)位置及大小
32     gridBag.fill = GridBagConstraints.BOTH;//采用全填充方式布局
33     gridBag.weightx = 100;//行自适应缩放
34     gridBag.weighty = 100;//列自适应缩放
35     mailArea.setBackground(Color.blue);
36     mailArea.setForeground(Color.yellow);
37     mailArea.setTabSize(4);
38     //指定信件主体区(JTextArea)的位置及大小。(该JTextArea也置于JScrollPane中)
39     this.add(scroll, gridBag, 0, 6, 3, 1);
40     在上面用到一个方法add(),这个方法是自己定义的:
41 private void add(Component c, GridBagConstraints gbc, int x, int y, int w, int h)
42     {
43         gbc.gridx = x;
44         gbc.gridy = y;
45         gbc.gridheight = h;
46         gbc.gridwidth = w;
47         this.getContentPane().add(c, gbc);
48     }
49

  这个界面的设计比较复杂,涉及多个标签域(JLabel)、文本域(JTextField、JTextArea),且标签域的大小还不一样,如附件标签;并当窗体缩放时,标签域的大小应不改变,而文本域则必须自适应缩放。如何来实现呢?请看下面的代码:(工具栏的实现不再赘述)

  在用到GridBagLayout布局管理器的组件添加方法中,都可以重用它。事实上,你还可以在方法最前面加一个参数Container cn,而将方法中的this相应的改为cn,就可以通用于所有需要使用GridBagLayout进行布局管理的容器中。在下面的复杂例程中我们就会用到。

  3.4 综合多个布局的复杂应用实例

  3.4.1实际问题

  请看下面的实际应用界面要求:

  (图3.4-1)

        

  (图3.4-2)

       

  (图3.4-3)

        

  在这个具体应用中,底部的JButton组是确定的,但JTabbedPane的每一个选项都不同,如何实现呢?

  3.4.2解决方案

1 JTabbedPane dbTabPane = new JTabbedPane();
2 …… //下面需要用到的JButton等组件变量定义(或声明)
3 private void initLayout()
4 {
5 initDBTabPane();//初始化JTabbedPane:DBTabPane组件
6     this.getContentPane().add(BorderLayout.CENTER, dbTabPane);
7     //将JTabbedPane组件:dbTabPane布局于JDialog对话框的中间
8         initButtonPanel();//初始化JPanel:ButtonPanel组件
9         this.getContentPane().add(BorderLayout.SOUTH, buttonPanel);
10     //将JPanel组件:buttonPanel布局于JDialog对话框的底部(南面)
11     }
12 private void initDBTabPane()
13 {
14         JPanel loginPanel = new JPanel(new GridLayout(10, 1));
15 //为保证两个JCheckBox组件位于顶端,设置为共10行,每行一个组件的布局,但只
16 //放置界面要求的两个组件,这样就保持了界面的美观,否则如定义为
17 //Gridlayout(2,1)则会使两个组件居中,而且中间会隔开较长的距离。
18 pwdBox.setMnemonic('P');
19         loginPanel.add(pwdBox);
20         dspBox.setMnemonic('D');
21         loginPanel.add(dspBox);
22         dbTabPane.add("Login", loginPanel); //设置"Login"JPanel(图3.4-1)的布局
23         needRadio.setMnemonic('N');
24         allRadio.setMnemonic('A');
25         cacheRadio.setMnemonic('U');
26         radioPanel.setBorder(new TitledBorder("Load Option"));//加上边界标题
27         radioPanel.add(needRadio);
28         radioPanel.add(allRadio);
29         radioPanel.add(cacheRadio);
30 //以上为加入需要的JRadioButton组件到指定的JPanel: radioPanel
31         queryPanel.add(radioPanel);//加入含JRadioButton组的JPanel到queryPanel
32         reqBox.setMnemonic('R');
33         boxPanel.add(reqBox);
34         saveBox.setMnemonic('S');
35         boxPanel.add(saveBox);
36         autoBox.setMnemonic('t');
37         boxPanel.add(autoBox);
38 //以上为加入需要的JCheckBox组到指定的JPanel:boxPanel
39         queryPanel.add(boxPanel); //加入含JCheckBox组的JPanel到queryPanel
40         dbTabPane.add("Query", queryPanel);//设置"Query"JPanel(图3.4-2)的布局
41         initDrvPanel();
42     }
43 /**设置"Drivers"JPanel(图3.4-3)的布局*/
44 private void initDrvPanel()
45 {
46         gridBag.fill = GridBagConstraints.HORIZONTAL;
47         gridBag.weightx = 100;
48         gridBag.weighty = 0;
49         tipLabel.setForeground(Color.black);
50         this.add(drvPanel, tipLabel, gridBag, 0, 0, 4, 1);
51         urlLabel.setForeground(Color.black);
52         this.add(drvPanel, urlLabel, gridBag, 0, 5, 4, 1);
53         urlField.setEditable(false);
54         this.add(drvPanel, urlField, gridBag, 0, 6, 4, 1);
55         gridBag.weightx = 0;
56         gridBag.weighty = 0;
57         addButton.setMnemonic('A');
58         this.add(drvPanel, addButton, gridBag, 3, 1, 1, 1);
59         editButton.setMnemonic('E');
60         this.add(drvPanel, editButton, gridBag, 3, 2, 1, 1);
61         removeButton.setMnemonic('R');
62         this.add(drvPanel, removeButton, gridBag, 3, 3, 1, 1);
63         gridBag.fill = GridBagConstraints.BOTH;
64         gridBag.weightx = 100;
65         gridBag.weighty = 100;
66            //设置JTable组件:drvTable的从0到7行第0列的值
67 for (int i = 0; i < 8; i++)
68                drvTable.setValueAt(drvStrs[i],i,0);
69 //设置JTable的列头
70 drvTable.getColumn(drvTable.getColumnName(0)).setHeaderValue("All Drivers");
71         drvTable.setShowGrid(false);//设置不显示网格线
72         this.add(drvPanel, drvScroll, gridBag, 0, 1, 3, 4);
73         dbTabPane.add("Drivers", drvPanel);
74    }
75 /**初始化底部JButton组的布局*/
76 private void initButtonPanel()
77     {
78         JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
79 //从右边开始进行FlowLayout布局
80 okButton.setMnemonic('O');
81         buttonPanel.add(okButton);
82         cancelButton.setMnemonic('C');
83         buttonPanel.add(cancelButton);
84         helpButton.setMnemonic('H');
85         buttonPanel.add(helpButton);
86     }
87 /**给指定的容器cn在指定的(x,y)位置放置指定大小(宽度=w,高度=h)的组件c*/
88 private void add(Container cn, Component c, GridBagConstraints gbc, int x, int y, int w, int h)
89 {
90         gbc.gridx = x;
91         gbc.gridy = y;
92         gbc.gridwidth = w;
93         gbc.gridheight = h;
94         cn.add(c, gbc);
95     }
96

  首先我们可以采用BorderLayout确定主题对话框的布局方式,实现方法如下:

  结束语

  以上是本人在两年多J2EE应用开发中,总结的关于用Java进行GUI设计的一些经验,希望能给曾经象我一样迷惘,但依旧对Java一往情深,至今仍在摸索探求Java GUI设计捷径的朋友一些启示。更希望借此机会抛砖引玉,与更多的朋友进行交流与探讨。其实,在Java中所有的布局管理器都要实现一个接口,即LayoutManager Inerface或者是它的一个子接口LayoutManager2 Interface,后者用于更复杂的布局管理。如果在实际应用中,觉得Java API提供的这些布局管理器仍不够用,你完全可以自己来实现其中某一个接口的方法,从而为你自己的具体GUI应用设计提供更好的布局管理。

  附件

  JavaSrc.zip包含下列java源代码和Class代码:

  CalculatorApplet.java为计算器Applet的实现代码。可以通过点击Calculator.html运行;或通过appletviewer来运行。

  DBOptions.java为3.4实例的具体实现代码。你可以通过自己编写一个JFrame来调用。

0
相关文章