第九章 Swing用户界面组件

第九章 Swing用户界面组件

9.1 Swing和模型-视图-控制器的设计模式

9.1.1设计模式

    在Alexander的模式分类和软件模式的分类中,每种模式都遵循特定的格式。

  • 首先描叙背景,即引发设计问题的情形;

  • 接着解释问题,通常这里会有几个冲突的因素;

  • 最终,权衡这些冲突,给出问题的解决方案


    AWT和Swing设计中使用的几种模式:

  • 模型-视图-控制器(model-view-controller,MVC)

  • 容器和组件是“组合(composite)”模式

  • 带滚动条的面板是“装饰(decorate)”模式

  • 布局管理器是“策略(strategy)”模式


9.1.2模型-视图-控制器模式

    每个组件都有三要素(如按钮,复选框,文本框或复杂的树形组件)

  • 内容,如按钮的状态(是否按下),或者文本框的文本

  • 外观(颜色、大小等)

  • 行为(对事件的反应)


    面向对象设计的一个基本原则:限制一个对象拥有的功能数量。如不要用一个按钮类完成所有的事情,而是应该让一个对象负责组件的观感,另一个对象负责存储内容。MVC模式告诉我们,实现三个独立的类:

  • 模型(model):存储内容

  • 视图(view):显示内容

  • 控制器(controller):处理用户输入

    模型存储内容,他没有用户界面,完全不可见,必须实现改变内容和查找内容的方法。显示存储在模型中的数据是视图的工作。

    MVC的一个优点,一个模型可以有多个视图,每个视图可以显示全部内容或不同部分或不同形式。

    控制器负责处理用户输入事件,如鼠标和键盘。然后决定是否把这些事件转化成对模型或视图的改变。


9.1.3 Swing按钮的模型-视图-控制器分析

    对大多数组件来说,模型类将实现一个名字以Model结尾的接口,如按钮实现了ButtonModel接口。实现了此接口的类可以定义各种按钮的状态。

    每个JButton对象都存储了一个按钮模型对象,用下列方式得到它的引用;

JButton button = new JButton("Blue");
ButtonModel model = button.getModel();

    实际上,不必要关注按钮状态的零散信息,只有绘制它的视图才对此感兴趣。

   模型不存储按钮标签或者图标。

    需注意的是,同样的模型(即DefaultButtonModel)可用于下压按钮、单选安钮、复选框甚至是菜单项。当然这些按钮都有各自不同的视图和控制器。通常,每个Swing组件都有一个相关的后缀为UI的视图对象,但并非所有Swing组件都有专门的控制器对象。


  • ButtonModel接口的属性

属性名

ActionCommand

与按钮关联的动作命令字符串

Mnemonic

按钮的快捷键

Armed

如果按钮被按下且鼠标仍在按钮上则为true

Enabled

如果按钮是可选择则为true

Pressed

如果按钮被按下且鼠标按键没有释放则为true

Rollover

如果鼠标在按钮上则为true

Selected

如果按钮已经被选择(用于复选框和单选按钮)则为true

    


9.2 布局管理器概述

    通常,组件放置在容器中,布局管理器(layout manager)决定容器中的组件放置的位置和大小。

    按钮、文本域和其他用户界面元素都继承于Component类,组件可以放置在面板这样的容器中。由于Container类继承于Component类,所以容器也可以放置在另一容器中。下图给出Component的类层次结构。

第九章 Swing用户界面组件_第1张图片

    每个容器都有一个默认的布局管理器,但可以重新设置。

panel.setLayout(new GridLayout(4,4));

    这个面板用GridLayout类布局组件。可往容器中添加组件。容器的add方法把组件和放置的方位传递给布局管理器。

    API java.awt.Container 1.0

  • void SetLayout(LayoutManager m)

    为容器设置布局管理器

  • Component add(Component c)

  • Component add(Component c,Object constraints) 1.1

  • 将组件添加到容器中,并返回组件的引用。

    参数:c                            要添加的组件

              constraints            布局管理器理解的标识符

 API java.awt.FlowLayout 1.0

  • FlowLayout()

  • FlowLayout(int align)

  • FlowLayout(int align,int hgap,int vgap)   

        构造一个新的FlowLayout对象。

      参数:align        LEFT、CENTER或RIGHT

                hgap        以像素为单位的水平间距(如果为负值,则强行重叠)

                vgap        以像素为单位的垂直间距(如果为负值,则强行重叠)


9.2.1 边框布局

    边框布局管理器(border layout manager)是每个JFrame的内容窗格的默认布局管理器。流布局管理器完全控制每个组件的放置位置,边框布局管理器允许为每个组件选择一个放置位置。可选把组件放在内容窗格的中、北、南、东、西部。如:

frame。add(component,BorderLayout.SOUTH);

    先放置边缘组件,剩余可用空间由中间组件占据。容器缩放时,边缘组件厚度不变,中间组件大小变化。默认为CENTER。

    与流布局不同,边框布局会扩展所有组件的尺寸以便填满可用空间(流布局将维持每个组件的最佳尺寸)。当讲一个按钮添加到容器中时会出现问题:

frame.add(yellowButton,BorderLayout.SOUTH);//don't

    按钮将扩展至填满整个南部区域,如果在添加一个按钮到南部,就会取代第一个按钮。解决常用的方法是使用另一个面板(panel),再将面板放置在内容窗格的南部。

    先创建一个新的JPanel对象,然后逐一将按钮添加到面板。面板的默认布局管理器是FlowLayout。然后用frame.add将面板添加到框架的内容窗格中。

JPanel panel = new JPanel();
panel.add(yellowButton);
panel.add(blueButton);
panel.add(redButton);
frame.add(panel,BorderLayout.SOUTH);

    边框布局管理器将会扩展面板大小,直至填满整个南部区域。

    API java.awt.BorderLayout 1.0

  • BorderLayout()

  • BroderLayout(int hgap,int vgap)

    构造一个新的BorderLayout对象。

    参数:hgap        以像素为单位的水平间距(如果为负值,则强行重叠)

              vgap        以像素为单位的水平间距(如果为负值,则强行重叠)


9.2.2 网格布局

    网格布局像电子数据表一样,按行列排列所有的组件。不过,他的每个单元大小都一样的。在网格布局对象的构造器中,需要指定行数和列数:

panel.setLayout(new GridLayout(5,4));

    添加组件,从第一行的第一列开始,然后是第一行的第二列,以此类推。

panel.add(new JButton("1"));
panel.add(new JButton("2"));

    下图是一个计算器:

第九章 Swing用户界面组件_第2张图片

    程序中,在将组件添加到框架之后,调用了pack方法,这个方法用于将所有组件以最佳的高度和宽度显示在框架中。当然,很少有像计算器这样整齐的布局。实际上,在组件窗口的布局是小网格(通常只有一行或一列)比较有用。将其放置在一个面板上,这个面板使用只有一行的网格布局进行管理。

  • Calculator.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * @version 1.33 2007-06-12
 * @author Cay Horstmann
 */
public class Calculator
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               CalculatorFrame frame = new CalculatorFrame();
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}

/**
 * A frame with a calculator panel.
 */
class CalculatorFrame extends JFrame
{
   public CalculatorFrame()
   {
      setTitle("Calculator");
      CalculatorPanel panel = new CalculatorPanel();
      add(panel);
      pack();
   }
}

/**
 * A panel with calculator buttons and a result display.
 */
class CalculatorPanel extends JPanel
{
   public CalculatorPanel()
   {
      setLayout(new BorderLayout());

      result = 0;
      lastCommand = "=";
      start = true;

      // add the display

      display = new JButton("0");
      display.setEnabled(false);
      add(display, BorderLayout.NORTH);

      ActionListener insert = new InsertAction();
      ActionListener command = new CommandAction();

      // add the buttons in a 4 x 4 grid

      panel = new JPanel();
      panel.setLayout(new GridLayout(4, 4));

      addButton("7", insert);
      addButton("8", insert);
      addButton("9", insert);
      addButton("/", command);

      addButton("4", insert);
      addButton("5", insert);
      addButton("6", insert);
      addButton("*", command);

      addButton("1", insert);
      addButton("2", insert);
      addButton("3", insert);
      addButton("-", command);

      addButton("0", insert);
      addButton(".", insert);
      addButton("=", command);
      addButton("+", command);

      add(panel, BorderLayout.CENTER);
   }

   /**
    * Adds a button to the center panel.
    * @param label the button label
    * @param listener the button listener
    */
   private void addButton(String label, ActionListener listener)
   {
      JButton button = new JButton(label);
      button.addActionListener(listener);
      panel.add(button);
   }

   /**
    * This action inserts the button action string to the end of the display text.
    */
   private class InsertAction implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {
         String input = event.getActionCommand();
         if (start)
         {
            display.setText("");
            start = false;
         }
         display.setText(display.getText() + input);
      }
   }

   /**
    * This action executes the command that the button action string denotes.
    */
   private class CommandAction implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {
         String command = event.getActionCommand();

         if (start)
         {
            if (command.equals("-"))
            {
               display.setText(command);
               start = false;
            }
            else lastCommand = command;
         }
         else
         {
            calculate(Double.parseDouble(display.getText()));
            lastCommand = command;
            start = true;
         }
      }
   }

   /**
    * Carries out the pending calculation.
    * @param x the value to be accumulated with the prior result.
    */
   public void calculate(double x)
   {
      if (lastCommand.equals("+")) result += x;
      else if (lastCommand.equals("-")) result -= x;
      else if (lastCommand.equals("*")) result *= x;
      else if (lastCommand.equals("/")) result /= x;
      else if (lastCommand.equals("=")) result = x;
      display.setText("" + result);
   }

   private JButton display;
   private JPanel panel;
   private double result;
   private String lastCommand;
   private boolean start;
}

9.3 文本输入

        文本域(JTextField)和文本区(JTextArea)获取用户输入。文本域单行,文本区多行。JPassword也只接收单行文本,不会将输入的内容显示出来。

        三个类皆继承与抽象JTextComponent类。

9.3.1 文本域JTextField

        将文本域添加到窗口,常用是将其添加到面板或者其他容器,与按钮一样:

JPanel panel = new JPanel();
JTextField textField = new JTextField("Default input",20);
panel.add(textField);

        列数是给AWT设定的首选(preferred)大小的一个提示。如果布局管理器需要缩放这个文本域,会调整其大小。重新设置列数用setColumns。

  •     提示:使用setColumns后,要调用包含这个文本框的容器的revalidate.

textField.setColumns(10);
panel.revalidate();

        revalidate会重新计算容器内所有组件的大小,并重新布局。属于JComponent类.而改变JFrame的组件,要调用validate。

        可用setText()改变文本内容。用getText()获取文本。想去掉前后空格,调用trim:

String text = textField.getText().trim();

9.3.2 标签和标签组件

        想要用标示符标示不带标签的组件:

  1. 用相应的文本构造一个JLabel组件。

  2. 将标签组件放置在距离需要标示组件足够近的地方,以便用户可以知道标签标示的组件。

        JLabel的构造器允许指定出事文本和图表,亦可以选择内容的排列方式。用swing Constans接口中的常量来指定排列方式。

JLabel label = new JLabel("User name",Swing.Constants.RIGHT);

JLabel label = new JLabel("User name",JLabel.RIGHT);

用setText和setIcon可在运行期间设置标签的文本和图标。

9.3.3 密码域

  • API javax.swing.JPasswordField

  • JPasswordField(Sting text,int columns)

  • void setEchoChar(char echo) 为密码域设置回显字符。

  • char[] getPassword()

        返回密码域中的文本。为安全起见,使用之后应该覆写返回的数组内容。

9.3.4 文本区      

textArea = new JTextArea(4,8)

如果文本区文本超出显示范围,剩下的文本就会被剪掉。可以通过开启换行特性来避免裁剪过长的行:(只是视觉效果)

textArea.setLineWrap(true);

9.3.5 滚动窗格

        文本区没有滚动条,需要将文本区插入到滚动窗格(scrollpane)

textArea = new JTextArea(8,40);
JScrollPane scrollPane = new JScrollPane(textArea);

    为任意组件添加滚动功能的通用机制。

9.4 选择组件



你可能感兴趣的:(第九章 Swing用户界面组件)