7 绘图

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

7.1 组件绘图原理

之前我们已经学习了很多组件,例如Button、Frame、Checkbox等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图来完成的。

        在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制自身图形呢?

在Component类中,提供了下列三个方法来完成组件图形的绘制与刷新:

paint(Graphics g)

绘制组件的外观

update(Graphics g)

内部调用paint方法,刷新组件外观

repaint()

调用update方法,刷新组件外观

组件绘制图形流程图:

java中用awt包中画画会闪烁怎么解决 java awt graphics_java

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

7.2 Graphics对象的使用

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

画图的步骤:

        1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;

        2.在paint方法内部,真正开始画图之前调用Graphics对象的setColor(),setFont()等方法设置画笔颜色,字体等属性;

        3.调用Graphics画笔的drawXxx()方法开始画图

下面列出Graphics类中常用的一些方法:

setColor(Color c)

颜色设置

setFont(Font font)

字体设置

drawLine()

绘制直线

drawRect()

绘制矩形

drawRoundRect()

绘制圆角矩形

drawOval()

绘制椭圆形

drawPolygon()

绘制多边形

drawArc()

绘制圆弧

drawPolyline()

绘制折线

fillRect()

填充矩形区域

fillRoundRect()

填充圆角矩形区域

fillOval()

填充椭圆区域

fillPolygon()

填充多边形区域

fillArc()

填充圆弧对应的扇形区域

drawImage()

绘制位图

案例:

        使用AWT绘图API,完成下图效果:

java中用awt包中画画会闪烁怎么解决 java awt graphics_ide_02

 演示代码:

public class SimpleDraw {

    private final String RECT_SHAPE="rect";
    private final String OVAL_SHAPE="oval";

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

    Button btnRect = new Button("绘制矩形");
    Button btnOval = new Button("绘制椭圆");

    //定义一个变量,记录当前要绘制椭圆还是矩形
    private String shape = "";

    //自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;
    private class MyCanvas extends Canvas{
        @Override
        public void paint(Graphics g) {
            //绘制不同的图形

            if (shape.equals(RECT_SHAPE)){
                //绘制矩形
                g.setColor(Color.BLACK);//设置当前画笔的颜色为黑色
                g.drawRect(100,10,150,100);

            }else if(shape.equals(OVAL_SHAPE)){
                //绘制椭圆
                g.setColor(Color.RED);
                g.drawOval(100,10,150,100);
            }

        }
    }

    //创建自定义的画布对象
    MyCanvas drawArea = new MyCanvas();


    public void init(){
        //组装视图
        btnRect.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //修改标记的值为rect
                shape = RECT_SHAPE;
                drawArea.repaint();
            }
        });

        btnOval.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //修改标记的值为oval
                shape = OVAL_SHAPE;
                drawArea.repaint();
            }
        });

        //创建Panel,承载按钮
        Panel p = new Panel();
        p.add(btnRect);
        p.add(btnOval);

        frame.add(p,BorderLayout.SOUTH);

        //drawArea的大小需要设置
        drawArea.setPreferredSize(new Dimension(300,300));
        frame.add(drawArea);

        //设置WindowListener,监听用户点击X的动作,如果点击X,则关闭窗口
        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 SimpleDraw().init();
    }

}

7.3 处理位图

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

位图使用步骤:

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

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

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

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

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

案例:

        通过BufferedImage实现一个简单的手绘程序:通过鼠标可以在窗口中画图,右键鼠标可选择画笔颜色。

java中用awt包中画画会闪烁怎么解决 java awt graphics_ide_03

代码如下: 

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

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

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

    //定义一个变量,记录当前画笔的颜色
    private Color forceColor = Color.BLACK;

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

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

    //自定义一个类,继承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) {
                String actionCommand = e.getActionCommand();
                switch (actionCommand) {
                    case "红色":
                        forceColor = Color.RED;
                        break;
                    case "绿色":
                        forceColor = Color.GREEN;
                        break;
                    case "蓝色":
                        forceColor = Color.BLUE;
                        break;
                }
            }
        };

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

        colorMenu.add(redItem);
        colorMenu.add(greenItem);
        colorMenu.add(blueItem);

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

        drawArea.addMouseListener(new MouseAdapter() {//给右键显示菜单提供监听事件
            @Override
            public void mouseReleased(MouseEvent e) {//当鼠标键抬起时被调用
                boolean popupTrigger = e.isPopupTrigger();
                if (popupTrigger) {
                    colorMenu.show(drawArea, e.getX(), e.getY());
                }

                //重置preX和preY
                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);
                    //画线条  需要两组坐标,分别代表线条的起点和终点  e.getX(),e.getY()可以获取坐标()
                    g.drawLine(preX, preY, e.getX(), e.getY());
                }

                //修正preX和preY的值
                preX = e.getX();
                preY = e.getY();

                //重绘组件
                drawArea.repaint();
            }
        });

        drawArea.setPreferredSize(new Dimension(AREA_WIDTH,AREA_HEIGHT));
        frame.add(drawArea);
        //设置WindowListener,监听用户点击X的动作,如果点击X,则关闭窗口
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //停止当前程序
                System.exit(0);
            }
        });
        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }

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

7.4 ImageIO的使用

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

static BufferedImage read(File input)

读取本地磁盘图片文件(所传参数为一个file对象)

static BufferedImage read(InputStream input)

读取本地磁盘图片文件(所传参数为输入流)

static boolean write(RenderedImage im,

String formatName,File output)

往磁盘中输出图片文件

案例:

        编写图片查看程序,支持另存操作

 代码演示:

public class ReadAndSaveImage {

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

    MenuBar menuBar = new MenuBar();
    Menu menu = new Menu("文件");
    MenuItem open = new MenuItem("打开");
    MenuItem save = new MenuItem("另存为");

    //声明BufferedImage对象,记录本地存取到内存中的图片
    BufferedImage image;
    private class MyCanvas extends Canvas{
        @Override
        public void paint(Graphics g) {
            g.drawImage(image,0,0,null);
        }
    }
    MyCanvas drawArea = new MyCanvas();

    public void init() throws Exception{
        //组装视图

        open.addActionListener(e->{
            //打开一个文件对话框
            FileDialog fileDialog = new FileDialog(frame,"打开图片",FileDialog.LOAD);
            fileDialog.setVisible(true);

            //获取用户选择的图片路径以及名称
            String dir = fileDialog.getDirectory();
            String fileName = fileDialog.getFile();

            try {
                image = ImageIO.read(new File(dir,fileName));
                drawArea.repaint();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        });

        save.addActionListener(e->{
            //展示一个文件对话框
            FileDialog fileDialog = new FileDialog(frame,"保存图片",FileDialog.SAVE);
            fileDialog.setVisible(true);

            //获取用户设置的保存路径以及文件名称
            String dir = fileDialog.getDirectory();
            String fileName = fileDialog.getFile();

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

        menu.add(open);
        menu.add(save);

        menuBar.add(menu);

        //把菜单条放入到窗口中
        frame.setMenuBar(menuBar);
        frame.add(drawArea);

        frame.setBounds(200,200,740,508);

        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();
    }
}