Swing

Swing

Swing是一个为Java设计的图形用户界面(GUI)工具包。Swing是Java API的一部分。Swing包括了GUI的元器件,如:文本框,按钮,分隔窗格和表。
Swing用于提供一组“轻量级”(全部是 Java 语言)组件,它们用纯Java写成,所以同样可以跨平台使用。
轻量级元件的缺点则是执行速度较慢,优点就是可以在所有平台上采用统一的行为。

1. 30分钟快速上手
1.1 Swing和AWT的关系与区别
  • 关系
    Swing是一个用于开发JAVA应用程序用户界面的开发工具包。以抽象窗口包(AWT)为基础使跨平台应用程序可以使用任何可插拔的外观风格。
    Swing API的大部分是AWT的补充扩展而不是直接的替代。Swing用来绘制轻量级组件的核心渲染功能是由Java 2D提供的,这是AWT的一部分。然而,轻量级和重量级组件在同一个应用中使用会导致Z-order不兼容。
  • 区别
    Swing为基于窗体的GUI应用开发设计,为Java跨平台特性提供了卓越的支持,它完全没有本地代码,不受操作系统的影响,做到了真正的跨平台应用,甚至能够提供本地窗口系统不支持的其他特性。因此比AWT具有更强的实用性,同时比AWT程序拥有更加精致的外观感受。

AWT只提供基本的组件,使很多设计变得复杂,且无法在不同的平台下保持显示风格的一致性。
例如:如果建立一个按钮(Button)对象,就会有一个按钮对象会请求底层操作系统创建一个真正的按钮。即在WindowsNT上执行那么创建的就是WindowsNT按钮;在Linux上执行,那么创建的就是Linux按钮。因此AWT组件外观会受到底层操作系统的影响。

1.2 Swing操作步骤
1.2.1 导入Swing包
import javax.swing.*;

大部分的Swing程序用到AWT的基础底层结构和事件模型,因此需要导入两个包:

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

如果图形界面中包括了事件处理,那么还需要导入事件处理包:

import.javax.swing.event.*;
1.2.2 选择界面风格

Swing允许选择程序的图形界面风格常用的有Java风格,Windows风格等。
下面的代码用于选择图形界面风格,这里选择的是跨平台的Java界面风格。

try{
  UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}catch(Exception e){

}
1.2.3 设置顶层容器

图形界面至少要有一个顶层Swing容器(详见:2.1 容器)。顶层Swing容器为其他Swing组件在屏幕上绘制和处理事件提供支持。
例如:一个框架(JFrame)包括边界、菜单栏、工具栏、状态栏。以及中间占主要部分的窗格。
窗格也可以看作是一种面板,但它是框架的一个组成部分。
组件不会直接放到框架上,而是放在若干面板(JPanel)上,这些面板再放到窗格上。
用框架对象的getContentPane()函数来获得窗格,在调用窗格的add()函数放置面板。

// 声明一个名为SwingApplication的框架
JFrame frame = new JFrame("SwingApplication");
// 声明一个空的面板
JPanel panel = new JPanel();
// 将面板添加到指定的框架中,定义添加面板的布局方式
frame.getContentPane().add(panel, BorderLayout.Center);
...
// 调整此窗口的大小,以适合其子组件的首选大小和布局。
frame.pack();
// 设置声明框架可见,否则框架不显示
frame.setVisible(true);
1.2.3 添加组件

组件(详见:2.2 常用组件)添加需要容器的支持,添加方式与面板添加的方式相类似,面板也有用于添加元件或子面板的add()函数。

// 声明一个按钮组件
JButton button = new JButton("button");
// 将按钮组件添加到之前声明的面板上,定义添加组件在面板上的布局方式
panel.add(button, BorderLayout.Center);
1.2.4 为组件添加事件

组件通过添加事件来实现用户与系统之间的交互,通过监听键盘键入事件和鼠标操作事件来进行相应的处理。

// 添加指定的操作监听器,以接收发自此组件的操作事件。
button.addActionListener(...);
// 添加指定的鼠标侦听器,以接收发自此组件的鼠标事件。
button.addMouseListener(...);
// 添加指定的鼠标移动侦听器,以接收发自此组件的鼠标移动事件。
button.addMouseMotionListener(...);
// 添加指定的鼠标滚轮侦听器,以接收发自此组件的鼠标滚轮事件。容器还接收发自子组件的鼠标滚轮事件。
button.addMouseWheelListener(...);
// 添加指定的按键侦听器,以接收发自此组件的按键事件。
button.addKeyListener(...);
1.3 Swing 的线程策略

通常 Swing 不是线程安全的。除非另行说明,否则所有 Swing 组件及相关类都必须在事件调度线程上访问。
典型的 Swing 应用程序执行处理以响应用户动作所生成的事件。例如,单击 JButton 通知所有添加到JButton 的 ActionListener。由于用户动作所生成的所有事件都在调度线程上指派,所以大部分开发人员不受该限制的影响。

但是,影响存在于构造以及显示 Swing 的应用程序中。对应用程序的 main 方法或 Applet 中方法的调用不在事件调度线程上调用。因此,构造和显示应用程序或 applet 时,必须注意要将控件转移到事件调度线程。
转移控件和开始处理 Swing 的首选方法是使用 invokeLater。invokeLater 方法安排Runnable 在事件调度线程上处理。
以下两个示例都同样很好地用于转移控件和启动 Swing 应用程序:

public class MyApp implements Runnable {
    public void run() {
        // Invoked on the event dispatching thread.
        // Construct and show GUI.
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MyApp(args));
    }
}
public class MyApp {
    MyApp(String[] args) {
        // Invoked on the event dispatching thread. Do any initialization
        // here.
    }

    public void show() {
        // Show the UI.
    }

    public static void main(final String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new MyApp(args).show();
            }
        });
    }
}
2. 技术点说明
2.1 容器

Swing中一些组件继承JComponent类,JComponent类继承于Container类,所以凡是继承此类的组件都可以作为容器使用。

容器从功能上可以分为:
1) 顶层容器:

JFrame——框架,主要用来设计应用程序的图像界面。
JApplet——小应用程序,主要用来设计嵌入式网页中运行的JAVA程序。
JDialog——对话框,通常用来设计具有依赖关系的窗口。
JWindow——窗口。

2) 普通容器:

JPanel——面板,通常只有背景颜色的普通容器。
JScrollPane——滚动窗格,具有滚动条。
JToolBar——工具条,通常将多个组件排成一排或一列。
JSplitPane——分裂窗格,用来装两个组件的容器。
JTabbedPane——选项卡窗格,允许多个组件共享相同的界面空间。

3) 专用容器:

JInternalFrame——内部窗格,可以在一个窗口内显示若干个类似于框架的窗口。
JLayeredPanel——分层窗格,给窗格增加了深度的概念。
JRootPane——根窗格,一般是自动创建的容器。

2.2 常用组件
控件从功能上可以分为:
1) 基本控件,实现人机交互的组件:

JButton——按钮控件
JComboBox——下拉选择框
JList——列表控件
JMenu——菜单控件
JSlider——滚动条控件
JTextField——文本控件,是一个轻量级组件,它允许编辑单行文本。

2) 不可编辑信息的显示控件,向用户显示不可编辑信息的控件:

JLabel——标签,用于短文本字符串或图像或二者的显示区。
JProgressBar——以可视化形式显示某些任务进度的组件。
JToolTip——用来显示 Component 的“提示”。

3) 可编辑信息的显示控件,向用户显示可编辑的格式化信息的控件:

JColorChooser——颜色选取器,提供一个用于允许用户操作和选择颜色的控制器窗格。
JFileChooser——文件选择器,为用户选择文件提供了一种简单的机制。
JTable——用来显示和编辑常规二维单元表。有很多用来自定义其呈现和编辑的工具,同时提供了这些功能的默认设置,从而可以轻松地设置简单表。
JTextArea——显示纯文本的多行区域。
JTree——将分层数据集显示为轮廓的控件。有关面向任务的文档和使用树的示例。

2.3 布局
2.3.1 Swing布局管理器介绍

提供布局逻辑(一句不同的布局管理器和UI内容)

public staic void addComponentsToPane(Container pane){...}

实例化一个容器,通过它的ContentPane加载布局逻辑内容

private static void createAndShowGUI(){
  JFrame frame = new JFrame("...");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  addComponentsToPane(frame.getContentPane());

  frame.pack();
  frame.setVisible(true);
}

main()程序入口,单独起一个线程,实例化UI。

public static void main(String[] args){
  javax.swing.SwingUtilities,invokeLater(new Runnable(){
    public void run(){
      createAndShowGUI();
    }
  });
}
2.3.2 常用的布局管理器

常用的布局管理器有:FlowLayout、BorderLayout、BoxLayout、CardLayout、GridLayout和GridBagLayout。

1) FlowLayout

FlowLayout类是最简单的布局管理器。按照:从左到右,直到没有多余的空间,然后,转到下一行。

public static void addComponentsToPane(Container pane){
  pane.setLayout(new FlowLayout());
  pane.add(new JButton("button1"));
  pane.add(new JButton("button2"));
  ...
}
2) BorderLayout

BorderLayout将界面分成上下左右中五大区域:PAGE_START、PAGE_END、LINE_START、LINE_END、CENTER。

public static void addComponentsToPane(Container pane) {
  JButton button = new JButton("button1");
  pane.add(button, BorderLayout.PAGE_START);

  button = new JButton("button2");
  button.setPreferredSize(new Dimension(200, 100));
  pane.add(button, BorderLayout.CENTER)
}
3) BoxLayout

BoxLayout可以将组件由上至下或由左至右依次加入当前面板。

public static void addComponentsToPane(Container pane) {
  JPanel xPanel = new JPanel();
  xPanel.setLayout(new BoxLayout(xPanel, BoxLayout.X_AXIS));
  xPanel.add(new JButton("button1"));
  xPanel.add(new JButton("button2"));

  pane.add(xPanel, BorderLayout.PAGE_START);
}
4) CardLayout

卡片布局和其他布局不同,它隐藏了一些组件。卡片布局就是一组容器或组件,它们一次仅仅显示一个,组中每一个容器称作卡片。

public static void addComponentsToPane(Container pane){
  final JPanel contentPanel = new JPanel();
  JPanel controlPanel = new JPanel();
  final CardLayout CardLayout  cardLayout=new CardLayout();
  pane.add(contentPanel, BorderLayout.CENTER);
  pane.add(controlPanel, BorderLayout.PAGE_END);
  controlPanel.setLayout(new FlowLayout());

  JButton[] b = new JButton[10];
  for (int i = 0; i < 10; i++) {
    b[i] = new JButton("No" + i);
    contentPanel.add(b[i]);
  }

  contentPanel.setLayout(cardLayout);
  JButton nextButton = new JButton("next");
  nextButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e){
      cardLayout.next(contentPanel);
    }
  });
}
5) GridLayout

GridLayout建立一个组件表格,并且当组件加入时,会一次从左到右,从上到下填充每个格子,并不能指定填充某一个格子。

public static void addComponentsToPane(Container pane){
  JButton[] b = new JButton[10];
  pane.setLayout(new GridLayout(3, 3));
  for (int i = 0; i < b.length; i++) {
    b[i] = new JButton("No" + i);
    pane.add(b[i]);
  }
}
6) GridBagLayout

GridBagLayout是所有AWT布局中最复杂的,同时也是最强大的。GridBagLayout同GridLayout一样,在容器中以网格的形式来管理组件。但GridBagLayout功能要强大得多。
1. GridBagLayout管理的所由有行和列可以大小不同。
2. GridLayout把每个组件限制在一个单元格。而GridBagLayout中的组件可以占据任意大小的矩形区域。

public static void addComponentsToPane(Container pane){
  JButton button;
  pane.setLayout(new GridBagLayout());
  GridBagConstraints c = new GridBagConstraints();

  button = new JButton("button1");
  c.fill = GridBagConstraints.HORIZONTAL;
  c.gridx = 0;
  c.gridy = 0;
  pane.add(button, c);

  button = new JButton("button2");
  c.fill = GridBagConstraints.HORIZONTAL;
  c.weightx = 0.5;
  c.gridx = 1;
  c.gridy = 0;
  pane.add(button, c);

  ...
}
2.4 事件监听

Java Swing组件自动产生各种事件来响应用户行为。Java将事件封装成事件类,并且为每个事件类定义一个事件监听器。
一个组件注册事件监听器方法,表明该组件要响应指定事件。也就是说我们可以通过注册监听器,监听事件源产生的事件,从而在事件处理程序中处理我们所需要处理的用户行为。

2.4.1 注册事件的三种方法
1) 采用一个监听多个if语句来实现

继承ActionListener接口,并且实现actionPerformed方法。通过getActionCommend()方法来获取
事件的事件源。
缺点:当程序比较复杂时,需要一大串if语句来实现。程序的代码比较难阅读和维护。

public class Test_01 extends JFrame implement ActionListener {
  Test_01(){
    JPanel panel = new JPanel();
    JButton button1 = new JButton("按钮一");
    JButton button2 = new JButton("按钮二");

    panel.add(button1);
    panel.add(button2);
    this.getContentPane().add(panel);
    this.setVisible(true);

    button1.addActionListener(this);
    button2.addActionListener(this);
  }

  public void actionPerformed(ActionEvent e){
    String source = e.getActionCommend();
    if(source.equals("按钮一")){
      System.out.println("你按了按钮一");
    }
    if(source.equals("按钮二")){
      System.out.println("你按了按钮二");
    }
  }

  public static void main(String args[]){
    new Test_01();
  }
}
2) 采用匿名内部类实现

缺点:由于匿名内部类和事件组是一起的。根据事件组在代码中的位置不同,类的定义以及处理事件,不便于阅读。如果事件处理程序比较复杂,内部类中的代码会变得很长。

public class Test_02 extends JFrame implement ActionListener {
  Test_02(){
    JPanel panel = new JPanel();
    JButton button1 = new JButton("按钮一");
    JButton button2 = new JButton("按钮二");

    panel.add(button1);
    panel.add(button2);
    this.getContentPane().add(panel);
    this.setVisible(true);

    button1.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          System.out.println("你按了按钮一");
        }
    });

    button2.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          System.out.println("你按了按钮二");
        }
    });
  }

  public static void main(String args[]){
    new Test_02();
  }
}
3) 采用一般内部类实现

优点:单个事件处理,可以被工具栏、菜单栏等重复使用。

public class Test_03 extends JFrame implement ActionListener {
  Test_03(){
    JPanel panel = new JPanel();
    JButton button1 = new JButton("按钮一");
    JButton button2 = new JButton("按钮二");

    panel.add(button1);
    panel.add(button2);
    this.getContentPane().add(panel);
    this.setVisible(true);

    button1.addActionListener(new Button1ActionListener());
    button2.addActionListener(new Button2ActionListener());
  }

  private class Button1ActionListener implement ActionListener{
    public void actionPerformed(ActionEvent e){
      System.out.println("你按了按钮一");
    }
  }

  private class Button2ActionListener implement ActionListener{
    public void actionPerformed(ActionEvent e){
      System.out.println("你按了按钮二");
    }
  }

  public static void main(String args[]){
    new Test_02();
  }
}
2.5 焦点体系
2.5.1 窗口事件和焦点事件
1) FocusEvent
  • FocusEvent的应用:

指示 Component 已获得或失去输入焦点的低级别事件。此低级别事件由 Component(比如 TextField)生成。事件被传递给每一个 FocusListener 或 FocusAdapter 对象,这些对象使用 Component 的 addFocusListener 方法注册,以接收这类事件。(FocusAdapter 对象实现 FocusListener 接口。)当发生该事件时,所有这类侦听器对象都将获得此 FocusEvent。

  • FocusEvent的事件类型:
事件类型摘要
FocusEvent.FOCUS_GAINED控件获得焦点
FocusEvent.FOCUS_LOST控件失去焦点
2) WindowEvent
  • WindowEvent的应用:

指示窗口状态改变的低级别事件。当打开、关闭、激活、停用、图标化或取消图标化 Window 对象时,或者焦点转移到 Window 内或移出 Window 时,由 Window 对象生成此低级别事件。 该事件被传递给每一个使用窗口的 addWindowListener 方法注册以接收这种事件的 WindowListener 或 WindowAdapter 对象。(WindowAdapter 对象实现 WindowListener 接口。)发生事件时,所有此类侦听器对象都将获取此 WindowEvent。

  • WindowEvent焦点相关的事件类型:
事件类型摘要
WindowEvent.WINDOW_ACTIVATED窗口激活
WindowEvent.WINDOW_GAINED_FOCUS窗口获得焦点
WindowEvent.WINDOW_LOST_FOCUS窗口失去焦点
WindowEvent.WINDOW_DEACTIVATED窗口非激活
3) 通过FocusEvent.getOppositeComponent和WindowEvent.getOppositeWindow可以获取焦点跳转过程中的对立控件。
2.5.2 键盘事件的全局处理
  • KeyEventDispatcher:在键盘消息传递到焦点控件之前预先处理键盘消息,需向
    KeyboardFocusManager注册
  • KeyEventPostProcessor:在键盘消息传递到焦点控件并由焦点空间处理之后处理键盘消息,需要向
    KeyboardFocusManager注册。
  • KeyboardFocusManager以事件链的模式处理KeyEventDispatcher和KeyEventPostProcessor,
    并以自身作为事件链的最后一个处理者。
2.5.3 键盘控制焦点的跳转
  • Component提供了两个方法用于控制键盘的焦点跳转。

  • setFocusTraversalKeysEnabled(boolean)是否支持键盘控制焦点跳转

参数值摘要
true(默认)支持键盘控制焦点,普通的KeyListener将接收不到焦点控制键。
flase不支持键盘控制焦点,焦点控制键和其他键一致。

* setFocusTraversalKeys(int, Set)用指定按键控制焦点跳转。
* int 参数指定跳转方向
* KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS跳到上一个焦点控件。
* KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS跳到下一个焦点控件。
* KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS跳到下一个焦点循环体。
* KeyEventPostProcessor.UP_CYCLE_TRAVERSAL_KEYS跳到上一个焦点循环体。
* Set参数是键盘(AWTKeyStroke)事件的集合
* 可以多个键盘事件对应一个焦点跳转操作,但不能一个事件对应多个焦点跳转操作。
* 不能使用KEY_TYPED类型的键盘事件对应焦点跳转操作,可以使用KEY_PRESSED或KEY_RELEASED。

  • Swing控制的默认情况:
    • 切换到下一个组件:
      文本域:CTRL+TAB控制焦点切换
      其他组件:TAB和CTRL+TAB都可以控制焦点切换
    • 切换到上一个组件:
      文本域:CTRL+SHIFT+TAB控制焦点切换
      其他组件:SHIFT+TAB/CTRL+SHIFT+TAB都可以控制焦点切换
2.5.4 焦点跳转策略
  • 焦点跳转策略(FocusTraversalPolicy)定义了焦点跳转的顺序。

  • Swing实现了两个焦点跳转策略

    • SortingFocusTraversalPolicy:通过一个Comparator来决定焦点跳转
    • LayoutFocusTraversalPolicy:继承于SortingFocusTraversalPolicy,通过控件的大小,
      位置和方向来决定焦点的跳转。
  • 默认情况下Swing中的Containers使用LayoutFocusTraversalPolicy

2.5.5 程序控制焦点的跳转
  • KeyboardFocusManager提供的接口:

    • KeyboardFocusManager.focusNextComponent(Component)
    • KeyboardFocusManager.focusPreviousComponent(Component)
    • KeyboardFocusManager.upFocusCycle(Component)
    • KeyboardFocusManager.downFocusCycle(Component)
  • Component提供接口:

    • Component.transferFocus()
    • Component.transferFocusBackward()
    • Component.transferFocusUpCycly()
    • Component.transferFocusDownCycle()
  • Component自身获得焦点:

    • Component.requestFocus():支持跨窗口获取焦点,受系统平台限制
    • Component.requestFocusWindow:不支持跨窗口获取焦点,不受系统平台限制
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值