事件处理(Java核心技术卷Ⅰ)

来源:互联网 发布:淘宝开店需要哪些软件 编辑:程序博客网 时间:2024/06/08 16:09

    • 事件处理
      • 1 事件处理基础
      • 2 动作
      • 3 鼠标事件
      • 4 AWT事件继承层次

7. 事件处理

7.1. 事件处理基础

  • 任何支持GUI的操作环境都要不断地监视按键或者点击鼠标这样的事件。操作环境将这些事件报告给正在运行的应用程序。如果有事件产生,每个应用程序将决定如何对它们做出响应。程序员对相关的特定事件编写代码,并将这些代码放置在过程中,通过人们将它们成为事件过程(event process)。
  • Java AWT所知的事件范围内,完全可以控制事件从事件源(event source)例如,按钮或滚动条,到事件监听器(event listener)的传递过程,并将任何对象指派给事件监听器。
  • 像Java这样的面向对象语言,都将事件的相关信息封装在一个事件对象(event object)中。在Java中,所有的事件对象都最终派生于java.util.EventObject类。
  • 不同的事件源可以产生不同类别的事件。例如,按钮可以发送一个AcitonEvent对象,而窗口可以发送WindowEvent对象。

AWT事件处理机制概要:

  • 监听器对象是一个实现了特定监听器接口(listener interface)的类的实例。
  • 事件源是一个能够注册监听器对象并发送事件对象的对象。
  • 当事件发生时,事件源将事件对象传递给所有注册的监听器。
  • 监听器对象将利用事件对象中的信息决定如何对事件作出响应。
/* 事件委托模型(event delegation model)实例 *///构造按钮->将按钮添加到面板上->构造监听器->添加动作监听器public class ButtonFrame extends JFrame{    private JPanel buttonPanel;    private static final int DEFAULT_WIDTH=300;    private  static final int DEFAULT_HEIGHT=200;    public ButtonFrame(){        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);        JButton yellowButton=new JButton("yellow");        JButton redButton=new JButton("red");        JButton blueButton=new JButton("blue");        buttonPanel=new JPanel();        //把按钮添加到面板上        buttonPanel.add(blueButton);        buttonPanel.add(redButton);        buttonPanel.add(yellowButton);        //添加面板到框架里        add(buttonPanel);        //构造一个对象,并将对象设置为按钮监听器        ColorAction yellowAction=new ColorAction(Color.YELLOW);        ColorAction blueAction=new ColorAction(Color.blue);        ColorAction redAction=new ColorAction(Color.red);        //为按钮添加监听器        yellowButton.addActionListener(yellowAction);        blueButton.addActionListener(blueAction);        redButton.addActionListener(redAction);    }    //内部类。才能访问buttonPanel变量    private class ColorAction implements ActionListener{        private Color backgroundColor;        public ColorAction(Color c){            backgroundColor=c;        }        //实现ActionListener接口,监听器类必须有一个actionPerformed方法        public void actionPerformed(ActionEvent event){            buttonPanel.setBackground(backgroundColor);        }    }}

创建一个包含方法调用的监听器
假设有一个标签为load的按钮,它的事件处理只包含下面一个方法调用:frame.loadData();
可以使用匿名内部类

loadButton.addActionListener(new ActionListener(){    public void actionPerformed(ActionEvent event){        frame.loadData();    }});

改变观感
默认情况下,Swing程序使用Metal观感,可以采用两种方式改变观感。

  1. (没有成功)在Java安装的子目录jre/lib下有一个文件swing.properties.在这个文件中,将swing.defaultlaf设置为所希望的观感类名,如swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel 。注意,Metal观感位于javax.swing包中。其他的观感包位于com.sun.java包中,并且不是在每个Java实现中都提供。
  2. 动态地改变观感。这需要调用静态的UIManager.setLookAndFeel方法,并提供所想要的观感类名,然后再调用静态方法SwingUtilities.updateComponentTreeUI刷新全部的组件集。
/* 改变观感 */public class PlafFrame extends JFrame{    private JPanel buttonPanel;    public PlafFrame(){        buttonPanel=new JPanel();        //获取所有安装的观感实现        UIManager.LookAndFeelInfo[] infos=UIManager.getInstalledLookAndFeels();        for(UIManager.LookAndFeelInfo info: infos){            makeButton(info.getName(), info.getClassName());        }        add(buttonPanel);        pack();    }    void makeButton(String name,final String plafName){        JButton button=new JButton(name);        buttonPanel.add(button);        button.addActionListener(new ActionListener() {            public void actionPerformed(ActionEvent arg0) {                try {                    //调用静态方法,设置观感                    UIManager.setLookAndFeel(plafName);                    //调用静态方法,刷新全部的组件集。这里需要向方法提供一个组件,并由此找到其他的所有组件                    //外部对象的this引用必须将外部类名作为前缀                    SwingUtilities.updateComponentTreeUI(PlafFrame.this);                    pack();                } catch (ClassNotFoundException | InstantiationException                        | IllegalAccessException                        | UnsupportedLookAndFeelException e) {                    e.printStackTrace();                }            }        });    }}

适配器类

  • 当用户试图关闭一个框架窗口时,JFrame对象就是WindowEvent的事件源。如果希望捕获这个事件,就必须有一个合适的监听器对象,将它添加到框架的窗口监听器列表中。WindowListener listener=...; frame.addWindowListener(listener);
  • 窗口监听器必须是实现WindowListener接口的类的一个对象。在WindowListener接口中包含7个方法。当发生窗口事件时,框架将调用这些方法响应7个不同的事件。
  • 在Java中,实现一个接口的任何类都必须实现其中所有的方法。在这里,意味着需要实现7个方法,然而只对名为windowClosing的方法感兴趣。可以在windowClosing方法增加对System.exit(0)的调用,其他6个方法不做任何事情。
  • 书写6个没有任何操作的方法代码显然是一种乏味的工作。基于简化的目的,每个含有多个方法的AWT监听器接口都配有一个适配器(adapter)类,这个类实现了接口中所有的方法,但每个方法没有做任何事情。这意味着适配器类自动地满足了Java实现相关监听器接口的技术需求。可以通过扩展适配器类来制定对某些事件的相应动作,而不必实现接口中的每个方法(actionListener接口只有一个方法,因此没必要提供适配器类)。
/* 适配器使用实例 *///最清晰简练的方法frame.addWindowListener(new WindowAdapter(){    public void windowClosing(WindowEvent e){        ...    }});

7.2. 动作

Action接口
通常,激活一个命令可以有多种方式。Swing包提供了一种非常实用的机制来封装命令,并将它们连接到多个事件源,这就是Action接口。一个动作是一个封装下列内容的对象:命令的说明(一个文本字符串和一个可选图标) 和 执行命令所需要的参数。

Action接口的方法 功能 actionPerformed(ActionEvent e) 设置响应动作(Action接口扩展于ActionListener接口) setEnabled(boolean b) 设置启用或禁用这个动作 isEnabled() 检查动作当前是否被启用 putValue(String key,Object value) 存储动作对象中的任意名/值 getValue(String key) 检索动作对象中的任意名/值 addPropertyChangeListener(PropertyChangeListener listener) 添加对象属性变化的监听器 removePropertyChangeListener(PropertyChangeListener listener) 删除对象属性变化的监听器

Action是一个接口,而不是一个类。实现这个接口的所有类都必须实现7个方法。庆幸的是,有一个类实现了除actionPerformed方法之外的所有方法,它就是AbstractAction。这个类存储了所有名/值对,并管理着属性变更监听器。可以直接扩展AbstractAction类,并在扩展类中实现actionPerformed方法。

把动作和击键关联起来

  • 要将动作对象添加到击键中,以便让用户敲击键盘命令来执行这项动作。为了将动作与击键关联起来,首先需要生成KeyStroke类对象。这个类封装了对键的说明。要想生成一个KeyStroke对象,不要调用构造器,而是调用KeyStroke类中的静态getKeyStroke方法:KeyStroke ctrlKey=KeyStroke.getKeyStroke("ctrl B");
  • 用户界面中包含许多按钮、菜单、滚动栏以及其他的组件。当用户敲击键盘时,这个动作会被发送给拥有焦点的组件。用户可以使用TAB键在组件之间移动焦点。当按下SPACE键时,就点击了拥有焦点的按钮。示例中不希望将击键发送给拥有焦点的组件。否则,每个按钮都需要知道如何处理CTRL+Y、CTRL+B、CTRL+R这些组合键。这是一个常见的问题,Swing设计者给出了一种很便捷的解决方案。每个JComponent有三个输入映射(input maps),每一个映射的KeyStroke对象都与动作关联。三个输入映射对应三个不同的条件。WHEN_FOCUSED:当这个组件拥有键盘焦点时激活。 WHEN_ANCESTOP_OF_FOCUSED_COMPONENT:当这个组件包含了拥有键盘焦点的组件时激活。WHEN_IN_FOCUSED_WINDOW:当这个组件被包含在一个拥有键盘焦点组件的窗口中时。可以使用getInputMap方法从组件中得到输入映射。InputMap imap=panel.getInputMap(JComponent.WHEN_FOCUSED);
  • InputMap不能直接地将KeyStroke对象映射到Action对象。而是先映射到任意对象上,然后由ActionMap类实现将对象映射到动作上的第2个映射。这样很容易实现来自不同输入映射的按键共享一个动作的目的。
  • 习惯上,使用字符串none表示空动作。这样可以轻松地取消一个按键动作。imap.put(KeyStroke.getKeyStroke("ctrl C"),"none");

总结:用同一个动作相应按钮、菜单项或按键

  1. 实现一个扩展于AbstractAction类的类。多个相关的动作可以使用同一个类。
  2. 构造一个动作类的对象。
  3. 使用动作对象创建按钮或菜单项。构造器将从动作对象中读取标签文本和图标。
  4. 为了能够通过按键触发动作,必须额外地执行几步操作。首先定位顶层窗口组件,如包含所有其他组件的面板。
  5. 然后得到顶层组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT输入映射。为需要的按键创建一个KeyStroke对象。创建一个描述动作字符串这样的动作键对象。将(按键,动作键)对添加到输入映射中。
  6. 最后,得到顶层组件的动作映射。将(动作键,动作对象)添加到映射中。
public class ActionFrame extends JFrame{    private JPanel buttonPanel;    private static final int DEFAULT_WIDTH=300;    private static final int DEFAULT_HEIGHT=200;    public ActionFrame(){        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);        buttonPanel=new JPanel();        Action yellowAction=new ColorAction("Yellow", new ImageIcon("yellow.gif"), Color.YELLOW);        Action blueAction=new ColorAction("Blue", new ImageIcon("blue.gif"), Color.BLUE);        Action redAction=new ColorAction("Red", new ImageIcon("red.gif"), Color.RED);        //用Action对象构造按钮,把动作和按钮关联起来        buttonPanel.add(new JButton(yellowAction));        buttonPanel.add(new JButton(blueAction));        buttonPanel.add(new JButton(redAction));        add(buttonPanel);        //得到顶层组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT输入映射        InputMap imap=buttonPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);        //将(按键,动作键)添加到输入映射中        imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");        imap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.bule");        imap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red");        //得到顶层组件的动作映射        ActionMap amap=buttonPanel.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,Icon icon,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 arg0) {            Color c=(Color) getValue("color");            buttonPanel.setBackground(c);        }    }}

7.3. 鼠标事件

  • 如果只希望用户能够点击按钮或菜单,就不需要显式地处理鼠标事件。鼠标操作将由用户界面中的各种组件内部处理。然而,如果希望用户使用鼠标画图,就需要捕获鼠标移动点击和拖动事件。
  • 本部分设计一个简单的图形编辑器应用程序,它允许用户在画布上放置、移动和擦除方块。
  • 当用户点击鼠标按钮时,将会调用3个监听器方法:鼠标第一次被按下时调用mousePressed;鼠标被释放时调用mouseReleased;最后调用mouseClicked。如果只对最终的点击事件感兴趣,就可以忽略前两个方法。用MouseEvent类对象作为参数,调用getX和getY方法可以获得鼠标被按下时鼠标指针所在的x和y坐标。要想区分单击、双击和三击,需要使用getClickCount方法。
public class MouseFrame extends JFrame{    public MouseFrame(){        add(new MouseComponent());        pack();    }}class MouseComponent extends JComponent{    private static final int SIDELENGTH=10;    private ArrayList<Rectangle2D> squares;    private Rectangle2D current;    public MouseComponent(){        squares=new ArrayList<>();        current=null;        addMouseListener(new MouseHandler());        addMouseMotionListener(new MouseMotionHandler());    }    public void paintComponent(Graphics g){        Graphics2D g2=(Graphics2D)g;        for(Rectangle2D r:squares){            g2.draw(r);        }    }    public Rectangle2D find(Point2D p){        for(Rectangle2D r:squares){            if (r.contains(p)) return r;        }        return null;    }    public void add(Point2D p){        double x=p.getX();        double y=p.getY();        current=new Rectangle2D.Double(x-SIDELENGTH/2, y-SIDELENGTH/2, SIDELENGTH, SIDELENGTH);        squares.add(current);        repaint();    }    public void remove(Rectangle2D r){        if(r==null) return;        if(r==current) current=null;        squares.remove(r);        repaint();    }    //两个独立的接口MouseListener和MouseMotionListener,有利于提高效率。    //当用户移动鼠标时,只关心鼠标点击的监听器就不会被多余的鼠标移动所困扰。    private class MouseHandler extends MouseAdapter{        public void mousePressed(MouseEvent e){            //getPoint方法返回事件源组件左上角的x y坐标            //判断该处是否已经绘制图形            current=find(e.getPoint());            if (current==null)  add(e.getPoint());        }        public void mouseClicked(MouseEvent e){            current=find(e.getPoint());            //双击鼠标,擦除方块            if(current!=null && e.getClickCount()>=2) remove(current);        }    }    private class MouseMotionHandler implements MouseMotionListener{        //移动鼠标的同时按下鼠标,调用mouseMoved        public void mouseMoved(MouseEvent e) {            //光标在一个小方块之上时变成另外一种形状(十字)            if(find(e.getPoint())==null) setCursor(Cursor.getDefaultCursor());//普通鼠标图标            else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));//十字形图标        }        //更新光标位置        public void mouseDragged(MouseEvent e) {            if (current!=null) {                int x=e.getX();                int y=e.getY();                //设置形状坐标和大小                current.setFrame(x-SIDELENGTH/2, y-SIDELENGTH/2, SIDELENGTH, SIDELENGTH);                repaint();            }        }    }}

7.4. AWT事件继承层次

Java事件处理采用的是面向对象方法,所有的时间都是由java.util包中EventObject类扩展而来。EventObject类有一个子类AWTEvent,它是所有AWT事件类的父类。

语义事件和低级事件
AWT将事件分成低级(low-level)时间和语义(semantic)事件。语义事件是表示用户动作的事件,如点击按钮,因此,ActionEvent是一种语义事件。低级事件是形成那些事件的事件。在点击按钮时,包含了按下鼠标、连续移动鼠标、抬起鼠标事件。调节滚动条是一种语义事件,但拖动鼠标是低级事件。
下面是java.awt.event包中最常用的语义事件类:

  • ActionEvent(对应按钮点击、菜单选择、选择列表项或在文本框中ENTER)
  • AdjustmentEvent(调节滚动条)
  • ItemEvent(从复选框或列表框中选择一项)

常用的低级事件类是:

  • KeyEvent(一个键被按下或释放)
  • MouseEvent(鼠标键被按下、释放、移动或拖动)
  • MouseWheelEvent(鼠标滚轮被转动)
  • FocusEvent(某个组件获得焦点或失去焦点)
  • WindowEvent(窗口状态被改变)

AWT监听器接口、事件和事件源:

接口 方法 参数/访问方法 事件源 ActionListener actionPerformed ActionEvent
-getActionCommand
-getModifiers AbstractButton
JComboBox
JTextField
Timer AdjustmentListener adjustmentValueChanged AdjustmentEvent
-getAdjustable
-getAdjustmentType
-getValue JScrollbar ItemListener itemStateChanged ItemEvent
-getItem
-getItemSelectable
-getStateChange AbstractButton
JComboBox FocusListener focusGained
focusLost FocusEvent
-isTemporary Component KeyListener keyPressed
keyReleased
keyTyped KeyEvent
-getKeyChar
-getKeyCode
-getKeyModifiersText
-getKeyText
-isActionKey Component MouseListener mousePressed
mouseReleased
mouseEntered
mouseExited
mouseClicked MouseEvent
-getClickCount
-getX
-getY
-getPoint
-transaltePoint Component MouseMotionListener mouseDragged
mouseMoved MouseEvent Component MouseWheelListener mouseWheelMoved MouseWheelEvent
-getWheelRotation
-getScrollAmount Component WindowListener windowClosing
windowOpened
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactiovated WindowEvent
-getWindow Window WindowFocusListener windowGainedFocus
windowLostFocus WindowEvent
-getOppositeWindow Window WindowStateListener windowStateChanged WindowEvent
-getOldState
-getNewState Window
0 0
原创粉丝点击