Swing组件集合的事件处理(六)

摘要:
为了简化事件处理,Swing库使用Action接口来扩展原始ActionListener接口,以使用事件处理程序存储可视属性。然后,当Action稍后与组件关联时,该组件将通过事件处理程序自动获取信息。Swing库还添加了一个KeyStroke类,使我们更容易响应键盘事件。这些将在本章稍后的Swing组件管理中讨论。
2.3 Swing特定的事件处理

请记住,Swing组件是构建在AWT库之上的,Swing组件库具有一些改进的功能从而使得事件处理更为简单。功能改进覆盖AWT核心事件处理特性之上,由基本的动作监听到焦点管理。

为了简化事件处理,Swing库使用Action接口扩展了原始的ActionListener接口来存储具有事件处理器的可视属性。这使得事件处理器的创建独立于可视化组件。然后,当Action在稍后与一个组件相关联时,组件直接由事件处理器自动获取信息(例如按钮标签)。这包括当Action被修改时更新标签的通知。AbstractAction与TextAction类实现了这个概念。

Swing库同时添加了KeyStroke类从而使得我们更容易的响应键盘事件。当一个特定的击键序列被按下时,我们可以通知组件必须响应特定的动作,而无需监听一个特定键的所有按键事件。这些击键到动作的映射存储在InputMap与ActionMap对象的组合中。当组件容器具有信息时,InputMap就会特例化ComponentInputMap。Swing文本组件借助于Keymap接口可以更容易的使用这些来存储击键到动作的映射。第16章更详细的描述了TextAction支持的映射,以及文本事件处理功能的其余部分。

KeyboardFocusManager与DefaultKeyboardFocusManager,借助于FocusTraversalPolicy及其实现的帮助,管理焦点子系统。InputVerifier用于用户输入验证。这些内容都会在本章稍后的Swing组件管理部分进行讨论。

2.3.1 Action接口

Action接口是ActionListener接口的扩展,他可以非常灵活的用于定义与作为触发代理的组件相独立的共享事件处理器。这个接口实现了ActionListener,并且定义了一个查询表数据结构,其键值作为属性。然后,当Action与一个组件相关联时,这些显示属性会自动的传递到Action。下面是接口定义:

public interface Action implements ActionListener {
  // Constants 
  public final static String ACCELERATOR_KEY;
  public final static String ACTION_COMMAND_KEY;
  public final static String DEFAULT;
  public final static String LONG_DESCRIPTION;
  public final static String MNEMONIC_KEY;
  public final static String NAME;
  public final static String SHORT_DESCRIPTION;
  public final static String SMALL_ICON;  // Listeners
  public void addPropertyChangeListener(PropertyChangeListener listener);
  public void removePropertyChangeListener(PropertyChangeListener listener);
  // Properties
  public boolean isEnabled();
  public void setEnabled(boolean newValue);
  // Other methods
  public Object getValue(String key);
  public void putValue(String key, Object value);
}

因为Action仅是一个接口,Swing提供了一个类来实现这个接口,这就是AbstractAction。

2.3.2 AbstractAction类

AbstractAction类提供了Action接口的一个默认实现。这就是属性行为实现的地方。

使用Action

一旦我们通过继承定义一个AbstractAction并且提供一个public void actionPerformed(ActionEvent actionEvent)方法,我们就可以将其传递给一些特殊的Swing组件。JCheckBox,JToggleButton,JMenuItem,JCheckBoxMenuItem以及JRadioButtonMenuItem提供了由动作创建组件的构造函数,而Swing文本组件通过Keymap,InputMap以及ActionMap对Action对象提供了内建支持。

当具有关联Action的组件被添加到相应的Swing容器中时,选中会触发Action的actionPerformed(ActionEvent actionEvent)方法的调用。组件的显示是通过添加到内部数据结构的属性元素来定义的。了为演示的需要,列表2-8提供了一个具有Print标签以及一个图标的Action。当其被激活时,会输出一个Hello, World消息。

import java.awt.event.*;
import javax.swing.*;
public class PrintHelloAction extends AbstractAction {
  private static final Icon printIcon = new ImageIcon("Print.gif");
  PrintHelloAction() {
    super("Print", printIcon);
    putValue(Action.SHORT_DESCRIPTION, "Hello, World");
  }
  public void actionPerformed(ActionEvent actionEvent) {
    System.out.println("Hello, World");
  }
}

一旦定义了Action,我们就可以创建Action并将其与我们所希望的组件相关联。

Action printAction = new PrintHelloAction();
menu.add(new JMenuItem(printAction));
toolbar.add(new JButton(printAction));

在我们将Action与对象相关联之后,如果我们发现我们需要修改Action的属性,我们只需要在一个地方修改其设置 。因为所有的属性都是绑定的,他们会传播到使用Action的任意组件。例如,禁止Action(printAction.setEnabled(false))将会禁止分别在JMenu与JToolBar上所创建的JMenuItem与JButton。相应的,通过printAction.putValue(Action.NAME, "Hello, World")修改Action的名字将会修改相关联组件的文本标签。

图2-6JToolBar与JMenu上的PrintHelloAction的样子。可选中的按钮用来允许或是禁止Action,同时也可以修改其名字。

swing_2_6

此示例的完整代码显示在列表2-9中。不要担心工具栏与菜单栏的创建。我们将会在第6章对其进行详细的讨论。

/**
 * 
 */
package swingstudy.ch02;
 
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JToolBar;
 
/**
 * @author lenovo
 *
 */
public class ActionTester {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
 
		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("Action Sample");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
				final Action printAction = new PrintHelloAction();
 
				JMenuBar menuBar = new JMenuBar();
				JMenu menu = new JMenu("File");
				menuBar.add(menu);
				menu.add(new JMenuItem(printAction));
 
				JToolBar toolBar = new JToolBar();
				toolBar.add(new JButton(printAction));
 
				JButton enableButton = new JButton("Enable");
				ActionListener enableActionListener = new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						printAction.setEnabled(true);
					}
				};
				enableButton.addActionListener(enableActionListener);
 
				JButton disableButton = new JButton("Disable");
				ActionListener disableActionListener = new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						printAction.setEnabled(false);
					}
				};
				disableButton.addActionListener(disableActionListener);
 
				JButton relabelButton = new JButton("Relabel");
				ActionListener relabelActionListener = new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						printAction.putValue(Action.NAME, "Hello, World");
					}
				};
				relabelButton.addActionListener(relabelActionListener);
 
				JPanel buttonPanel = new JPanel();
				buttonPanel.add(enableButton);
				buttonPanel.add(disableButton);
				buttonPanel.add(relabelButton);
 
				frame.setJMenuBar(menuBar);
 
				frame.add(toolBar, BorderLayout.SOUTH);
				frame.add(buttonPanel, BorderLayout.NORTH);
 
				frame.setSize(300, 200);
				frame.setVisible(true);
			}
		};
 
		EventQueue.invokeLater(runner);
	}
 
}

AbstractAction属性

正如表2-2所示,AbstractAction类有三个可用的属性。

属性名
数据类型

访问性

enabled
boolean

读写绑定

keys
Object[]

只读

propertyChangeListeners
PropertyChangeListener[]

只读

其余的绑定属性通过putValue(String key, Object value)放置在查询表中。获取当前的keys属性设置可以使得我们查看可以进行哪些设置,而不需要进行单独请求。表2-3描述了可以用作键值的Action预定义常量集合。我们也可以添加我们自己的常量,从而在以后动作发生时进行查询。

常量

描述

NAME

Action名字,用作按钮标签

SMALL_ICON

Action图标,用作按钮标签

SHORT_DESCRIPTION

Action的简短描述;可以用作提示文本,但是默认情况下并不用

LONG_DESCRIPTION

Action的长描述;可以用作访问功能(查看第22章)

ACCELERATOR

KeyStroke字符串;可以用Action的快捷键

ACTION_COMMAND_KEY

InputMap键;映射到与JComponent相关的ActionMap中的Action

MNEMONIC_KEY

按键代码;可以用作Action的快捷键

DEFAULT

可以用于我们自定义属性的未用常量

一旦一个属性已经存放在查询表中,我们可以通过public Object getValue(String key)进行获取。其作用方式类似于java.util.Hashtable类或是java.util.Map接口,区别在于:如果表中存在一个键值,那么我们尝试存入一个具有null值的key/value对,则查询表会移除这个键值。

2.3.3 KeyStroke类

KeyStroke类以及特定JComponent的inputMap与actionMap属性提供了一个简单的替换可以向组件注册KeyListener对象并监听特定键的按下。KeyStroke使得我们可以定义一个简单的按键集合,例如Shift-Ctrl-P或是F4。然后我们可以通过将其注册到组件来激活按键,并且在组件识别出时通知按键进行动作,从而通知ActionListener。

在我们探讨如何创建按键之前,我们先来了解一下可以激活按键的不同条件,从而添加不同的输入映射。有三个条件可以激活已注册的按键,并JComponent中的四个常量可以提供帮助。第四个用于未定义的状态。表2-4中列出了可用的四个常量。

常量

描述

WHEN_FOCUSED

当实际的组件获得输入焦点时激活按键

WHEN_IN_FOCUSED_WINDOW

当组件所在的窗口获得输入焦点时激活按键

WHEN_ANCESTOR_OF_FOCUSED_COMPONENT

当在组件或是在组件的容器中按下时激活按键

UNDEFINED_CONDITION

用于没有定义条件的情况

构建按键

KeyStroke类是AWTKeyStroke的子类,并且没有公开的构造函数。我们可以通过下面的方法来创建一个按键:

public static KeyStroke getKeyStroke(char keyChar)
public static KeyStroke getKeyStroke(String representation)
public static KeyStroke getKeyStroke(int keyCode, int modifiers)
public static KeyStroke getKeyStroke(int keyCode, int modifiers,
  boolean onKeyRelease)
public static KeyStroke getKeyStrokeForEvent(KeyEvent keyEvent)

列表中的第一个版本,public static KeyStroke getKeyStroke(char keyChar),可以使得我们由一个char变量创建按键,例如Z。

KeyStroke space = KeyStroke.getKeyStroke('Z');

public static KeyStroke getKeyStroke(String representation)版本是最有趣的版本。他可以使得我们通过一个文本字符串来指定按键,例如"control F4"。字符串的标识符集合为shift, control, meta, alt, button1, button2与button3以及可以指定的多标识符。字符串的其余部分来自KeyEvent类的VK_*常量。例如,下面的代三为Ctrl-Alt-7定义了一个按键:

KeyStroke controlAlt7 = KeyStroke.getKeyStroke("control alt 7");

public static KeyStroke getKeyStroke(int keyCode, int modifiers)public static KeyStroke getKeyStroke(int keyCode, int modifiers,boolean onKeyRelease)是两个最为直接的方法。他允许我们直接指定VK_*常量 以及用于标识符的InputEvent掩码(没有标识符时为0)。当没有指定时,onKeyRelease为false。

KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true);
KeyStroke shiftF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.SHIFT_MASK);

列表中的最后一个版本,public static KeyStroke getKeyStrokeForEvent(KeyEvent keyEvent),将特定的KeyEvent直接映射到KeyStroke。当我们希望允许用户使用按键来激活事件时,这个方法就十分有用。我们要求用户为某一事件按下一个键,然后注册KeyEvent,从而下次按键发生时,事件就会被激活。

KeyStroke fromKeyEvent = KeyStroke.getKeyStrokeForEvent(keyEvent);

注册按键

在我们创建了按键之后,我们需要将其注册到组件。当我们向组件注册一个按键时,我们提供一个当按键按下(或是释放)时要调用的Action。注册要提供一个由按键到Action的映射。首先,我们通过getInputMap(condition)方法获取基于焦点激活条件组件的相应的InputMap。如果没有指定条件,则假定为WHEN_FOCUSED。然后我们在InputMap中添加一个由按键到文本字符串的映射:

component.getInputMap().put(keystroke, string)

如果我们知道已存在动作的动作字符串,我们就可以使用这个字符串;否则我们要定义这个字符串。然后我们使用ActionMap将字符串映射到Action:

component.getActionMap.put(string, action)

我们可以通过共享ActionMap实例来在组件之间共享动作。列表2-10的例子中创建了四个按钮,每一个都注册了不同的按键以及不同的焦点激活条件。按钮标签表明了按键激活条件。Action只是简单的输出消息并激活按钮标签。

/**
 * 
 */
package swingstudy.ch02;
 
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
 
/**
 * @author lenovo
 *
 */
public class KeyStrokeSample {
 
	private static final String ACTION_KEY = "theAction";
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("KeyStroke Sample");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
				JButton buttonA = new JButton("<html><center>FOCUSED<br>control alt 7");
				JButton buttonB = new JButton("<html><center>FOCUS/RELEASE<br>VK_ENTER");
				JButton buttonC = new JButton("<html><center>ANCESTOR<br>VK_F4+SHIFT_MASK");
				JButton buttonD = new JButton("<html><center>WINDOW<br>' '");
 
				Action actionListener = new AbstractAction() {
					public void actionPerformed(ActionEvent event) {
						JButton source = (JButton)event.getSource();
						System.out.println("Activated: "+source.getText());
					}
				};
 
				KeyStroke controlAlt7 = KeyStroke.getKeyStroke("control alt 7");
				InputMap inputMap = buttonA.getInputMap();
				inputMap.put(controlAlt7, ACTION_KEY);
				ActionMap actionMap = buttonA.getActionMap();
				actionMap.put(ACTION_KEY, actionListener);
 
				KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true);
				inputMap = buttonB.getInputMap();
				inputMap.put(enter, ACTION_KEY);
				buttonB.setActionMap(actionMap);
 
				KeyStroke shiftF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.SHIFT_MASK);
				inputMap = buttonC.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
				inputMap.put(shiftF4, ACTION_KEY);
				buttonC.setActionMap(actionMap);
 
				KeyStroke space = KeyStroke.getKeyStroke(' ');
				inputMap = buttonD.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
				inputMap.put(space, ACTION_KEY);
				buttonD.setActionMap(actionMap);
 
				frame.setLayout(new GridLayout(2,2));
				frame.add(buttonA);
				frame.add(buttonB);
				frame.add(buttonC);
				frame.add(buttonD);
 
				frame.setSize(400, 200);
				frame.setVisible(true);
			}
		};
 
		EventQueue.invokeLater(runner);
	}
 
}

图2-7显示了程序运行时的样子。

swing_2_7

2.3.4 使用快捷键

Swing库也可以使用KeyStroke对象用于一些内部功能。两个这样的功能为记忆键与快捷键,其工作如下:

  • 在组件记忆键中,标签中的一个字符以下划线出现。当这个字符平台特定的热键组合被按下时,组件就会被激活。例如,在图2-8所示的窗体中按下Alt-A则会在Windows XP平台下选中About按钮。
  • 菜单快捷键可以在菜单条目不可见的情况下激活条目。例如,在图2-8所示的窗体中按下Ctrl-P将会在File菜单不可见的情况下选中Print菜单条目。

swing_2_8

我们将会在第6章了解更多关于记忆键与快捷键的内容。

免责声明:文章转载自《Swing组件集合的事件处理(六)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇iOS崩溃日志ips文件解析Ubuntu16.04中Appium的安装和使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

在C#中利用SharpZipLib进行文件的压缩和解压缩收藏

我在做项目的时候需要将文件进行压缩和解压缩,于是就从http://www.icsharpcode.net(http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx)下载了关于压缩和解压缩的源码,但是下载下来后,面对这么多的代码,一时不知如何下手。只好耐下心来,慢慢的研究,总算找到了门路。...

C#-WebForm-JS知识:基础部分、BOM部分、DOM部分、JS事件

---恢复内容开始--- 一、基础部分: 1、JavaScript 是什么? 是一门脚本语言,是属于弱类型(语言语法很随意),C#是强类型(语言语法非常严格)(李献策lxc) 优点:JS 执行速度快 2、JS 与java有什么关系?  没有任何关系 3、JS 能做什么事情?   能控制浏览器 BOM   控制元素 DOM JS不能做什么?   不能操作文件...

ES6 阮一峰阅读学习

参考: ECMAScript6入门   就是随便看看,了解一下。 一、ECMAScript6简介 1. 什么是ECMAScript6? JavaScript语言的下一代标准。2015年6月发布,正式名称是《ECMAScript2015标准》。 思考:编程语言和英语、法语语言差不多吧。其实就是语言的标准、规范之类,和英语语法差不多的东西吧 = =! 二、le...

WinForm窗体间如何传值的几种方法

(转) 窗体间传递数据,无论是父窗体操作子窗体,还是子窗体操作符窗体,有以下几种方式: 公共静态变量; 使用共有属性; 使用委托与事件; 通过构造函数把主窗体传递到从窗体中; 一、通过静态变量 特点:传值是双向的,实现简单 实现代码如下: 在一个app类中定义一个静态成员value public class app { public static st...

CSerialPort类的源码(程序文件和头文件)

#ifndef __SERIALPORT_H__#define __SERIALPORT_H__ #define WM_COMM_BREAK_DETECTED  WM_USER+1 // A break was detected on input.#define WM_COMM_CTS_DETECTED  WM_USER+2 // The CTS (cle...

Java格式化字符串,左对齐,左补0

今天在做java编程实现邮件发送的时候,遇到邮件发送的内容涉及表格形式的数据,不适合用附件,只能用对齐的形式来展现表格内容,一开始很苦恼,不知道该怎么对齐,最后写了下面这个函数,实现了格式化字符串,左对齐的功能,很简单的函数,却解决了问题。   下面这段代码,可以处理字符串的左对齐输出,可以自定义补充的字符(不仅限于‘ ’),可以自定义补充后字符串的长度。...