图形用户界面GUI(二)

事件处理

在 AWT 编程中 ,所有用户的操作,都必须都需要经过一套事件处理机制来完成,而 Frame 和组件本身并没有事件处理能力。

GUI事件处理机制

在某个组件上发生某些操作的时候,会自动的触发一段代码的执行。

事件源(Event Source):操作发生的场所,通常指某个组件,例如按钮、窗口等;
事件(Event):在事件源上发生的操作可以叫做事件,GUI会把事件都封装到一个Event对象中,如果需要知道该事件的详细信息,就可以通过Event对象来获取。
事件监听器(Event Listener):当在某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理。
注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源©上,当在事件源C上发生了事件B之后,那么事件监听器A的代码就会自动执行。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

public class EventDemo1 {
    
    
     Frame frame=new Frame("这是文件处理");
    TextField tf=new TextField(30);
    //创建事件源对象
    JButton button=new JButton("确定");

    public  void init(){
    
    
        //创建监听器对象
        MyListener myListener=new MyListener();
        //注册监听
        button.addActionListener(myListener);
        
        frame.add(tf,BorderLayout.NORTH);
        frame.add(button);
        frame.pack();
        frame.setVisible(true);
    }
//自定义内部类,实现接口,重写方法
    private class MyListener implements ActionListener{
    
    
        @Override
        public void actionPerformed(ActionEvent e) {
    
    
            tf.setText("hello world");
        }
    }
}

常见事件和事件监听器

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

低级事件

这类事件是基于某个特定动作的事件。比如进入、点击、拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件。
在这里插入图片描述

高级事件

这类事件并不会基于某个特定动作,而是根据功能含义定义的事件。
在这里插入图片描述

事件监听器分类

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

public class ListenerDemo1 {
    
    
    public static void main(String[] args) {
    
    
    Frame frame = new Frame("这里测试监听器");
    //创建一个单行文本域
    TextField tf = new TextField(30);
    //给文本域添加TextListener,监听内容的变化
    tf.addTextListener(new TextListener() {
    
    
       @Override
       public void textValueChanged(TextEvent e) {
    
    
          System.out.println("当前内容:"+tf.getText());;
        }
     });

    //给frame注册ContainerListener监听器,监听容器中组件的添加
     frame.addContainerListener(new ContainerAdapter() {
    
    
        @Override
        public void componentAdded(ContainerEvent e) {
    
    
             Component child = e.getChild();
             System.out.println("容器中添加了新组件:"+child);
            }
        });

        //添加tf到frame
        frame.add(tf);
        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }
}
//用户点击错号即可关闭
public class ListenerDemo2 {
    
    
    public static void main(String[] args) {
    
    
        Frame frame = new Frame("这里测试WindowListener");

        frame.addWindowListener(new WindowAdapter() {
    
    
        //文件适配器WindowAdapter,根据自己选择选取内部方法
            @Override
            public void windowClosing(WindowEvent e) {
    
    
                System.exit(0);
            }
        });

        frame.setBounds(200,200,500,300);
        frame.setVisible(true);
    }
}

菜单组件

在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便的使用特定的功能,在AWT中,菜单相关组件的使用和之前学习的组件是一模一样的,只需要把菜单条、菜单、菜单项组合到一起,按照一定的布局,放入到容器中即可。
在这里插入图片描述(1)常见的菜单组件:

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

在这里插入图片描述

(2)菜单相关组件的使用:

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

(3)相关技巧:

1.如果要在某个菜单的菜单项之间添加分割线,那么只需要调用Menu的add(new MenuItem(-))即可。

2.如果要给某个菜单项关联快捷键功能,那么只需要在创建菜单项对象时设置即可,例如给菜单项关联 ctrl+shif+/ 快捷键,只需要:new MenuItem("菜单项名字",newMenuShortcut(KeyEvent.VK_Q,true);

public class SimpleMenu {
    
    
    //创建窗口
    private Frame frame = new Frame("这里测试菜单相关组件");
    
    //创建菜单条组件
    private MenuBar menuBar = new MenuBar();
    
    //创建文件菜单组件
    private Menu fileMenu = new Menu("文件");
    //创建编辑菜单组件
    private Menu editMenu = new Menu("编辑");
    //创建格式菜单
    private Menu formatMenu = new Menu("格式");
    
    //创建新建菜单项
    private MenuItem newItem = new MenuItem("新建");
    //创建保存菜单项
    private MenuItem saveItem = new MenuItem("保存");
    //创建退出菜单项
    private MenuItem exitItem = new MenuItem("退出");

    //创建自动换行选择框菜单项
    private CheckboxMenuItem autoWrap = new CheckboxMenuItem("自动换行");
    //创建复制菜单项
    private MenuItem copyItem = new MenuItem("复制");
    //创建粘贴菜单项
    private MenuItem pasteItem = new MenuItem("粘贴");
    //创建注释菜单项
    private MenuItem commentItem = new MenuItem("注释");
    //创建取消注释菜单项
    private MenuItem cancelItem = new MenuItem("取消注释");

    //创建一个文本域
    private TextArea ta = new TextArea(6, 40);


    public void init(){
    
    
        //定义菜单事件监听器
        ActionListener listener = new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                String command = e.getActionCommand();
                ta.append("单击“"+command+"”菜单\n");
                if (command.equals("退出")){
    
    
                    System.exit(0);
                }
            }
        };

        //为注释菜单项和退出菜单项注册监听器
        commentItem.addActionListener(listener);
        exitItem.addActionListener(listener);

        //为文件菜单fileMenu添加菜单项
        fileMenu.add(newItem);
        fileMenu.add(saveItem);
        fileMenu.add(exitItem);

        //为编辑菜单editMenu添加菜单项
        editMenu.add(autoWrap);
        editMenu.add(copyItem);
        editMenu.add(pasteItem);

        //为格式化菜单formatMenu添加菜单项
        formatMenu.add(commentItem);
        formatMenu.add(cancelItem);

        //将格式化菜单添加到编辑菜单中,作为二级菜单
        editMenu.add(new MenuItem("-"));
        editMenu.add(formatMenu);


        //将文件菜单和编辑菜单添加到菜单条中
        menuBar.add(fileMenu);
        menuBar.add(editMenu);


        //把菜单条设置到frame窗口上
        frame.setMenuBar(menuBar);

        //把文本域添加到frame中
        frame.add(ta);

        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }


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

public class PopupMenuTest {
    
    

    private Frame frame = new Frame("这里测试PopupMenu");

    //创建PopubMenu菜单
    private PopupMenu popupMenu = new PopupMenu();

    //创建菜单条

    private MenuItem commentItem = new MenuItem("注释");
    private MenuItem cancelItem = new MenuItem("取消注释");
    private MenuItem copyItem = new MenuItem("复制");
    private MenuItem pasteItem = new MenuItem("保存");


    //创建一个文本域
    private TextArea ta = new TextArea("我爱中华!!!", 6, 40);

    //创建一个Panel
    private  Panel panel = new Panel();

    public void init(){
    
    

        //把菜单项添加到PopupMenu中
        popupMenu.add(commentItem);
        popupMenu.add(cancelItem);
        popupMenu.add(copyItem);
        popupMenu.add(pasteItem);

        //设置panel大小
        panel.setPreferredSize(new Dimension(300,100));

        //把PopupMenu添加到panel中
        panel.add(popupMenu);

        //为panel注册鼠标事件
        panel.addMouseListener(new MouseAdapter() {
    
    
            @Override
            public void mouseReleased(MouseEvent e) {
    
    
                boolean flag = e.isPopupTrigger();
                //判断当前鼠标操作是不是触发PopupMenu的操作
                if (flag){
    
    
                    //让PopupMenu显示在panel上,并且跟随鼠标事件发生的地方显示
                    popupMenu.show(panel,e.getX(),e.getY());
                }
            }
        });

        //把ta添加到frame中间区域中
        frame.add(ta);

        //把panel添加到frame底部
        frame.add(panel,BorderLayout.SOUTH);

        //设置frame最佳大小,并可视;
        frame.pack();
        frame.setVisible(true);

    }

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

}

组件绘图原理

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

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

在这里插入图片描述
一般情况下,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画布上画出什么颜色、什么样式的图形.
在这里插入图片描述

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

    public class SimpleDraw {
    
    
       //标记画的是椭圆还是矩形
        private final String RECT_SHAPE="rect";
        private final String OVAL_SHAPE="oval";

        private Frame frame = new Frame("这里测试绘图");

        private Button drawRectBtn = new Button("绘制矩形");
        private Button drawOvalBtn = new Button("绘制椭圆");

        //定义变量用来保存当前用户需要绘制什么样的图形
        private String shape="";
        //创建画布
        private MyCanvas drawArea = new MyCanvas();
        
        public void init(){
    
    

            //为按钮添加点击事件
            drawRectBtn.addActionListener(new ActionListener() {
    
    
                @Override
                public void actionPerformed(ActionEvent e) {
    
    
                    shape = RECT_SHAPE;
                    drawArea.repaint();
                }
            });

            drawOvalBtn.addActionListener(new ActionListener() {
    
    
                @Override
                public void actionPerformed(ActionEvent e) {
    
    
                    shape = OVAL_SHAPE;
                    drawArea.repaint();
                }
            });

            //定义一个Panel,装载两个按钮
            Panel p = new Panel();
            p.add(drawRectBtn);
            p.add(drawOvalBtn);

            //把panel添加到frame底部
            frame.add(p,BorderLayout.SOUTH);

            //设置画布的大小
            drawArea.setPreferredSize(new Dimension(300,200));
            //把画布添加到frame中

            frame.add(drawArea);

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


        }

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


        //1.自定义类,继承Canvas类,重写paint方法

        private class MyCanvas extends Canvas{
    
    
            @Override
            public void paint(Graphics g) {
    
    
                Random r = new Random();

                if (shape.equals(RECT_SHAPE)){
    
    
                    //绘制矩形
                    g.setColor(Color.BLACK);
                    g.drawRect(r.nextInt(200),r.nextInt(100),40,60);
                }

                if(shape.equals(OVAL_SHAPE)){
    
    
                    //绘制椭圆
                    g.setColor(Color.RED);
                    g.drawOval(r.nextInt(200),r.nextInt(100),60,40);
                }

            }
        }
    }

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

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

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class PinBall {
    
    
    //桌面宽度
    private final int TABLE_WIDTH = 300;
    //桌面高度
    private final int TABLE_HEIGHT = 400;
    //球拍的高度和宽度
    private final int RACKET_WIDTH = 60;
    private final int RACKET_HEIGHT = 20;
    //小球的大小
    private final int BALL_SIZE = 16;
    //定义小球纵向运行速度
    private int ySpeed = 10;
    //小球横向运行速度
    private int xSpeed = 5;
    //定义小球的初始坐标
    private int ballX = 120;
    private int ballY = 20;
    //定义球拍的初始坐标,x坐标会发生变化,y坐标不会发生变化
    private int rackeX = 120;
    private final int RACKET_Y = 340;

    //声明定时器
    private Timer timer;
    //定义游戏结束的标记
    private boolean isLose = false;

    //声明一个桌面
    private MyCanvas tableArea = new MyCanvas();

    //创建窗口对象
    private Frame frame = new Frame("弹球游戏");

    public void init(){
    
    
        //设置桌面区域的最佳大小
        tableArea.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));
        //把桌面添加到frame中
        frame.add(tableArea);

        //定义键盘监听器
        KeyListener keyListener = new KeyAdapter(){
    
    

            //监听键盘 ←  → 按下操作,当指定的键按下时,球拍的水平坐标分别会增加或者减少
            @Override
            public void keyPressed(KeyEvent e) {
    
    
                int keyCode = e.getKeyCode();
                if (keyCode==KeyEvent.VK_LEFT){
    
    //←
                    //没有到左边界,可以继续向左移动
                    if (rackeX>0){
    
    
                        rackeX-=10;
                    }
                }

                if (keyCode==KeyEvent.VK_RIGHT){
    
    //→
                    //没有到右边界,可以继续向右移动
                    if (rackeX<TABLE_WIDTH-RACKET_WIDTH){
    
    
                        rackeX+=10;
                    }
                }
            }
        };

        //为窗口和tableArea分别添加键盘事件
        frame.addKeyListener(keyListener);
        tableArea.addKeyListener(keyListener);

        //定义ActionListener,用来监听小球的变化情况
        ActionListener timerTask = new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                //小球碰到左右边框
                if (ballX<=0 || ballX>=TABLE_WIDTH-BALL_SIZE){
    
    
                    xSpeed=-xSpeed;
                }
                //小球的高度超出了球拍的位置,且横向不在球拍范围内,则游戏结束
                if (ballY > RACKET_Y && (ballX<rackeX || ballX>rackeX+RACKET_WIDTH)){
    
    
                    //结束定时器
                    timer.stop();
                    //把游戏结束的标记设置为true
                    isLose = true;
                    //重绘界面
                    tableArea.repaint();

                }
                //如果小球横向在球拍范围内,且到达球拍位置或者到达顶端位置,则小球反弹
                if (ballY<=0 || (ballY>=RACKET_Y-BALL_SIZE && ballX>=rackeX && ballX<=rackeX+RACKET_WIDTH)){
    
    
                    ySpeed=-ySpeed;
                }

                //更新小球的坐标
                ballX+=xSpeed;
                ballY+=ySpeed;

                //重绘桌面
                tableArea.repaint();
            }
        };

        //设置定时器,定时任务就是timerTask
        timer = new Timer(100,timerTask);
        timer.start();

        //设置frame最佳大小,并可视

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


    }

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

    private class MyCanvas extends Canvas{
    
    

        //重写paint方法,实现绘图
        @Override
        public void paint(Graphics g) {
    
    
            //判断游戏是否结束
            if (isLose){
    
    //结束
                g.setColor(Color.BLUE);
                g.setFont(new Font("Times",Font.BOLD,30));
                g.drawString("游戏结束!",50,200);
            }else{
    
    //没有结束

                //设置颜色并绘制小球
                g.setColor(Color.RED);
                g.fillOval(ballX,ballY,BALL_SIZE,BALL_SIZE);

                //设置颜色并绘制球拍
                g.setColor(Color.PINK);
                g.fillRect(rackeX,RACKET_Y,RACKET_WIDTH,RACKET_HEIGHT);
            }

        }
    }
}

处理位图

AWT 允许在组件上绘制位图, Graphics 提供了 drawlmage() 方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可以绘制出指定的位图 。

方法
1.创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;

2.调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;

3.调用组件的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。

使用位图绘制组件的好处:

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

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

public class HandDraw {
    
    
    //定义画图区的宽高
    private final int AREA_WIDTH = 500;
    private final int AREA_HEIGHT = 400;

    //定义变量,保存上一次鼠标拖动时,鼠标的坐标
    private int preX = -1;
    private int preY = -1;

    //定义一个右键菜单,用于设置画笔的颜色
    private PopupMenu colorMenu = new PopupMenu();
    private MenuItem redItem = new MenuItem("红色");
    private MenuItem greenItem = new MenuItem("绿色");
    private MenuItem blueItem = new MenuItem("蓝色");

    //定义一个BufferedImage对象
    private BufferedImage image = new BufferedImage(AREA_WIDTH,AREA_HEIGHT,BufferedImage.TYPE_INT_RGB);
    //获取BufferedImage对象关联的画笔
    private Graphics g = image.getGraphics();

    //定义窗口对象
    private Frame frame = new Frame("简单手绘程序");

    //定义画布对象
    private Canvas drawArea =  new Canvas(){
    
    
        @Override
        public void paint(Graphics g) {
    
    
            //把位图image绘制到0,0坐标点
            g.drawImage(image,0,0,null);
        }
    };

    //定义一个Color对象,用来保存用户设置的画笔颜色,默认为黑色
    private Color forceColor = Color.BLACK;

    public void init(){
    
    
        //定义颜色菜单项单击监听器
        ActionListener menuListener = new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                String command = e.getActionCommand();
                switch (command){
    
    
                    case "红色":
                        forceColor=Color.RED;
                        break;
                    case "绿色":
                        forceColor = Color.GREEN;
                        break;
                    case "蓝色":
                        forceColor = Color.BLUE;
                        break;
                }
            }
        };

        //为三个菜单项添加点击事件
        redItem.addActionListener(menuListener);
        greenItem.addActionListener(menuListener);
        blueItem.addActionListener(menuListener);

        //把菜单项添加到右键菜单中
        colorMenu.add(redItem);
        colorMenu.add(greenItem);
        colorMenu.add(blueItem);

        //把右键菜单添加到绘图区域drawArea
        drawArea.add(colorMenu);

        //将iamge图片背景设置为白色
        g.fillRect(0,0,AREA_WIDTH,AREA_HEIGHT);

        //设置绘图区域drawArea的大小
        drawArea.setPreferredSize(new Dimension(AREA_WIDTH,AREA_HEIGHT));

        //绘图区域drawArea设置鼠标移动监听器
        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());
                }

                //更新preX和preY
                preX = e.getX();
                preY = e.getY();

                //重新绘制drawArea组件
                drawArea.repaint();

            }
        });

        drawArea.addMouseListener(new MouseAdapter() {
    
    

            //用于弹出右键菜单
            @Override
            public void mouseReleased(MouseEvent e) {
    
    //松开鼠标键会触发
                boolean popupTrigger = e.isPopupTrigger();
                if (popupTrigger){
    
    
                    //把colorMenu显示到drawArea画图区域,并跟随鼠标显示
                    colorMenu.show(drawArea,e.getX(),e.getY());
                }

                //当鼠标松开时,把preX和preY重置为-1
                preX = -1;
                preY = -1;

            }
        });

        //把drawArea添加到frame中
        frame.add(drawArea);

        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }
    public static void main(String[] args) {
    
    
        new HandDraw().init();
    }
}

ImageIO的使用

软件都支持打开本地磁盘已经存在的图片,然后进行编辑,编辑完毕后,再重新保存到本地磁盘。我们可以用imageIO类来操作本地磁盘的图片文件。
在这里插入图片描述

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;

public class ReadAndSaveImage {
    
    

    private Frame frame = new Frame("图片查看器");

    private BufferedImage image;

    private class MyCanvas  extends Canvas{
    
    

        @Override
        public void paint(Graphics g) {
    
    
            if (image!=null){
    
    
                g.drawImage(image,0,0,image.getWidth(),image.getHeight(),null);
            }
        }
    }

    private MyCanvas imageComponent = new MyCanvas();

    public void init() throws Exception{
    
    

        //设置菜单项
        MenuBar mb = new MenuBar();
        Menu menu = new Menu("文件");
        MenuItem openItem = new MenuItem("打开");
        MenuItem saveItem = new MenuItem("另存为");

        openItem.addActionListener(e -> {
    
    
            //弹出对话框,选择本地图片
            FileDialog oDialog = new FileDialog(frame);
            oDialog.setVisible(true);
            //读取用户选择的图片
            String dir = oDialog.getDirectory();
            String file = oDialog.getFile();
            try {
    
    
                image = ImageIO.read(new File(dir,file));

                imageComponent.repaint();

            } catch (IOException e1) {
    
    
                e1.printStackTrace();
            }

        });


        saveItem.addActionListener(e -> {
    
    
            //弹出对话框,另存为
            FileDialog sDialog = new FileDialog(frame,"保存图片",FileDialog.SAVE);
            sDialog.setVisible(true);
            String dir = sDialog.getDirectory();
            String file = sDialog.getFile();

            try {
    
    
                ImageIO.write(image,"JPEG",new File(dir,file));
            } catch (IOException e1) {
    
    
                e1.printStackTrace();
            }
        });

        mb.add(menu);
        menu.add(openItem);
        menu.add(saveItem);

        frame.setMenuBar(mb);
        frame.add(imageComponent);

        frame.setBounds(200,200,800,600);

        frame.setVisible(true);

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



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

五子棋

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;

public class Gobang {
    
    

    //定义三个BufferedImage,分别代表棋盘图、黑子图、白子图
    private BufferedImage table;
    private BufferedImage black;
    private BufferedImage white;
    //定义一个BufferedImage,代表当鼠标移动时将要下子的选择框
    private BufferedImage selected;

    //定义棋盘的宽高,这里的定义尺寸和给定的board.jpg图片的尺寸一致因为棋盘背景是通过图片加载的
    private final int TABLE_WIDTH = 535;
    private final int TABLE_HEIGHT = 536;

    //定义棋盘中,每行和每列可下子的数目,这个数目跟给定的board.jpg中的数目是一致的,都为15
    private final int  BOARD_SIZE = 15;

    //定义每个棋子所占棋盘总宽度的大小比率;每个棋子所占宽度 535/15=35
    private final int RATE = TABLE_WIDTH/BOARD_SIZE;

    //定义棋盘有效区域与背景图坐标之间的偏移值,x坐标右移5个像素,y坐标下移6个像素
    private final int X_OFFSET = 5;
    private final int Y_OFFSET = 6;


    /*

        定义一个二维数组充当棋盘上每个位置处的棋子;
        该数组的索引与该棋子在棋盘上的坐标需要有一个对应关系:
            例如: 索引[2][3]处的棋子,对一个的真实绘制坐标应该是:

                xpos = 2*RATE+X_OFFSET=75;
                ypos = 3*RATE+Y_OFFSET=111;

     */
    private int[][] board = new int[BOARD_SIZE][BOARD_SIZE];//如果存储0,代表没有棋子,如果存储1,代表黑棋,如果存储2,代表白棋

    //定义五子棋游戏窗口
    private JFrame f = new JFrame("五子棋游戏");

    //定义五子棋游戏棋盘对应的Canvas组件
    private class ChessBoard extends JPanel{
    
    
        //重写paint方法,实现绘画
        @Override
        public void paint(Graphics g) {
    
    
            //绘制五子棋棋盘
            g.drawImage(table,0,0,null);
            //绘制选中点的红框
            if (selectX>0 && selectY>0){
    
    
                g.drawImage(selected,selectX*RATE+X_OFFSET,selectY*RATE+Y_OFFSET,null);
            }

            //遍历数组,绘制棋子
            for (int i = 0; i < BOARD_SIZE; i++) {
    
    
                for (int j = 0; j < BOARD_SIZE; j++) {
    
    
                    //绘制黑棋

                    if (board[i][j]==1){
    
    
                        g.drawImage(black,i*RATE+X_OFFSET,j*RATE+Y_OFFSET,null);
                    }

                    //绘制白棋
                    if (board[i][j]==2){
    
    
                        g.drawImage(white,i*RATE+X_OFFSET,j*RATE+Y_OFFSET,null);
                    }
                }
            }
        }
    }

    private ChessBoard chessBoard = new ChessBoard();

    //定义变量,记录当前选中的坐标点对应的boad数组中对应的棋子索引;
    private int selectX = -1;
    private int selectY = -1;

    //定义一个变量,记录当前用户选择下的是白棋还是黑棋还是清除,清除:0,黑棋:1,白棋:2;
    private int chessCategory = 1;

    //定义Panel,放置点击按钮
    Panel p = new Panel();
    private Button whiteBtn = new Button("白棋");
    private Button blackBtn = new Button("黑棋");
    private Button clearBtn = new Button("删除");

    public void updateBtnColor(Color whiteBtnColor,Color blackBtnColor,Color clearBtnColor){
    
    
        whiteBtn.setBackground(whiteBtnColor);
        blackBtn.setBackground(blackBtnColor);
        clearBtn.setBackground(clearBtnColor);
    }

    public void init() throws Exception{
    
    

        //初始化按钮的颜色
        updateBtnColor(Color.LIGHT_GRAY,Color.GREEN,Color.LIGHT_GRAY);
        whiteBtn.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                chessCategory = 2;
                updateBtnColor(Color.GREEN,Color.LIGHT_GRAY,Color.LIGHT_GRAY);
            }
        });
        blackBtn.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                chessCategory=1;
                updateBtnColor(Color.LIGHT_GRAY,Color.GREEN,Color.LIGHT_GRAY);
            }
        });
        clearBtn.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                chessCategory=0;
                updateBtnColor(Color.LIGHT_GRAY,Color.LIGHT_GRAY,Color.GREEN);
            }
        });

        p.add(whiteBtn);
        p.add(blackBtn);
        p.add(clearBtn);
        //把Panel放入到frame底部
        f.add(p,BorderLayout.SOUTH);


        //初始化黑棋,白棋,棋盘,选中框
        table = ImageIO.read(new File("awt_demo\\board.jpg"));
        black = ImageIO.read(new File("awt_demo\\black.gif"));
        white = ImageIO.read(new File("awt_demo\\white.gif"));
        selected = ImageIO.read(new File("awt_demo\\selected.gif"));
        //初始化board数组,默认情况下,所有位置处都没有棋子
        for (int i = 0; i < BOARD_SIZE; i++) {
    
    
            for (int j = 0; j < BOARD_SIZE; j++) {
    
    
                board[i][j]=0;
            }
        }

        //设置chessBoard的最佳大小
        chessBoard.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));

        //给chessBoard注册鼠标监听器
        chessBoard.addMouseListener(new MouseAdapter() {
    
    
            //鼠标单击会触发
            @Override
            public void mouseClicked(MouseEvent e) {
    
    
                //将用户鼠标的坐标,转换成棋子的坐标
                int xPos = (e.getX()-X_OFFSET)/RATE;
                int yPos = (e.getY()-Y_OFFSET)/RATE;

                board[xPos][yPos] = chessCategory;

                //重绘chessBoard
                chessBoard.repaint();
            }

            //当鼠标退出棋盘区域后,复位选中坐标,重绘chessBoard,要保证红色选中框显示正确
            @Override
            public void mouseExited(MouseEvent e) {
    
    
                selectX=-1;
                selectY=-1;
                chessBoard.repaint();
            }
        });

        //给chessBoard注册鼠标移动监听器
        chessBoard.addMouseMotionListener(new MouseMotionAdapter() {
    
    
            //当鼠标移动时,修正selectX和selectY,重绘chessBoard,要保证红色选中框显示正确
            @Override
            public void mouseMoved(MouseEvent e) {
    
    
                //将鼠标的坐标,转换成棋子的索引
                selectX = (e.getX()-X_OFFSET)/RATE;
                selectY = (e.getY()-Y_OFFSET)/RATE;
                chessBoard.repaint();
            }
        });

        //把chessBoard添加到Frame中
        f.add(chessBoard);



        //设置frame最佳大小并可见
        f.pack();
        f.setVisible(true);

    }

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

}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_63203388/article/details/124629052