事件处理基础

是visual basic和c的一个折中,完全可以控制事件从事件源(按钮)到事件监听器的传递过程,并将任何对象指派给事件监听器。事件委托模型。在java中,所有的事件对象都最终派生于java.util.EventObject类。
AWT事件处理的机制:
1.监听器对象事一个实现了特定监听器接口的类的实例
2.事件源是一个能够注册监听器对象并发送事件对象的对象
3.当事件发生时,事件源将事件对象传递给所有注册的监听器
4.监听器对象将利用事件源对象注册监听器对象
模板:eventSourceObject.addEventListener(eventListenerObject);
要求监听器对象所属的类必须实现相应的接口,实现一个接口就意味着要用相同的签名实现每个方法

  • 实例:处理按钮点击事件
package cn.wj.view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;

public class ButtonPanel extends JPanel {
    public ButtonPanel() {
        JButton yellow = new MyButton("yellow");
        JButton blue = new MyButton("blue");
        JButton green = new MyButton("green");
        yellow.addActionListener(new ColorAction(Color.YELLOW));
        blue.addActionListener(new ColorAction(Color.blue));
        green.addActionListener(new ColorAction(Color.green));
        add(yellow);
        add(blue);
        add(green);
    }

    private class ColorAction implements ActionListener {
        private Color background;

        public ColorAction(Color c) {
            background = c;
        }

        // 点击一次就调用一次这个方法
        @Override
        public void actionPerformed(ActionEvent e) {
            if (background != null)
                setBackground(background);
        }

    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
    }

}

javax.swing.JButton
1.JButton(String label):构造一个按钮。标签可以是常规的文本,从jdk1.3开始也可以是html
JButton yellow = new MyButton("<html><div style='color:red'>yellow</div></html>");
2.JButton(Icon icon):显示在按钮表面的图标
3.JButton(String label,Icon icon):显示在按纽表面的图标和文本
java.awt.Container
1.Component add(Component c):将组件c添加到容器中
java.swing.ImageIcon是icon的子类
ImageIcon(Sring filename):构造一个图标,他的图像存储在一个文件中.通过媒体跟踪器自定的加载这个图像

  • 建议使用内部类
    使用内部类的好处:

不需要为每个用户界面组件定义一个心累。在前面列举的栗子中,三个按钮共享同一个监听器类。当然,每个按钮分别使用不同的监听器对象,但是这些对象并不大,他们只包含一个颜色值和一个面板的引用.建议为事件处理设计一个专门的内部类,而不要将一个已经存在的类转换成监听器。
不仅如此,还可以使用匿名内部类简化代码
注意:需要注意的是,在匿名内部类引用的参数变量在外部类前面要加final修饰,匿名内部类适用于只作为参数传递,使用一次。
在jdk1.4中引入了不使用内部类定义简单的事件监听器的机制。EventHandler类
Object EventHandler.create(Class listenerInterface,Object o,String action)
在我的理解中:这种方法使用于调用没有参数的方法,就是将目标类的action方法添加到一个游离的listener中,然后可以把其他的事件源添加监听器

  • 将组件变成事件监听器
    任何实现了ActionListener接口的类对象都可以作为按钮监听器。但是更好的是将要执行的按钮动作创建一个新类和该类对象.

但是可以将组件变成事件监听器,这样会省去一部分代码
EventObject类是所有事件类的超类,其中getSource方法可以给出每个事件的事件源。事件源是产生事件和通告监听器的对象。
java.util.EventObject
Object getSource():返回发生事件的对象引用
java.awt.event.ActionEvent
1.String getActionCommand():返回于这个动作事件相关的命令字符串。如果是按钮,命令字符串就等于按钮标签,除非已经使用setActinoCommand方法对字符串进行了修改
java.beans.EventHandler
1.Object EventHandler.create(Class listenerInterface,Object o,String action)
2.Object EventHandler.create(Class listenerInterface,Object o,String action,String eventProperty)
3.Object EventHandler.create(Class listenerInterface,Object o,String action,String eventProperty,String listenerMethod)

  • 实例:改变观感
    在默认情况下,swing使用metal观感

改变观感:
1.jre/lib下的swing.properties将属性swing.defaultlaf设置为其他
2.动态改变观感
2.1UIManager.setLookAndFeel(plaf);
2.2调用静态方法
SwingUtilities.updateComponentTreeUI(Component c)来刷新所有组件集,需要相方法提供一个组件,并由此找到其他的所有组件
UIManager.LookAndFellInfo[] infos = UIManager.getInstalledLookAndFeels();获得所有观感的名字和类名

package cn.wj.view;

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;

public class PlatPanel extends JPanel{
    public PlatPanel() {
        LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
        for (LookAndFeelInfo info : infos) {
            makeButton(info.getName(), info.getClassName());
        }
    }
    private void makeButton(String name,final String platName){
        JButton j = new JButton(name);
        add(j);
        j.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    UIManager.setLookAndFeel(platName);
                    SwingUtilities.updateComponentTreeUI(PlatPanel.this);
                    
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });
    }
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
}
}

javax.swing.UIManager
1.static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels():得到一个用于描述已安装的观感实例的对象数组
2.static setLookAndFeel(String classname):设置当前的观感 classname为观感实现类的名称
javax.swing.UIManger.LookAndFeelInfo
1.String getName():获取观感的显示名称
2.String getClassName)():获取观感实现类的名称

  • 实例:捕获窗口事件
    WindowListener接口中有7个方法,当发生窗口事件时,框架将调用这些方法来响应7个不同的事件

1.windowOpend(WindowEvent e):窗口打开后调用这个方法
2.windowClosing(WindowEvent e):在用户发出窗口管理器命令关闭窗口是调用这个方法,仅当调用hide或者dispose方法后窗口才能够关闭
3.windowClosed(WindowEvent e):窗口关闭后调用这个方法
4.windowIconified(WindowEvent e):图标化,窗口图标化后调用这个方法
5.windowDeiconified(WindowEvent e):去石墨化,窗口非图标化后调用这个方法
6.windowActiveted(WindowEvent e):激活的,激活窗口后调用这个方法,只有框架或者对话框可以被激活,通常窗口管理器会对活动窗口进行修饰
7.windowDeactivated(WindowEvent e):未激活的,窗口未激活状态后调用这个方法
注意:为了能够查看窗口是否最大化,需要安装WindowStateListener。
适配器类:
书写6个没有任何操作的方法代码显然是一种乏味的工作,出于简化的目的,每个含有多个方法的AWT监听器接口都配有一个adapter(适配器)类,这个类实现了接口中的所有方法,但每个方法没有做任何事情.可以通过扩展适配器来指定对某些事件的响应,而不必实现接口中的每个方法.(ActionListener接口中只有一个actionPerformed(ActionEvent e);因此没有必要有适配器)
只要框架产生了窗口事件,就会调用7个方法中的一个方法将事件传递给listener对象.
创建一个扩展于windowAdapter的监听器类是一种很好的改进,但是还可以继续改进。事实上,没有必要未listener对象命名,同时还可以将监听器类定义成框架的匿名内部类
java.awt.event.WindowStateListener
void windowStateChanged(WindowEvent e):窗口被极大化,图标化或恢复未正常大小时调用这个方法
java.awt.event.WindowEvent
int getNewState():返回窗口状态改变事件中窗口的新状态。
int getOldState():返回窗口状态改变事件中窗口的旧状态

AWT事件继承层次

AWT的语义事件和低级事件

AWT将时间分为低级事件和语义事件。语义事件指表达用户动作的事件,如点击按钮,ActionEvent就是语义事件。低级事件指形成那些事件的事件。调节滚动条是一种语义事件,但拖动鼠标是低级事件。
AWT常用语义事件:
1.ActionEvent:对应按钮点击,菜单选择,选择列表项或在文本域中键入enter键
2.AjustmentEvent:用户调节滚动条
3.ItemEvent:用户从复选框或列表项中选择一项
常用低级事件:
1.KeyEvent:一个键被按下或释放
2.MouseEvent:鼠标被按下、释放、移动或拖动
3.MouseWheelEvent:鼠标滚轮被转动
4.FocusEvent:某个组件获得焦点或失去焦点。
5.windowWvent:窗口状态被改变
事件处理总结:
事件委托机制,能够确保正确地理解事件类、监听器接口和适配器之间的关系
事件源是用户界面组件、窗口和菜单。操作系统会将用户的动作通知给相关的事件源,例如移动鼠标。事件源在事件对象中描述了事件属性。事件源还保留了一组监听器,这是事件发生时需要调用的对象。事件发生后,事件源调用监听器接口的相应方法,以便将事件的相关信息传递给不同的监听器。事件源通过将相应的事件对象传递给监听器类中的方法来实现这一点。监听器分析事件对象,以便获取与事件相关的详细信息。例如:可以调用getSource方法得到事件源,或者调用MouseEvent类的getX和getY方法得到鼠标的当前位置.所有低级事件(在我理解就是组成用户动作的拆分成单个的事件集合)都继承与ComponentEvent。这个类有一个被称为getComponent的方法,主要用途时报告那个组件产生了事件,可以用getComponent代替getSource,getComponent方法的返回值跟getSource一样,但前者已经将结果转换成Component类型
java.awt.event.ComponentEvent
Component getComponent():返回事件源组件的引用,它与(Component)getSource()一样

低级事件类型

与具体用户界面组件无关,但与敲击键盘和活动鼠标有关的事件

  • 键盘事件
    当按下键盘产生id为KEY_PRESSED的keyEvent事件,当释放键盘产生id为KEY_RELEASED的keyEvent事件.可以使用实现了keyListener接口的任意类的keyPressed和keyReleased方法处理这些事件.将两个方法合并得keyTyped方法,可以报告由用户敲击键盘产生的字符.java明确区分字符和虚拟键码,虚拟键码用前缀VK_表示,例如VK_A,虚拟键码,虚拟键码与键盘上的键一一对应,例如VK_A表示被标记为A的键,虚拟键码没有单独小写键,即键盘没有单独的小写键

注意:虚拟键码涉及"扫描码",这是在按下一个物理键或释放一个物理键时,键盘项计算机发动的编码.

JTextArea area = new JTextArea(12,12);
        area.setText("helloworld");
        area.addKeyListener(new KeyListener(){
            @Override
            public void keyTyped(KeyEvent e) 
            {
            }
            
            @Override
            public void keyReleased(KeyEvent e) {
                
            }
            
            @Override
            public void keyPressed(KeyEvent e) {
                int keyCode = e.getKeyCode();
                if(keyCode==KeyEvent.VK_A && e.isShiftDown()){
                    System.out.println(true);
                }else{
                    System.out.println(false);
                }
            }
        });
        add(area);

在keyTyped方法中,调用getKeyChar方法得到键入的实际字符,并不是所有的敲击键盘都会产生keyTyped的调用,只有那些产生unicode字符的敲击才能够在keyTyped方法中捕获,可以使用keyPressed方法检查光标键和其他命令键
通常面板不接收任何键盘事件,因此可以调用setFocusable方法对默认情形进行覆盖

package cn.wj.view;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

public class SketchPanel extends JPanel {

    private Point2D last;
    private List<Line2D> lines;
    private final int max = 20;
    private final int min = 10;

    public SketchPanel() {
        last = new Point2D.Double(100, 100);
        lines = new ArrayList<Line2D>();
        addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
                char keyChar = e.getKeyChar();
                int d;
                if (Character.isUpperCase(keyChar)) {
                    d = max;
                    keyChar = Character.toLowerCase(keyChar);
                } else {
                    d = min;
                }
                if (keyChar == 'a')
                    add(-d, 0);
                else if (keyChar == 'd')
                    add(d, 0);
                else if (keyChar == 'w')
                    add(0, -d);
                else if (keyChar == 's')
                    add(0, d);
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
                int keyCode = e.getKeyCode();
                int d;
                if (e.isShiftDown())
                    d = max;
                else
                    d = min;
                if (keyCode == KeyEvent.VK_LEFT)
                    add(-d, 0);
                else if (keyCode == KeyEvent.VK_RIGHT)
                    add(d, 0);
                else if (keyCode == KeyEvent.VK_UP)
                    add(0, -d);
                else if (keyCode == KeyEvent.VK_DOWN)
                    add(0, d);
            }
        });
        setFocusable(true);// 对默认情形进行覆盖,panel不接收键盘事件
    }

    public void add(int dx, int dy) {
        Point2D end = new Point2D.Double(last.getX() + dx, last.getY() + dy);
        System.out.println(end);
        Line2D line = new Line2D.Double(last, end);
        lines.add(line);
        repaint();// 快速重新绘制形状
        last = end;// 记录前一个位置
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D gd = (Graphics2D) g;
        for (Line2D line2d : lines) {
            gd.draw(line2d);
        }
    }
}

java.awt.event.KeyEvent
1.char getChar():返回用户键入字符
2.int getKeyCode():返回改键盘事件的虚拟键码
3.boolean isActionKey()如果事件的键是一个动作键,返回true;动作键:HOME,END,PAGE UP,UP,DOWN,LEFT,F1...
4.static String getKeyText(int keyCode):返回描述键码的字符串
5.static String getKeyModifiers(int modifiers):返回描述shift,ctrl+shift这类修饰符键的字符串
java.awt.event.Input
int getModifiers():返回一个整型数值,描述shift,control,all...修饰符的状态,这个方法既可以用于键盘事件,也可以应用于鼠标事件。

  • 鼠标事件
    当用户点击鼠标按钮时,将会调用三个监听器方法:鼠标第一次被按下时调用mousePressed,鼠标释放时调用mouseReleaseed,最后调用mouseClicked。如果只对最后的点击事件感兴趣,就可以忽略前面两个方法。想区分单击,双击和三击,需要使用getClickCount方法.不建议使用鼠标点击和键盘修饰符组合,会显得十分混乱。

getModifiersEx方法能够准确地报告鼠标事件的鼠标按钮和键盘修饰符.
可以利用Tookit类中的createCustomCursor方法自定义光标类型
Tookit t = Tookit.getDefaultTookit();
Image i = t.getImage("image");
t.createCustomCursor(img,new Point(10,10),"表述光标的字符串")
ne Point(10,10):给出了光标的热点偏移
字符串:可以用于访问的支持,可以将光标形式读给视力受损或没有在屏幕前面的人
当用户在移动鼠标的同时按下鼠标,就会调用mouseMoved而不是调用mouseDragged
注意:只有鼠标在一个组件内部停留才会调用mouseMoved,然而,即使鼠标拖动到组件外面,mouseDragged方法也会被调用
mouseEntered:鼠标进入组件时调用
mouseExited:鼠标移出组件时调用
如何监听鼠标事件:鼠标点击有mouseClick过程报告,他是mouseListener接口的一部分,由于大部分应用程序仅对鼠标点击感兴趣,而对鼠标移动并不太感兴趣,但鼠标移动事件发生的频率又很高。因此将鼠标移动事件与拖动事件定义在一个称为MouseMotionListener的独立接口中.

package cn.wj.view;

import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

public class MousePanell extends JPanel {
    private List<Rectangle2D> list;
    private Rectangle2D current;
    private static double width = 20;
    private static double height = 20;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D gd = (Graphics2D) g;
        for (Rectangle2D rectangle2d : list) {
            gd.draw(rectangle2d);
        }
    }

    public MousePanell() {
        list = new ArrayList<Rectangle2D>();
        // 添加点击事件,用来判断点击多少次
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                current = find(e.getPoint());
                int count = e.getClickCount();
                if (count == 2 && current != null) {
                    list.remove(current);
                }
                repaint();
            }

            // 点击事件
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                current = find(e.getPoint());
                if (current == null)
                    add(e.getPoint());
            }

        });
        // 添加移动事件
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                super.mouseMoved(e);
                if (find(e.getPoint()) == null) {
                    URL url = DrawFrame.class.getClassLoader().getResource("xiaozhi.png");
                    Toolkit toolkit = Toolkit.getDefaultToolkit();
                    Image image = toolkit.createImage(url);
                    Cursor createCustomCursor = toolkit.createCustomCursor(image, new Point(10, 10), "小智");
                    setCursor(createCustomCursor);
                } else
                    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            }

            // 移出了组件仍要执行
            @Override
            public void mouseDragged(MouseEvent e) {
                super.mouseDragged(e);
                if (current != null) {
                    current.setFrame(e.getX(), e.getY(), width, height);
                    repaint();
                }
            }
        });
    }

    // 添加
    protected void add(Point point) {
        Rectangle2D r = new Rectangle2D.Double();
        r.setRect(point.getX(), point.getY(), width, height);
        list.add(r);
    }

    // 寻找
    public Rectangle2D find(Point2D p) {
        for (Rectangle2D r : list) {
            if (r.contains(p))
                return r;
        }
        return null;
    }

}

用Shape.contains(Point p):只要point在shape中就返回true
java.awt.Component
public void setCursor(Cursor c):给光标图像设置给定光标

  • 焦点事件
    用鼠标可以指向屏幕上的任何一个对象。但是在使用键盘输入时,敲击键盘必须定位于一个特定的屏幕对象。窗口管理器(例如window)直接将所有的敲击定位于活动窗口。通常,活动窗口用高亮度显示的标题栏进行区分,在任何时刻,只有一个窗口可以是活动的。当java窗口接收到敲击键盘的操作,并定位于某个特定的组件时,这个组件就具有了焦点.一个窗口中,最多只有一个组件拥有焦点。如果用户点击另一个组件,那么刚刚获得焦点的组件就会失去焦点,而被点击的组件就会获得焦点。tab键可以切换焦点.

标签和面板在默认情况下不能获得焦点,因为他们主要是用于装饰和分组的.如果希望根据用户的击键操作在面板上绘制图形,就要对默认处理进行覆盖
jdk1.4可以调用panel.setFocusable(true);
焦点拥有者
焦点窗口
活动窗口
获得键盘焦点管理器
KeyboardFocusManager.getCurrentKeyboardFocusManger();
为了能够得到焦点变化的通知,需要将焦点监听器安装到组件或者窗口中。一个组件焦点监听器必须实现FocusListener接口和两个方法focusGained(获取焦点)和focusLost(失去焦点)
getComponent方法负责报告获得或失去焦点的组件
isTemporary:将在焦点发生临时性变化时返回true,临时性的焦点改变只组件临时性地失去控制,但又可以自动地找回焦点。当用户选择不同的活动窗口时就会发生这种情况,只要用户再次选择当前窗口,同一组件就会重新获得焦点

动作-

多点传送

实现事件源