Java基础知识:事件处理

  • 大纲:
    • Java事件处理的基本方法
    • Java AWT事件模型的工作机制
    • 如何以更加结构化的方式处理动作事件。
  • 事件处理基础:
    • 事件委托模型: 在AWT所知的事件范围内,完全可以控制事件从 事件源(如:按钮、滚动条)到 事件监听器 的传递过程,并将任何对象 指派 给事件监听器。
    • 像Java这样的面向对象语言,都将事件的相关信息封装在一个 事件对象(Event Object)。在Java中,所有的事件对象都最终派生于 java.util.EventObject类
    • AWT事件处理机制的概要:
      • 监听器对象是一个实现了 特定 监听器接口 的类的实例。
      • 事件源是一个能够 注册 监听器对象 发送 事件对象 的对象。
      • 当事件发生时,事件源将 事件对象 传递给所有注册的监听器
      • 监听器对象 利用 事件对象 中的信息决定如何对事件做出响应
    • 可以将多个监听器对象添加到一个像按钮这样的事件源中。只要用户点击按钮,按钮就会创建一个ActionEvent对象并调用所有监听器的actionPerformed方法(传入按钮创建的ActionEvent对象)
    • ActionListener接口并不仅限于按钮点击事件:
      • 鼠标双击
      • 选择菜单项
      • 文本域中按回车
      • Timer组件的到达指定的时间间隔
    • 事件监听器对象通常需要执行一些对其他对象可能产生影响的操作。可以策略性地将监听器类放置在需要改变状态的哪个类中。(将监听器类作为某个类的内部类
    • 实例程序:(按相应的颜色按钮改变相应的背景颜色,但在按与背景颜色相同的颜色颜色按钮则恢复背景为默认颜色(原功能上稍加改动))
package button;

import java.awt.*;
import javax.swing.*;
public class ButtonTest {
	public static void main( String[] args ) {
		EventQueue.invokeLater(()->{
			ButtonFrame frame = new ButtonFrame();
			frame.setTitle("Button Frame");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
}
package button;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class ButtonFrame extends JFrame{
	private JPanel buttonPanel;
	private Color originalColor;
	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 200;
	
	public ButtonFrame() {
		buttonPanel = new JPanel();
		originalColor = buttonPanel.getBackground();
		setSize( DEFAULT_WIDTH, DEFAULT_HEIGHT );
		
		
		JButton yellowButton = new JButton("Yellow");
		JButton redButton = new JButton("Red");
		JButton blueButton = new JButton("Blue");
		
		add(buttonPanel);
		buttonPanel.add(yellowButton);
		buttonPanel.add(redButton);
		buttonPanel.add(blueButton);
		
		yellowButton.addActionListener( new ColorAction(Color.YELLOW) );
		redButton.addActionListener(new ColorAction(Color.RED));
		blueButton.addActionListener(new ColorAction(Color.BLUE) );
		
	}
	
	private class ColorAction implements ActionListener{
		private Color backgroundColor;
		
		public ColorAction( Color c ) { 
			backgroundColor = c;
		}
		
		public void actionPerformed( ActionEvent event ) {
			if( buttonPanel.getBackground().equals(backgroundColor) ) {
				buttonPanel.setBackground(originalColor);
			}else {
				buttonPanel.setBackground(backgroundColor);
			}
		}
	}
}

 

  • 简洁地指定监听器:
    • 常见的情况是:每个监听器执行 一个 单独 的动作。这种情况下,没必要建立单独的类,只需要使用一个 lambda表达式
    • 较老的代码中,构造监听器可能会用到匿名类。(相对繁琐)
    • 还可以创建实现了 ActionListener接口 的 事件源容器,再设置自身(this)作为监听器。(不建议)
    • EventHandler类创建监听器(借助反射)。EventHandler.create( ActionListener.class, frame, "loadData" )。(效率不高,容易出错)
    • java.awt.event.ActionEvent
      • String getActionCommand() //返回与动作事件关联的命令字符串。
    • java.util.EventObject
      • Object getSource() //返回 发生这个事件的对象 的一个引用
  • 实例:改变观感
    • 在默认情况下,Swing程序使用 Metal观感
    • 可以采用两种方式改变观感:
      • Java安装子目录jre/lib下的文件 swing.properties。可在此文件中,将 swing.defaultaf 设置为所希望的观感类名。
      • 动态改变观感。需要调用 静态的 UIManager.setLookAndFeel方法,并提供所想要的观感类,然后再调用静态方法 SwingUtilities.updateComponentTreeUI 来刷新全部的部件集。
    • javax.swing.UIManager
      • static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels()
      • static setLookAndFeel(String className)
    • javax.swing.UIManager.LookAndFeelInfo
      • String getName()
      • String getClassName()
    • javax.swing.SwingUtilities
      • public static void updateComponentTreeUI​(Component c)
    • 示例程序:(实现通过按按钮切换观感)
package plaf;

import java.awt.*;
import javax.swing.*;
public class PlafFrameTest {
	public static void main( String[] args ) {
		EventQueue.invokeLater(()->{
			PlafFrame frame = new PlafFrame();
			frame.setTitle("PlafFrame");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
}
package plaf;

import java.awt.*;
import javax.swing.*;
public class PlafFrame extends JFrame {
	private JPanel panel;
	
	public PlafFrame() {
		panel = new JPanel();
		
		UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
		for( UIManager.LookAndFeelInfo i : infos ) {
			makeButton( i.getName(), i.getClassName() );
		}
		
		add(panel);
		pack();
	}
	
	private void makeButton( String name, String className ) {
		JButton button = new JButton(name);
		panel.add(button);
		button.addActionListener(event->{
			try {
				UIManager.setLookAndFeel(className);
				SwingUtilities.updateComponentTreeUI(this);
				pack();
			}catch( Exception e ) {
				e.printStackTrace();
			}
		});
	}
}

 

  • 适配器类
    • 当用户试图关闭一个框架窗口时,JFrame对象 就是 WindowEvent 的事件源。如果需要捕获这个事件,就需要一个窗口监听器
    • 窗口监听器必须是实现 WindowListener接口 的类的对象。WindowListener接口中总共7个方法。
    • 每个含有多个方法的AWT监听器接口都配有一个适配器(adapter)类,这个类实现了接口中的所有方法,但每个方法没有做任何事。有了适配器类,我们就可以通过扩展适配器类,只覆盖自己需要的方法,就不必把所有(不需要的)方法实现了。
    • java.awt.event.WindowStateListener
      • void windowStateChanged( WindowEvent event ) //窗口状态改变的时候调用这个方法
    • java.awt.event.WindowEvent
      • int getNewState()
      • int getOldState()
        • Frame.NORMAL
        • Frame.ICONIFIED
        • Frame.MAXIMIZED_HORIZ
        • Frame.MAXIMIZED_VERT
        • Frame.MAXIMIZED_BOTH
    • java.awt.event.WindowListener
      • void windowOpened( WindowEvent event )
      • void windowClosing( WindowEvent event )
      • void windowClosed( WindowEvent event )
      • void windowIconified( WindowEvent event )
      • void windowDeiconified( WindowEvent event )
      • void windowActivated( WindowEvent event )
      • void windowDeactivated( WindowEvent event )
  • 动作:
    • 通常,激活一个命令可以有多种方式。将所有事件连接到同一个监听器上。
    • Swing包提供了一种非常实用的机制来封装命令,并将它们连接到多个事件源,这就是 Action接口
    • 一个 动作 是一个封装下列内容的对象:
      • 命令的说明(一个文本字符串和一个可选图标)
      • 执行命令所需要的参数
    • 实际上,Action接口 扩展于 ActionListener接口。 
    • javax.swing.Action
      • void actionPerformed( ActionEvent event )
      • void setEnable( boolean b )
      • boolean isEnable()
      • void putValue( String key, Object value ) //允许存储和检索动作对象的任意名/值。
      • Object getValue( String key )
        • NAME    动作名称
        • SMALL_ICON  存储小图标的地方(按钮 菜单栏 或 工具栏中)
        • SHORT_DESCRIPTION  图标的简要说明(工具提示)
        • LONG_DESCRIPTION  图标的详细说明(在线帮助)
        • MNEMONIC_KEY  快捷键的缩写(菜单项中)
        • ACCELERATOR_KEY  
        • ACTION_COMMAND_KEY
        • DEFAULT
      • void getPropertyChangeListener( PropertyChangeListener listener )
      • void removePropertyChangeListener( PropertyChangeListener listener )
    • 有一个类实现了 Action接口 除了 actionPerformed方法 之外的所有方法,即 AbstractAction类
    • 将动作对象添加到 击键 中:
      • 生成 KeyStroke类对象,调用静态 getKeyStroke方法。
    • 每个JComponent有三个输入映射(input map)
      • WHEN_FOCUSED
      • WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
      • WHEN_IN_FOCUSED_WINDOW
    • InputMap , ActionMap 。
    • 习惯上,实用字符串none表示空动作。
    • javax.swnig.JComponent
      • ActionMap getActionMap()
      • InputMap getInputMap( int flag )
    • 总结(用同一个动作响应按钮、菜单项或按键的方式):
      • 实现一个扩展于 AbstractAction的类。
      • 构造一个 动作类 的对象
      • 使用动作对象构造按钮或菜单项。构造器可从动作对象中读取标签文本和图标。
      • 按键触发动作:
        • 定位顶层窗口组件
        • 得到顶层组件的输入映射(WHEN_ANCESTOR_OF_FOCUS_COMPONENT)。创建一个KeyStroke对象,并通过 输入映射(InputMap) 将 keyStroke对象 与一个 字符串str 建立映射关系。
        • 通过 ActionMap 将 字符串str(上面所建立的) 与 一个动作对象 建立映射关系。
    • javax.swing.KeyStroke
      • static KeyStroke getKeyStroke( String description ) //以空格分隔,以0或多个修饰符开始(shift control ctrl meta alt altGraph)..."typed + ?" 或 "pressed"(默认) 或 "released"紧跟一个键码结束。...
        <modifiers>* (<typedID> | <pressedReleasedID>)。
    • 示例程序:(按钮 击键 触发面板换色)
package action;

import java.awt.*;
import javax.swing.*;
public class ActionFrameTest {
	public static void main( String[] args ) {
		EventQueue.invokeLater(()->{
			ActionFrame frame = new ActionFrame();
			frame.setTitle("Action Frame");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
}
package action;

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class ActionFrame extends JFrame {
	private JPanel panel;
	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 200;
	
	public ActionFrame() {
		setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
		panel = new JPanel();
		
		//define actions
		Action yellowAction = new ColorAction("Yellow", Color.YELLOW);
		Action blueAction = new ColorAction("Blue", Color.BLUE);
		Action redAction = new ColorAction("Red", Color.RED);
		
		//add buttons for these actions
		panel.add(new JButton(yellowAction));
		panel.add(new JButton(blueAction));
		panel.add(new JButton(redAction));
		
		//add panel to frame
		add(panel);
		
		//associate the Y, B and R keys with names
		InputMap imap = panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
		imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");
		imap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.blue");
		imap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red");
		
		//associate the names with actions
		ActionMap amap = panel.getActionMap();
		amap.put("panel.yellow", yellowAction);
		amap.put("panel.blue", blueAction);
		amap.put("panel.red", redAction);
	}
	
	
	public class ColorAction extends AbstractAction{
		public ColorAction( String name, Color c ) {
			putValue(Action.NAME, name);
			//putValue(Action.SMALL_ICON, icon );
			putValue(Action.SHORT_DESCRIPTION, "set panel color to " + name.toLowerCase() );
			putValue("color",c);
		}
		
		public void actionPerformed( ActionEvent event ) {
			Color c = (Color) getValue("color");
			panel.setBackground(c);
		}
	}
}

 

  • 鼠标事件
    • java.awt.event.MouseEvent
      • int getX()
      • int getY()
      • Point getPoint()
      • int getClickCount()
    • java.awt.event.InputEvent
      • int getModifiersEx() //使用下面的掩码值来检测返回值
        • BUTTON1_DOWN_MASK
        • BUTTON2_DOWN_MASK
        • BUTTON3_DOWN_MASK
        • SHIFT_DOWN_MASK
        • CTRL_DOWN_MASK
        • ALT_DOWN_MASK
        • ALT_GRAPH_DOWN_MASK
        • META_DOWN_MASK
      • static String getModifiersExText( int modifiers )
    • java.awt.Toolkit
      • public Cursor createCustomCursor( Image image, Point hotSpot, String name )
    • java.awt.Component
      • public void setCursor( Cursor cursor )
    • 有两个独立的接口: MouseListener 和 MouseMotionListener 。点击监听器 和 移动监听器, 有利于提供效率。
    • 光标形状样例:(P461 表11-3 10th)。Cursor类的getPredefinedCursor方法。
    • 还可以利用 Toolkit类中的 createCustomCursor方法 来自定义光标类型。
    • 示例程序:(鼠标事件测试:单击添加方块,可拖动,双击删除方块)
package mouse;

import javax.swing.JFrame;

import java.awt.EventQueue;
public class MouseFrame extends JFrame {
	public static void main( String[] args ) {
		EventQueue.invokeLater(()->{
			MouseFrame frame = new MouseFrame();
			frame.setTitle("MouseFrame");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
	
	public MouseFrame() {
		add( new MouseComponent() );
		pack();
	}
}

package mouse;

import javax.swing.JComponent;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
public class MouseComponent extends JComponent {
	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 200;
	
	private static final int SLIDE_WIDTH = 10;
	
	private ArrayList<Rectangle2D> squares;
	private Rectangle2D current;
	
	public MouseComponent() {
		setSize( DEFAULT_WIDTH, DEFAULT_HEIGHT );
		squares = new ArrayList<>();
		current = null;
		
		addMouseListener( new MouseHandler() );
		addMouseMotionListener( new MouseMotionHandler() ); 
	}
	public void paintComponent(Graphics g ) {
		Graphics2D graph = (Graphics2D) g;
		for( Rectangle2D r : squares ) graph.draw(r);
	}
	
	public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
	
	public Rectangle2D find( Point2D p ) {
		for( Rectangle2D rect : squares ) {
			if( rect.contains(p)) return rect;
		}
		return null;
	}
	
	public void add( Point2D p ) {
		if( find(p) == null ) {
			double x = p.getX();
			double y = p.getY();
			current = new Rectangle2D.Double(x-SLIDE_WIDTH/2, y-SLIDE_WIDTH/2, SLIDE_WIDTH, SLIDE_WIDTH);
			squares.add(current);
			repaint();
		}
	} 
	
	public void remove( Rectangle2D rect ) {
		if( rect == null ) return;
		if( rect == current ) current = null;
		squares.remove(rect);
		repaint();
	}
	
	private class MouseHandler extends MouseAdapter{
		public void mousePressed( MouseEvent event ) {
			current = find(event.getPoint());
			if( current == null ) add(event.getPoint());
		}
		
		public void mouseClicked( MouseEvent event ) {
			current = find(event.getPoint());
			if( current != null && event.getClickCount() >= 2 ) remove(current);
		}
	}
	
	private class MouseMotionHandler extends MouseMotionAdapter{
		public void mouseMoved( MouseEvent event ) {
			if( find(event.getPoint()) == null ) setCursor(Cursor.getDefaultCursor());
			else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
		}
		
		public void mouseDragged( MouseEvent event ) {
			if( current != null ) {
				int x = event.getX();
				int y = event.getY();
				current.setFrame(x-SLIDE_WIDTH/2, y-SLIDE_WIDTH/2, SLIDE_WIDTH, SLIDE_WIDTH);
				repaint();
			}
		}
	}
}
  • AWT事件继承层次
    • 所有的事件都是由 java.util包 中的 EventObject类 扩展而来的。
    • EventObject类有一个子类 AWTEvent 。它是所有 AWT事件类 的父亲。
    • 事件对象 封装事件源 监听器 彼此通信的事件信息
    • AWT将事件分为 底层事件 和 语义事件。
      • 语义事件是 表示用户动作的事件,如:按下按钮。
      • 底层事件是 形成那些事件的事件,如:点击按钮时,包含 按下鼠标、连续移动鼠标、抬起鼠标事件。
    • java.awt.event包中最常用的语义事件类:
      • ActionEvent (按钮点击 菜单选择 选择列表项 在文本框中ENTER)
      • AdjustmentEvent (滚动条)
      • ItemEvent (从复选框或列表框中选择一项)
    • 常用的5个底层事件
      • KeyEvent
      • MouseEvent
      • MouseWheelEvent
      • FocusEvent
      • WindowEvent
    • 以上事件都有对应的监听器接口。P467 10th。
    • javax.swing.event包中,包含了许多专门用于Swing组件的附加事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值