JavaGUI(二)——组件

有无示例代码取决于作者想不想练

AWT中常用组件

基本组件

组件名 功能
Button 按钮
Canvas 用于绘图的画布
Checkbox 复选框组件(也可当做单选框组件使用)
CheckboxGroup 用于将多个Checkbox组件组合成一组,一组Checkbox组件将只有一个可以被选中,即全部变成单选框组件
Choice 下拉选择框
Frame 窗口,在GUI程序里通过该类创建窗口
Label 标签类,用于放置提示性文本
List 列表框组件,可以添加多条目
Panel 不能单独存在基本容器类,必须放到其他容器中
Scrollbar 滑动条组件,如果需要用户输入谓语某个范围的值,就可以使用滑动条组件,比如调色板中设置RGB的三个值所用的滑动条。当创建一个滑动条时,必须指定它的方向,初始值、滑块大小,最小值和最大值。
ScrollPane 带水平以及垂直滚动条的容器组件
TextArea 多行文本域
TextField 单行文本框

示例代码:

public class Demo01 {
    static TextArea text = new TextArea(5,50);

    static Choice choice = new Choice();

    static CheckboxGroup cg = new CheckboxGroup();
    static Checkbox male = new Checkbox("male",true,cg);
    static Checkbox female = new Checkbox("female",false,cg);

    static Checkbox married = new Checkbox("married?");

    static List list = new List();

    static TextField textField = new TextField();
    static Button btn = new Button("confirm");

    public static void main(String[] args){
        initComponent();
        showView();
    }
    static void initComponent(){
        list.add("red");
        list.add("green");
        list.add("blue");

        choice.add("red");
        choice.add("green");
        choice.add("blue");
    }

    static void showView(){
        Box box1 = Box.createVerticalBox();

        Box box1_box1 = Box.createHorizontalBox();
        Box box1_box2 = Box.createHorizontalBox();
        box1.add(box1_box1);
        box1.add(box1_box2);

        Box box1_box1_box1 =Box.createVerticalBox();
        box1_box1.add(box1_box1_box1);

        box1_box1_box1.add(text);
        Box box1_box1_box1_box = Box.createHorizontalBox();
        box1_box1_box1.add(box1_box1_box1_box);
        box1_box1_box1_box.add(male);
        box1_box1_box1_box.add(female);
        box1_box1_box1_box.add(married);
        box1_box1_box1_box.add(choice);

        box1_box1.add(list);

        Box box1_box2_box = Box.createHorizontalBox();
        box1_box2.add(box1_box2_box);

        box1_box2_box.add(textField);
        box1_box2_box.add(btn);

        Frame frame = new Frame("测试窗口");

        frame.add(box1);

        frame.pack();
        frame.setVisible(true);
    }
}

对话框Dialog

Dialog

Dialog是Window类的子类,是一个容器类,属于特殊组件。对话框是可以独立存在的顶级窗口,因此用法与普通窗口的用法几乎完全一样,但是使用对话框需要注意下面两点:

  • 对话框通常依赖于其他窗口,就是通常需要有一个父窗口;
  • 对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被打开后,该模式对话框总是位于它的父窗口之上,在模式对话框被关闭之前,父窗口无法获得焦点。
方法名称 方法功能
Dialog(Frame owner,String title,boolean modal) 创建一个对话框对象:
owner:当前对话框的父窗口
title:当前对话框的标题
modal:当前对话框是否是模式对话框,true/false

FileDialog

Dialog类还有一个子类:FileDialog,它代表一个文件对话框,用于打开或者保存文件,需要注意的是Filedialog无法指定模态或者非模态,这是因为FileDIalog依赖于运行平台的实现,如果运行平台的文件对话框都是模态的,那么FileDialog也是模态的;否则就是非模态的。

方法名称 方法功能
FileDialog(Frame parent,String title,int mode) 创建一个文件对话框:
parent:指定父窗口
title:对话框标题
mode:文件对话框类型,如果指定为FileDialog.LOAD,用于打开文件,如果指定为FileDialog.SAVE,用于保存文件
String getDirectory() 获取被打开或保存文件的绝对路径
String getFile() 获取被打开或保存文件的文件名

事件处理

前面学习了如何防止各种组件,从而得到了丰富多彩的图形界面,但这些界面还不能响应用户的任何操作。比如单击前面所有窗口右上角的“X”按钮,但窗口依然不会关闭。因为在AWT编程中,所有用户的操作,都必须经过一套事件处理机制来完成,而Frame和组件本身并没有事件处理能力。


GUI事件处理机制

定义:当在某个组件上发生某些操作的时候,会自动触发一段代码的执行。
在GUI事件处理机制中设计到4个重要的概念需要理解:
事件源(Event Source):操作发生的场所,通常指某个组件,例如按钮、窗口等;
事件(Event):在事件源上发生的操作可以叫做对象,GUI会把事件都封装到一个Event对象中,如果需要知道该事件的详细信息,就可以通过Event对象来获取。
事件监听器(Event Listener):当某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理。
注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源(C)上,当在事件源C上发生了实践B之后,那么事件监听器A的代码就会自动执行。

使用步骤:

  1. 创建事件源组对象;
  2. 自定义类,实现XXXListener接口,重写方法;
  3. 创建事件监听器对象(自定义类对象)
  4. 调用事件源组件对象的addXXXListener方法完成注册监听

GUI中常见事件和事件监听器

事件监听器必须实现事件监听接口,AWT提供了大量的时间监听器用于实现不同类型的事件监听器,用于监听不同类型的事件。AWT中提供了丰富的事件类,用于封装不同组件上所有发生的特点操作,AWT的事件类都是AWTEvent类的子类,AWTEvent是EventObject的子类。

事件

AWT把事件分为了两大类:

  1. 低级事件:这类事件是基于某个特定动作的事件。比如进入、点击、拖放等动作的鼠标事件。比如进入、点击拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件。
事件 触发时机
ComponentEvent 组件事件,当组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变时触发该事件。
Container 容器事件,当容器里发生添加组件、删除组件时触发该事件。
WindowEvent 窗口事件,当窗口状态发生改变(如打开、关闭、最大化、最小化)时触发该事件。
FocusEvent 焦点事件,当组件得到焦点或失去焦点时触发该事件。
KeyEvent 键盘事件,当按键被按下、松开、单击时触发该事件。
MouseEvent 鼠标事件,当进行点击、按下、松开、移动鼠标等动作时触发该事件。
PaintEvent 组件绘制事件,该事件是一个特殊的事件类型
  1. 高级事件:这类事件并不会基于某个特定动作,而是根据功能含义定义的事件。
事件 触发时机
ActionEvent 动作时间,当按钮、菜单项被单击,在TextField中安Enter键时触发
AjustmentEvent 条件事件,在滑动条上移动滑块以调节数值时触发该事件。
ItemEvent 选项事件,当用户选中某项,或取消选中某项时触发该事件。
TextEvent 文本事件,当文本框、文本域的文本发生变化时触发该事件。

事件监听器

不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口,当指定事件发生后,事件监听器就会调用所包含的事件处理器(实例方法)来处理事件。

事件类别 描述信息 监听器接口名
ActionEvent 激活组件 ActionListener
ItemEvent 选择了某些项目 ItemListener
MouseEvent 鼠标移动 MouseMotionListener
MouseEvent 鼠标点击等 MouseListener
KeyEvent 键盘输入 KeyListener
FocusEvent 组件收到或失去焦点 FocusListener
AdjustmentEvent 移动了滚动条等组件 AdjustmentListener
ComponentEvent 对象移动缩放显示隐藏等 ComponentListener
WindowEvent 窗口收到窗口级事件 WindowListener
TextEvent 文本字段或文本区发生改变 TextListener

菜单组件

构建GUI界面,其实就是把一些GUI组件,按照一定的布局放入到容器中展示就可以了。在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便地使用特定的功能,在AWT中,菜单相关组件的使用和之前学习的组件时一模一样的,只需要把菜单条、菜单、菜单项组合到一起,按照一定的布局,放入容器中即可。

菜单组件名称 功能
MenuBar 菜单条,菜单的容器
menu 菜单组件,菜单项的容器。它是MenuItem的子类,所以可作为菜单项使用
PopupMenu 上下文菜单组件(右键菜单组件)
MenuItem 菜单项组件
CheckboxMenuItem 复选框菜单项组件

image

菜单相关组件使用:

  1. 准备菜单项组件,这些组件可以是MenuItem及其子类对象
  2. 准备菜单组件Menu或者PopupMenu(右击弹出子菜单),把第一步中准备好的菜单项组件添加进来;
  3. 准备菜单条组件MenuBar,把第二步中准备好的菜单组件Menu添加进来;
  4. 把第三步中准备好的菜单条组件添加到窗口对象中显示。

小技巧:

  1. 如果要在某个菜单的菜单项之间添加分割线,那么只需要调用Menu的add(new MenuItem("-"))即可。
  2. 如果要给某个菜单项关联快捷键功能,那么只需要在创建菜单项对象时设置即可,例如给菜单项关联Ctrl+shift+Q快捷键,只需要:new MenuItem("菜单项名字",new MenuShortcut(KeyEvent.VK Q,true));

示例代码如下:

public class Demo05 {
    public static void main(String[] args){
        new Demo05().init();
    }

    public void init(){
        Frame frame = new Frame();

        frame.setBounds(300,300,500,300);

        MenuBar menuBar = new MenuBar();

        Menu file = new Menu("File");
        Menu edit = new Menu("Edit");
        Menu format = new Menu("Format");

        MenuItem autoWrap = new MenuItem("Auto Wrap");
        MenuItem copy = new MenuItem("Copy");
        MenuItem paste = new MenuItem("Paste");

        MenuItem comment  = new MenuItem("Comment ",new MenuShortcut(KeyEvent.VK_Q,true));
        MenuItem cancelComment = new MenuItem("Cancel Comment"  );

        frame.setMenuBar(menuBar);

        menuBar.add(file);
        menuBar.add(edit);

        edit.add(autoWrap);
        edit.add(copy);
        edit.add(paste);
        edit.add(new MenuItem("-"));
        edit.add(format);

        format.add(comment);
        format.add(cancelComment);

        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        frame.setVisible(true);
    }
}

绘图

很多程序如各种游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时,有时候也必须“动态”地向客户端生成各种图形、图标,比如 图形验证码、统计图形等,这些都需要利用AWT的绘图功能。

组件绘图原理

之前我们所学的组件,例如Button、Frame等等,这些组件展示出来的图形,其本质就是用AWT的绘图来完成的。

在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制自身图形呢?在Component类中,提供了下列三个方法来完成组件图形的绘制与刷新;

paint(Graphics g): 绘制组件的外观;
update(Graphics g):内部调用paint方法,刷新组件外观;
repain():调用update方法,刷新组件外观;
image

一般情况下,update和paint方法时由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。

Graphics对象的使用

AWT中提供了Canvas类充当画布,提供了Graphics类来充当画笔,通过调用Graphics对象的setColor()方法可以给画笔设置颜色。

画图的步骤:

  1. 自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;
  2. 在paint方法内部,真正开始画图之前调用Graphics对象的setColor()、setFont()等方法设置画笔颜色、字体等属性;
  3. 调用Graphics画笔的drawXxx()方法开始画图。

其实画图的核心在于使用Graphics画笔在Canvas画布上画出什么颜色、什么样式的图形,所以核心在画笔上,下表中列出了Graphics类中常用的一些方法:

方法名称 方法功能
setColor(Color c) 设置颜色
setFont(Font font) 设置字体
drawLine() 绘制直线
drawRect() 绘制矩形
drawRoundRect() 绘制圆角矩形
drawOval() 绘制椭圆形
drawPolygon() 绘制多边形
drawArc() 绘制圆弧
drawPolyline() 绘制折线
fillRect() 填充矩形区域
fillOval() 填充椭圆区域
fillPolygon() 填充多边形区域
fillArc() 填充圆弧对应的扇形区域
drawImage() 绘制位图

绘图示例代码:

public class Demo01 {
    private static Graphics gra = null;
    public static void main(String[] args){
        init();
    }
     static class MyCanvas extends Canvas {
        @Override
        public void paint(Graphics g) {
            gra = g;
            g.drawOval(200 ,200,200,100);
            g.setColor(Color.red);
        }
    }

    static void init(){
        Frame frame = new Frame();
        MyCanvas mc = new MyCanvas();
        Panel p = new Panel();
        mc.setPreferredSize(new Dimension(500,500));

        frame.add(p);
        p.add(mc);

        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        frame.pack();
        frame.setVisible(true);
    }
}

Java也可以用于开发一些动画。所谓的动画,就是间隔一定事件(通常小于0.1秒)重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画。

为了实现间隔一定的时间就重新调用组件的repaint()方法,可以借助与Swing提供的TImer类,Timer类时一个定时器,它由如下一个构造器:
Timer(int delay,ActionListener listener):每隔delay毫秒,系统就会自动触发ActionListener监听器里的事件处理器方法,在方法内部我们就可以调用组件的repaint方法,完成组件重绘。

处理位图

如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调。AWT也允许在组件上绘制位图,Graphics提供了drawImage(Image image)方法用于绘制位图,该方法需要一个Image参数——代表位图,通过该方法就可以绘制出指定的位图。

位图使用步骤:
  1. 创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;
  2. 调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;
  3. 调用组件paint方法中提供的Graphics对象的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。
使用位图绘制组件的好处:

使用位图阿里绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完毕,再一次的图像显示到组件上即可,这样用户的体验会好一些。

示例代码:

public class Demo02 {

    Frame frame = new Frame("画布");

    // 设置绘图区的宽高
    private final int AREA_WIDTH = 500;
    private final int AREA_HEIGHT = 400;

    // 定义一个右键菜单,用于更改颜色
    private PopupMenu contextMenu = new PopupMenu();
    private MenuItem redItem = new MenuItem("RED");
    private MenuItem greenItem = new MenuItem("GREEN");
    private MenuItem blueItem = new MenuItem("BLUE");
    private MenuItem Clean = new MenuItem("CLEAN");

    // 定义一个用于记录画笔颜色的变量
    private Color forceColor = Color.black;

    // 创建一个BufferedImage位图对象
    BufferedImage image = new BufferedImage(AREA_WIDTH,AREA_HEIGHT,BufferedImage.TYPE_INT_RGB);

    // 通过位图,获取关联的Graphics对象
    Graphics g = image.createGraphics();

    // 自定义一个类,继承Canvas
    private class MyCanvas extends Canvas{
        @Override
        public void paint(Graphics g) {
            // 在位图上画画,然后将位图转交给画布对应的画笔,在使用画笔将位图画到画布上。
            g.drawImage(image,0,0,null);
        }
    }

    // 创建画布对象
    MyCanvas drawArea = new MyCanvas();

    // 定义变量,记录鼠标拖动过程,上一次所处的坐标
    private int preX = -1;
    private int preY = -1;
    public void init(){
        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switch (e.getActionCommand()){
                    case "RED":
                        forceColor = Color.red;
                        break;
                    case "GREEN":
                        forceColor = Color.green;
                        break;
                    case "BLUE":
                        forceColor = Color.BLUE;
                        break;
                    case "CLEAN":
                        g.setColor(Color.white);
                        g.fillRect(0,0,AREA_WIDTH,AREA_HEIGHT);
                        drawArea.repaint();
                        break;
                    default:
                        break;
                }
                if(!e.getActionCommand().equals("CLEAN")){
                    System.out.println("当前颜色是:"+e.getActionCommand());
                }
            }
        };

        redItem.addActionListener(listener);
        greenItem.addActionListener(listener);
        blueItem.addActionListener(listener);
        Clean.addActionListener(listener);

        contextMenu.add(redItem);
        contextMenu.add(greenItem);
        contextMenu.add(blueItem);
        contextMenu.add(Clean);

        // 把colorMenu设置给区域
        drawArea.add(contextMenu);

        drawArea.addMouseListener(new MouseAdapter() {
            @Override // 鼠标点击释放事件
            public void mouseReleased(MouseEvent e) {
                // PopupTrigger代表点击了弹出菜单键,也就是右键
                boolean popupTrigger = e.isPopupTrigger();
                if(popupTrigger){
                    contextMenu.show(drawArea,e.getX(),e.getY());
                }

                preX = -1;
                preY = -1;
            }
        });

        g.setColor(Color.white);
        g.fillRect(0,0,AREA_WIDTH,AREA_HEIGHT);

        drawArea.addMouseMotionListener(new MouseMotionAdapter() {
            // 鼠标拖拽(滑动)
            @Override
            public void mouseDragged(MouseEvent e) {
                // 鼠标拖拽使触发
                if(preX>=0&&preY>=0){
                    g.setColor(forceColor);
                    g.drawLine(preX,preY,e.getX(),e.getY());
                }
                preY = e.getY();
                preX = e.getX();

                drawArea.repaint();
            }
        });

        // 设置画布大小
        drawArea.setPreferredSize(new Dimension(AREA_WIDTH,AREA_HEIGHT));
        frame.add(drawArea);

        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args){
        new Demo02().init();
    }
}

ImageIO的使用

在实际生活中,很多软件偶支持打开本地磁盘已经存在的图片,然后进行编辑,编辑完毕,在重新保存到本地磁盘。如果使用AWT要完成这样的功能,那么需要使用到ImageIO这个类,可以操作本地磁盘的图片文件。

方法名称 方法功能
static BufferedImage read(File input) 读取本地磁盘图片文件
static BufferedImage read(InputStream input) 读取本地磁盘图片文件
static boolean write(RenderedImage im, String formatName,File output) 往本地磁盘中输出图片文件
posted @ 2022-09-08 14:19  maplerain  阅读(370)  评论(0编辑  收藏  举报