第十三周学习总结
第一部分:理论知识
第11章 事件处理(事件处理基础; 动作; 鼠标事件;AWT事件继承层次)
1. 事件源(event source):能够产生事件的对象都可 以成为事件源,如文本框、按钮等。一个事件源是一个 能够注册监听器并向监听器发送事件对象的对象。
2. 事件监听器(event listener):事件监听器对象接 收事件源发送的通告(事件对象),并对发生的事件作 出响应。一个监听器对象就是一个实现了专门监听器接 口的类实例,该类必须实现接口中的方法,这些方法当 事件发生时,被自动执行。
3. 事件对象(event object):Java将事件的相关信息 封装在一个事件对象中,所有的事件对象都最终派生于 java.util.EventObject类。不同的事件源可以产生不 同类别的事件
4.AWT事件处理机制的概要:
(1) 监听器对象:是一个实现了特定监听器接口( listener interface)的类实例。
(2)事件源:是一个能够注册监听器对象并发送事件对 象的对象。
(3)当事件发生时,事件源将事件对象自动传递给所 有注册的监听器。
(4) 监听器对象利用事件对象中的信息决定如何对事 件做出响应。
5.GUI设计中,程序员需要对组件的某种事件进行响应和处理时,必须完成两个步骤:
(1) 定义实现某事件监听器接口的事件监听器类,并具体化接口中声明的事件处理抽象方法。
(2) 为组件注册实现了规定接口的事件监听器对象;
6.注册监听器方法 eventSourceObject.addEventListener(eventListenerObject)
下面是监听器的一个示例: ActionListener listener = …;
JButton button=new JButton(“Ok”); button.addActionListener(listener);
7.动作事件(ActionEvent):当特定组件动作(点 击按钮)发生时,该组件生成此动作事件。
(1) 该 事 件 被 传 递 给 组 件 注 册 的 每 一 个 ActionListener 对象, 并 调 用 监 听 器 对 象 的 actionPerformed方法以接收这类事件对象。
(2)能够触发动作事件的动作,主要包括:
1) 点击按钮
2) 双击一个列表中的选项;
3) 选择菜单项;
4) 在文本框中输入回车。
8. 监听器接口的实现
监听器类必须实现与事件源相对应的接口,即必须提供接口中方法的实现。
⚫ 监听器接口方法实现
class Mylistener implements ActionListener
{
public void actionPerformed (ActionEvent event)
{ …… }
}
9.命令按钮Jbutton主要API
a.创建按钮对象
JButton类常用的一组构造方法:
(1) JButton(String text):创建一个带文本的按钮。
(2) JButton(Icon icon) :创建一个带图标的按钮。
(3)JButton(String text, Icon icon) :创建一个带文本和图标
的按钮。
b.按钮对象的常用方法
① getLabel( ):返回按钮的标签字符串;
② setLabel(String s):设置按钮的标签为字符串s。
10. 用匿名类、lambda表达式简化程序
例ButtonTest.java中,各按钮需要同样的处理:
1) 使用字符串构造按钮对象;
2) 把按钮添加到面板上;
3) 用对应的颜色构造一个动作监听器;
4) 注册动作监听器。
11.适配器类
12.用匿名类简化
13.动作事件
14.鼠标事件
第二部分:实验部分
实验十三 图形界面事件处理技术
实验时间 2018-11-22
1、实验目的与要求
(1) 掌握事件处理的基本原理,理解其用途;
(2) 掌握AWT事件模型的工作机制;
(3) 掌握事件处理的基本编程模型;
(4) 了解GUI界面组件观感设置方法;
(5) 掌握WindowAdapter类、AbstractAction类的用法;
(6) 掌握GUI程序中鼠标事件处理技术。
2、实验内容和步骤
实验1: 导入第11章示例程序,测试程序并进行代码注释。
测试程序1:
l 在elipse IDE中调试运行教材443页-444页程序11-1,结合程序运行结果理解程序;
l 在事件处理相关代码处添加注释;
l 用lambda表达式简化程序;
l 掌握JButton组件的基本API;
l 掌握Java中事件处理的基本编程模型。
package First; import java.awt.*; import javax.swing.*; /** * @version 1.34 2015-06-12 * @author Cay Horstmann */ public class ButtonTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new ButtonFrame();//创建对象;超类量引用子类对象; frame.setTitle("ButtonTest");//程序/软件的名字 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭操作; frame.setVisible(true);//使界面可视化; }); } }
1 package First; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import javax.swing.*; 6 7 /** 8 * A frame with a button panel 9 */ 10 public class ButtonFrame extends JFrame { 11 private JPanel buttonPanel; 12 private static final int DEFAULT_WIDTH = 300*2; 13 private static final int DEFAULT_HEIGHT = 200*2; 14 15 public ButtonFrame() { 16 setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 17 buttonPanel = new JPanel(); 18 makeButton("黄色", Color.yellow); 19 makeButton("蓝色", Color.blue); 20 makeButton("红色", Color.red); 21 makeButton("绿色",Color.green); 22 //添加面板到框架里 23 add(buttonPanel); 24 25 } 26 27 protected void makeButton(String name,Color backgound) { 28 // 创建按钮 29 JButton button = new JButton(name); 30 // //把按钮添加到面板上 31 buttonPanel.add(button); 32 // create button actions 33 //方法一:通过内部类方式实现 34 /* 35 //构造一个对象,并将对象设置为按钮监听器 36 ColorAction action = new ColorAction(backgound); 37 // 为按钮添加监听器 38 button.addActionListener(action);*/ 39 //方法二:匿名内部类方式实现 40 /*button.addActionListener(new ActionListener() { 41 42 @Override 43 public void actionPerformed(ActionEvent e) { 44 // TODO 自动生成的方法存根 45 buttonPanel.setBackground(backgound); 46 } 47 });*/ 48 //方法三通过lambad表达式实现 49 button.addActionListener((e)->{ 50 buttonPanel.setBackground(backgound); 51 }); 52 53 } 54 55 /** 56 * An action listener that sets the panel's background color. 57 */ 58 //这是实现了 ActionListener接口的内部类 59 /*private class ColorAction implements ActionListener { 60 private Color backgroundColor; 61 62 public ColorAction(Color c) { 63 backgroundColor = c; 64 } 65 //实现ActionListener接口,监听器类必须有一个actionPerformed方法 66 public void actionPerformed(ActionEvent event) { 67 buttonPanel.setBackground(backgroundColor); 68 } 69 }*/ 70 }
测试程序2:
l 在elipse IDE中调试运行教材449页程序11-2,结合程序运行结果理解程序;
l 在组件观感设置代码处添加注释;
l 了解GUI程序中观感的设置方法。
1 package Second; 2 3 import java.awt.*; 4 import javax.swing.*; 5 6 /** 7 * @version 1.32 2015-06-12 8 * @author Cay Horstmann 9 */ 10 public class PlafTest 11 { 12 public static void main(String[] args) 13 { 14 EventQueue.invokeLater(() -> { 15 JFrame frame = new PlafFrame(); 16 frame.setTitle("PlafTest"); 17 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 18 frame.setVisible(true); 19 }); 20 } 21 }
1 package Second; 2 3 import javax.swing.JButton; 4 import javax.swing.JFrame; 5 import javax.swing.JPanel; 6 import javax.swing.SwingUtilities; 7 import javax.swing.UIManager; 8 9 /** 10 * A frame with a button panel for changing look-and-feel 11 */ 12 public class PlafFrame extends JFrame 13 { 14 private JPanel buttonPanel; 15 16 public PlafFrame() 17 { 18 buttonPanel = new JPanel(); 19 //获取所有安装的观感实现 20 21 UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); 22 for (UIManager.LookAndFeelInfo info : infos) 23 makeButton(info.getName(), info.getClassName()); 24 25 add(buttonPanel); 26 pack(); 27 } 28 29 /** 30 * Makes a button to change the pluggable look-and-feel. 31 * @param name the button name 32 * @param className the name of the look-and-feel class 33 */ 34 private void makeButton(String name, String className) 35 { 36 // 向面板添加按钮; 37 38 JButton button = new JButton(name); 39 buttonPanel.add(button); 40 41 // 设置按钮动作; 42 43 button.addActionListener(event -> { 44 // 按钮动作:切换到新的外观和感觉 45 try 46 { 47 UIManager.setLookAndFeel(className); 48 //调用静态方法,刷新全部的组件集。这里需要向方法提供一个组件,并由此找到其他的所有组件 49 //外部对象的this引用必须将外部类名作为前缀 50 51 SwingUtilities.updateComponentTreeUI(this); 52 pack(); 53 } 54 catch (Exception e) 55 { 56 e.printStackTrace(); 57 } 58 }); 59 } 60 }
总结:默认情况下,Swing程序使用Metal观感,可以采用两种方式改变观感。
- (没有成功)在Java安装的子目录jre/lib下有一个文件swing.properties.在这个文件中,将swing.defaultlaf设置为所希望的观感类名,如
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel
。注意,Metal观感位于javax.swing包中。其他的观感包位于com.sun.java包中,并且不是在每个Java实现中都提供。 - 动态地改变观感。这需要调用静态的UIManager.setLookAndFeel方法,并提供所想要的观感类名,然后再调用静态方法SwingUtilities.updateComponentTreeUI刷新全部的组件集。
测试程序3:
l 在elipse IDE中调试运行教材457页-458页程序11-3,结合程序运行结果理解程序;
l 掌握AbstractAction类及其动作对象;
l 掌握GUI程序中按钮、键盘动作映射到动作对象的方法。
1 package Third; 2 3 import java.awt.*; 4 import javax.swing.*; 5 6 /** 7 * @version 1.34 2015-06-12 8 * @author Cay Horstmann 9 */ 10 public class ActionTest 11 { 12 public static void main(String[] args) 13 { 14 EventQueue.invokeLater(() -> { 15 JFrame frame = new ActionFrame(); 16 frame.setTitle("ActionTest"); 17 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 18 frame.setVisible(true); 19 }); 20 } 21 }
1 package Third; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import javax.swing.*; 6 7 /** 8 * A frame with a panel that demonstrates color change actions. 9 */ 10 public class ActionFrame extends JFrame 11 { 12 private JPanel buttonPanel; 13 private static final int DEFAULT_WIDTH = 300; 14 private static final int DEFAULT_HEIGHT = 200; 15 16 public ActionFrame() 17 { 18 setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 19 20 buttonPanel = new JPanel(); 21 22 // define actions 23 Action yellowAction = new ColorAction("Yellow", new ImageIcon("yellow-ball.gif"), 24 Color.YELLOW); 25 Action blueAction = new ColorAction("Blue", new ImageIcon("blue-ball.gif"), Color.BLUE); 26 Action redAction = new ColorAction("Red", new ImageIcon("red-ball.gif"), Color.RED); 27 28 //用Action对象构造按钮,把动作和按钮关联起来 29 30 buttonPanel.add(new JButton(yellowAction)); 31 buttonPanel.add(new JButton(blueAction)); 32 buttonPanel.add(new JButton(redAction)); 33 34 // add panel to frame 35 add(buttonPanel); 36 37 //得到顶层组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT输入映射 38 39 // associate the Y, B, and R keys with names 40 InputMap imap = buttonPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 41 //将(按键,动作键)添加到输入映射中 42 imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow"); 43 imap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.blue"); 44 imap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red"); 45 46 // associate the names with actions、 47 //得到顶层组件的动作映射 48 ActionMap amap = buttonPanel.getActionMap(); 49 //将(动作键,动作对象)添加到映射中 50 51 amap.put("panel.yellow", yellowAction); 52 amap.put("panel.blue", blueAction); 53 amap.put("panel.red", redAction); 54 } 55 56 public class ColorAction extends AbstractAction 57 { 58 /** 59 * Constructs a color action. 60 * @param name the name to show on the button 61 * @param icon the icon to display on the button 62 * @param c the background color 63 */ 64 public ColorAction(String name, Icon icon, Color c) 65 { 66 //存储命令的名称、图标、简要说明和需要的颜色 67 putValue(Action.NAME, name); 68 putValue(Action.SMALL_ICON, icon); 69 putValue(Action.SHORT_DESCRIPTION, "Set panel color to " + name.toLowerCase());//显示在工具提示里 70 71 putValue("color", c); 72 } 73 74 public void actionPerformed(ActionEvent event) 75 { 76 Color c = (Color) getValue("color"); 77 buttonPanel.setBackground(c); 78 } 79 } 80 }
总结:用同一个动作相应按钮、菜单项或按键
- 实现一个扩展于AbstractAction类的类。多个相关的动作可以使用同一个类。
- 构造一个动作类的对象。
- 使用动作对象创建按钮或菜单项。构造器将从动作对象中读取标签文本和图标。
- 为了能够通过按键触发动作,必须额外地执行几步操作。首先定位顶层窗口组件,如包含所有其他组件的面板。
- 然后得到顶层组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT输入映射。为需要的按键创建一个KeyStroke对象。创建一个描述动作字符串这样的动作键对象。将(按键,动作键)对添加到输入映射中。
- 最后,得到顶层组件的动作映射。将(动作键,动作对象)添加到映射中。
测试程序4:
l 在elipse IDE中调试运行教材462页程序11-4、11-5,结合程序运行结果理解程序;
l 掌握GUI程序中鼠标事件处理技术。
1 package Forth; 2 3 import java.awt.*; 4 import javax.swing.*; 5 6 /** 7 * @version 1.34 2015-06-12 8 * @author Cay Horstmann 9 */ 10 public class MouseTest 11 { 12 public static void main(String[] args) 13 { 14 EventQueue.invokeLater(() -> { 15 JFrame frame = new MouseFrame(); 16 frame.setTitle("MouseTest"); 17 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 18 frame.setVisible(true); 19 }); 20 } 21 }
1 package Forth; 2 3 import javax.swing.*; 4 5 /** 6 * A frame containing a panel for testing mouse operations 7 */ 8 public class MouseFrame extends JFrame 9 { 10 public MouseFrame() 11 { 12 add(new MouseComponent()); 13 pack(); 14 } 15 }
1 package Forth; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import java.awt.geom.*; 6 import java.util.*; 7 import javax.swing.*; 8 9 /** 10 * A component with mouse operations for adding and removing squares. 11 */ 12 public class MouseComponent extends JComponent 13 { 14 private static final int DEFAULT_WIDTH = 300; 15 private static final int DEFAULT_HEIGHT = 200; 16 17 private static final int SIDELENGTH = 10; 18 private ArrayListsquares; 19 private Rectangle2D current; // the square containing the mouse cursor 20 21 public MouseComponent() 22 { 23 squares = new ArrayList<>(); 24 current = null; 25 26 addMouseListener(new MouseHandler()); 27 addMouseMotionListener(new MouseMotionHandler()); 28 } 29 30 public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } 31 32 public void paintComponent(Graphics g) 33 { 34 Graphics2D g2 = (Graphics2D) g; 35 36 // draw all squares 37 for (Rectangle2D r : squares) 38 g2.draw(r); 39 } 40 41 /** 42 * Finds the first square containing a point. 43 * @param p a point 44 * @return the first square that contains p 45 */ 46 public Rectangle2D find(Point2D p) 47 { 48 for (Rectangle2D r : squares) 49 { 50 if (r.contains(p)) return r; 51 } 52 return null; 53 } 54 55 /** 56 * Adds a square to the collection. 57 * @param p the center of the square 58 */ 59 public void add(Point2D p) 60 { 61 double x = p.getX(); 62 double y = p.getY(); 63 64 current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, 65 SIDELENGTH); 66 squares.add(current); 67 repaint(); 68 } 69 70 /** 71 * Removes a square from the collection. 72 * @param s the square to remove 73 */ 74 public void remove(Rectangle2D s) 75 { 76 if (s == null) return; 77 if (s == current) current = null; 78 squares.remove(s); 79 repaint(); 80 } 81 82 //两个独立的接口MouseListener和MouseMotionListener,有利于提高效率。 83 //当用户移动鼠标时,只关心鼠标点击的监听器就不会被多余的鼠标移动所困扰。 84 private class MouseHandler extends MouseAdapter 85 { 86 public void mousePressed(MouseEvent event) 87 { 88 // add a new square if the cursor isn't inside a square 89 //getPoint方法返回事件源组件左上角的x y坐标 90 //判断该处是否已经绘制图形 91 current = find(event.getPoint()); 92 if (current == null) add(event.getPoint()); 93 } 94 95 public void mouseClicked(MouseEvent event) 96 { 97 // remove the current square if double clicked 98 current = find(event.getPoint()); 99 //双击鼠标,擦除方块 100 if (current != null && event.getClickCount() >= 2) remove(current); 101 } 102 } 103 104 private class MouseMotionHandler implements MouseMotionListener 105 { 106 //移动鼠标的同时按下鼠标,调用mouseMoved 107 public void mouseMoved(MouseEvent event) 108 { 109 // set the mouse cursor to cross hairs if it is inside 110 // a rectangle 111 //光标在一个小方块之上时变成另外一种形状(十字) 112 if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor()); 113 else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); 114 } 115 116 //更新光标位置 117 public void mouseDragged(MouseEvent event) 118 { 119 if (current != null) 120 { 121 int x = event.getX(); 122 int y = event.getY(); 123 //设置形状坐标和大小 124 // drag the current rectangle to center it at (x, y) 125 current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH); 126 repaint(); 127 } 128 } 129 } 130 }
如果只希望用户能够点击按钮或菜单,就不需要显式地处理鼠标事件。鼠标操作将由用户界面中的各种组件内部处理。然而,如果希望用户使用鼠标画图,就需要捕获鼠标移动点击和拖动事件。
当用户点击鼠标按钮时,将会调用3个监听器方法:鼠标第一次被按下时调用mousePressed;鼠标被释放时调用mouseReleased;最后调用mouseClicked。如果只对最终的点击事件感兴趣,就可以忽略前两个方法。用MouseEvent类对象作为参数,调用getX和getY方法可以获得鼠标被按下时鼠标指针所在的x和y坐标。要想区分单击、双击和三击,需要使用getClickCount方法。
实验2:结对编程练习
利用班级名单文件、文本框和按钮组件,设计一个有如下界面(图1)的点名器,要求用户点击开始按钮后在文本输入框随机显示2017级网络与信息安全班同学姓名,如图2所示,点击停止按钮后,文本输入框不再变换同学姓名,此同学则是被点到的同学姓名。
图1 点名器启动界面
图2 点名器点名界面
1 package practise1; 2 3 import java.awt.*; 4 import javax.swing.*; 5 6 7 8 9 public class RandomName { 10 11 public static void main(String[] args) { 12 // TODO Auto-generated method stub 13 14 { 15 EventQueue.invokeLater(() -> { 16 JFrame A= new ButtonFrame(); 17 A.setTitle("点名器"); 18 A.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 19 A.setVisible(true); 20 }); 21 } 22 } 23 24 }
1 package practise1; 2 3 import java.awt.Color; 4 import java.awt.Container; 5 import java.awt.event.*; 6 import java.io.*; 7 import java.util.*; 8 import java.util.Timer; 9 10 import javax.swing.*; 11 12 public class ButtonFrame extends JFrame { 13 private ArrayList arrayList; 14 15 16 17 { 18 arrayList = new ArrayList<>(); 19 //读文件 20 File file = new File("H:/List.txt"); 21 FileInputStream fis; 22 try { 23 fis = new FileInputStream(file); 24 InputStreamReader in = new InputStreamReader(fis); 25 BufferedReader buf = new BufferedReader(in); 26 String readLine; 27 while ((readLine = buf.readLine())!=null) { 28 arrayList.add(readLine); 29 30 } 31 } catch (FileNotFoundException e1) { 32 // TODO Auto-generated catch block 33 e1.printStackTrace(); 34 } catch (IOException e1) { 35 // TODO Auto-generated catch block 36 e1.printStackTrace(); 37 } 38 } 39 private JPanel buttonPanel; 40 private static final int DEFAULT_WIDTH = 500; 41 private static final int DEFAULT_HEIGHT = 300; 42 43 44 45 public ButtonFrame() { 46 setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 47 buttonPanel = new JPanel(); 48 buttonPanel.setLayout(null); 49 JLabel jLabel = new JLabel("随机点名器"); 50 JButton jButton = new JButton("开始"); 51 jLabel.setBounds(200, 40, 65, 40); 52 jButton.setBounds(200, 90, 65, 40); 53 54 55 56 jButton.addActionListener(new ActionListener() { 57 58 Timer timer; 59 60 61 62 public void actionPerformed(ActionEvent e) { 63 64 if (jButton.getText().equals("开始")) { 65 66 timer = new Timer();; 67 68 TimerTask timerTask = new TimerTask() { 69 70 public void run() { 71 72 jButton.setText("停止"); 73 74 jButton.setBackground(Color.red); 75 76 jLabel.setText((String) arrayList.get((int) (Math.random() * 43))); 77 78 } 79 80 81 82 }; 83 84 timer.schedule(timerTask, 0, 10); 85 86 } 87 88 if (jButton.getText().equals("停止")) { 89 90 timer.cancel(); 91 92 jButton.setText("开始"); 93 94 jButton.setBackground(Color.gray); 95 96 } 97 98 } 99 100 }); 101 102 103 104 105 buttonPanel.add(jLabel); 106 buttonPanel.add(jButton); 107 add(buttonPanel); 108 109 } 110 111 112 }
第三部分:总结
1.本周继续深入学习学习GUI相关部分,事件处理。
2.测试程序一中三种方法的不断优化了程序,使其简练效率,以后的学习中要尝试用不同的解决方法;
3.构造按钮->将按钮添加到面板上->构造监听器->添加动作监听器。