zoukankan      html  css  js  c++  java
  • 画图小工具之重绘

    一、重绘:

    在画图工具中,因为画布是在顶级容器中画出来的,而顶级容器(所有的组件)也都是由系统画出来的,在改变窗体大小时,组件会自动重新绘制,而我们画的图形则会消失绘制。这时为了使我们的图形在改变窗体时也不消失,就需要用到重绘;

    方法:在该组件的重绘(paint)方法中重新绘制图形;简单来说就是将我们的画的图形存起来,然后在重绘组件的方法中重绘我们画的图形;

    具体就是将我们所画的图形当做一个对象,然后把它的属性和方法存起来;在paint方法中实现;

    在实现重绘时不需要像在初步实现图画的步骤那样,比如任意多边形,画多边形的时候是考虑了单击和双击,而在重绘则不需要考虑;

    步骤:1、定义一个Shape类;定义图形对象的属性和方法;

    2、在DrawFrame中定义一个Shape数组存储Shape类对象;

    3、把Shape数组传到LoginListener中;

    4、实例化Shape类对象并进行存储,画一个图形就存一个图形;

    5、在paint方法中改写重绘方法;

    二、具体代码:

    Shape.java

    package HuaTu;
    
    import java.awt.Color;
    import java.awt.Stroke;
    
    public class Shape {
        public int x1, x2, y1, y2;
        public Color color;
        public String type;
        public Stroke stroke;
    
        public Shape() {
    
        }
    
        public Shape(int x1, int y1, int x2, int y2, Color color, Stroke stroke, String type) {
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
            this.color = color;
            this.stroke = stroke;// 之前少了这一个,造成了变量为空的情况;
            this.type = type;
    
        }
    
    }

    LoginListener.java

    package HuaTu;
    
    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    /**
     * 1.新建一个LoginListener事件处理类,
     * 该类实现MouseListener鼠标事件接口,实现接口中的抽象方法。
     * 2.定义四个变量,在按下和释放方法中获取按下和释放的坐标值。
        3.定义Graphics画笔画布类的对象,调用绘制图形的方法来画图。
            我们的组件是画出来的,那么你要在哪一个组件上画图形,那你的画笔画布对象就从这个组件上获取。
        4.实例化DrawListener事件处理类的对象,对象名dl
        5.给事件源窗体对象添加addMouseListener()鼠标监听方法,指定事件处理类对象dl.
    
     */
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    import java.util.Random;
    
    //F3键可以看到代码
    public class LoginListener implements MouseListener, ActionListener, MouseMotionListener {
        private int c;
        private int x1, y1, x2, y2, x3, y3, x4, y4;// x, y,
        private Random rand = new Random();/*
                                             * 之前是在for循环中定义实例化的rand,增加了时间复杂度和空间复杂度,
                                             * 每次循环都会实例化一个rand变量,所以不要再循环中定义变量;
                                             */
        public String s;
    
        public Shape[] Sh;
        public int i = 0;
    
        public void setSh(Shape[] sh) {
            Sh = sh;
        }
    
        private int X1, X2, Y1, Y2;// 声明四个整数变量,用来记录按下和释放时的坐标值
    
        public int sx, sy, ex, ey;// 存储任意多边形的起始点坐标和结束点的坐标
        public int count = 0;// 记录画的是任意多边形的第几条线
    
        Graphics g;// 声明一个画笔画布类的对象名
    
        private Graphics2D g1;// 声明一个画布类的对象;Graphics2D是Graphics的一个子类。
        BasicStroke S = new BasicStroke(10);
        BasicStroke C = new BasicStroke(1);// 方法一:实例化画笔粗细。设置画笔粗细为1.
        /*
         * 没有用到对象的属性和方法就不用传过去;
         */
    
        public void SetG(Graphics gra) {
            // g = gra;// 把gra给g;
            g1 = (Graphics2D) gra;// 强制转型
    
            g1.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // 设置设置画笔抗锯齿,使线条更加平滑。
        }
    
        /**
         * 当你在事件源上发生鼠标按下动作时执行的方法。
         */
    
        public void mousePressed(MouseEvent e) {
            // 取得鼠标按下时取得的坐标;
            X1 = e.getX();
            Y1 = e.getY();
    
        }
    
        /**
         * 当你在事件源上发生鼠标释放动作时执行的方法。
         */
        public void mouseReleased(MouseEvent e) {
    
            X2 = e.getX();
            Y2 = e.getY();
    
            if (s.equals("直线")) {
                g1.setColor(Color.black);
                g1.setStroke(C);
                g1.drawLine(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(1), "直线");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
    
            } else if (s.equals("圆")) {
                g1.setColor(Color.black);
                g1.setStroke(C);
                g1.drawOval(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(1), "圆");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
    
            } else if (s.equals("矩形")) {
                g1.setColor(Color.black);
                g1.setStroke(C);
                g1.drawRect(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(1), "矩形");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
    
            } else if (s.equals("任意多边形") && count == 0) {// 判断是否画任意多边形的第一条线
                g1.setColor(Color.black);
                g1.setStroke(C);
                g1.drawLine(X1, Y1, X2, Y2);
    
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(1), "任意多边形");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
    
                // 存储第一条线的起始点
                sx = X1;
                sy = Y1;
                // 存储第一条线的结束点
                ex = X2;
                ey = Y2;
    
                count++;// 表示第一条已经画完了
            }
    
        }
    
        /**
         * 当你的鼠标进入到事件源是行时执行的方法。
         */
        public void mouseEntered(MouseEvent e) {
    
        }
    
        /**
         * .当你的鼠标离开到事件源是行时执行的方法。
         */
        public void mouseExited(MouseEvent e) {
    
        }
    
        /**
         * 当你在事件源上发生鼠标点击动作时执行的方法。(在同一个位置上按下并释放才会执行点击)
         */
    
        public void mouseClicked(MouseEvent e) {
    
            if (s.equals("任意多边形") && count != 0) {// 判断是否已经画完任意多边形的第一条线了
                // 获取点击的坐标值
                int x = e.getX();
                int y = e.getY();
                g1.setColor(Color.black);
                g1.setStroke(C);
                if (e.getClickCount() == 2) {// 判断是双击,图形要闭合
                    // 使用x,y和ex,ey画闭合的第一条线
    
                    g1.drawLine(ex, ey, x, y);
    
                    Shape shape = new Shape(ex, ey, x, y, Color.black, new BasicStroke(1), "任意多边形");
                    Sh[i++] = shape;
                    if (i == 10000) {
                        i = 0;
                    }
    
                    // 使用x,y和sx,sy画闭合图形的最后 一条线
    
                    g1.drawLine(sx, sy, x, y);
                    Shape shapes = new Shape(sx, sy, x, y, Color.black, new BasicStroke(1), "任意多边形");
                    Sh[i++] = shapes;
                    if (i == 10000) {
                        i = 0;
                    }
                    // 改变count的值,好让下一次又是重新开始新的多边形
                    count = 0;
                } else {// 判断不是双击,要接下来的线
    
                    // 根据上一条线的结束点和当前点击的坐标,来绘制直线
                    g1.drawLine(ex, ey, x, y);
    
                    Shape shape = new Shape(ex, ey, x, y, Color.black, new BasicStroke(1), "任意多边形");
                    Sh[i++] = shape;
                    if (i == 10000) {
                        i = 0;
                    }
                    // 将当前这条线的结束赋给ex,ey,作为下一条线的起始点
                    ex = x;
                    ey = y;
    
                }
    
            }
    
            else if (s.equals("图形1")) {
                g1.setColor(Color.BLUE);
                g1.setStroke(C);
    
                if (count < 4) { // 用变量控制存储四个值;
    
                    // x = e.getX();
                    // y = e.getY();
                    g1.drawLine(e.getX(), e.getY(), e.getX(), e.getY());
                    /*
                     * 之前是写的四个并列的if语句,所以每执行一次都会调用所有的if语句,同样的增加了程序运行的时间,增加了时间复杂度;
                     * 所有要加上else if;来减少程序运行的时间。所有学会减少不必要的语句;
                     */
                    if (count == 0) {// 存储第一个值A
                        x1 = e.getX();// 之前是写的x1=x;这样增加了空间复杂度,时间复杂度;
                        y1 = e.getY();// 改成x1=e.getY();可以少定义一个变量;减少占用的空间;
                    } else if (count == 1) {// 存储第二个值B
                        x2 = e.getX();
                        y2 = e.getY();
                    } else if (count == 2) {// 存储第三个值C
                        x3 = e.getX();
                        y3 = e.getY();
                    } else if (count == 3) {// 存储第四个值P;
                        x4 = e.getX();
                        y4 = e.getY();
                    }
                    count++;
                }
                if (count >= 4) {
                    for (c = 0; c <= 10000; c++) {
                        int s = rand.nextInt(3);// 这个不能放在for循环外边;随机数的大小;0到3;取值取0,1,2;
                        if (s == 0) {// 当随机数取0时,色子面为A;
                            x4 = (x1 + x4) / 2;
                            y4 = (y1 + y4) / 2;
                        } else if (s == 1) {// 当随机数取1时,色子面为B;
                            x4 = (x2 + x4) / 2;
                            y4 = (y2 + y4) / 2;
                        } else {// 当随机数取2时,色子面为C。
                            x4 = (x3 + x4) / 2;
                            y4 = (y3 + y4) / 2;
                        }
    
                        g1.drawLine(x4, y4, x4, y4);
    
                    }
                    count = 0;// count值变为0,然后开始下一个图形。不然只能画一个图形;
    
                }
    
            }
    
            else if (s.equals("图形2")) {
    
                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                double a = -2, b = -2, c = -1.2, d = 2;
                for (int n = 0; n <= 50000; n++) {
                    g1.setColor(Color.green);
                    g1.setStroke(C);
    
                    /*
                     * 先算出值,然后再来画图; 画图时要注意,因为求出的值很小,所以需要扩大。
                     * 而强制转型时应该是求出的值整体转也就是应该加上括号(x2 * 100 + 350), int m = (int)x2 *
                     * 100 + 350 ,不加括号时是,X2转型之后,再乘以100,加上350,
                     */
                    x2 = (Math.sin(a * y1) - Math.cos(b * x1));
                    y2 = (Math.sin(c * x1) - Math.cos(d * y1));
                    // int m = (int) (x2 * 100 + 350);(x2 * 100 + 350)整体强制转型;
                    // int s = (int) (y2 * 100 + 350);这时是X2乘以100加上350得到的数再进行转型;
                    // g.drawLine(m, s, m, s);
                    g1.drawLine((int) (x2 * 100 + 300), (int) (y2 * 100 + 300), (int) (x2 * 100 + 300),
                            (int) (y2 * 100 + 300));
                    // 强制转型时,要注意加上括号,整体转型
                    x1 = x2;
                    y1 = y2;
    
                }
    
            } else if (s.equals("图形3")) {
                g1.setColor(Color.MAGENTA);
                g1.setStroke(C);
    
                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                double a = 1.40, b = 1.56, c = 1.40, d = -6.56;
                for (int n = 0; n <= 60000; n++) {
                    x2 = d * Math.sin(a * x1) - Math.sin(b * y1);
                    y2 = c * Math.cos(a * x1) + Math.cos(b * y1);
                    g1.drawLine((int) (x2 * 50 + 550), (int) (y2 * 50 + 300), (int) (x2 * 50 + 550), (int) (y2 * 50 + 300));
                    // 乘以的数,控制大小,整体相加改变位置;
                    x1 = x2;
                    y1 = y2;
    
                }
    
            } 
            /**
             * signum(double d):返回参数的符号函数;如果参数为 0,则返回 0;如果参数大于 0,则返回 1.0;如果参数小于 0,则返回 -1.0。 
               abs(double a):返回 double 值的绝对值。
               sqrt(double a):返回正确舍入的 double 值的正平方根。
                     */
    
            else if (s.equals("图形4")) {
                g1.setColor(Color.BLUE);
                g1.setStroke(C);
            
                double a = 0.4, b = 1, c = 0;
                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    
                for (int n = 0; n <= 60000; n++) {
                    x2 = y1 - Math.signum(x1) * Math.sqrt(Math.abs(b * x1 - c));
                    y2 = a - x1;
                    g1.drawLine((int) (x2 * 150 + 500), (int) (y2 * 150 + 250), (int) (x2 * 150 + 500),(int) (y2 * 150 + 250));
                    // 乘以的数,控制大小,整体相加改变位置;
                    x1 = x2;
                    y1 = y2;
    
                }
            } else if (s.equals("图形5")) {
                g1.setColor(Color.BLUE);
                g1.setStroke(C);
                int a = 1, b = 4, c = 60;
                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    
                for (int n = 0; n <= 60000; n++) {
                    x2 = y1 - Math.signum(x1) * Math.sqrt(Math.abs(b * x1 - c));
                    y2 = a - x1;
                    g1.drawLine((int) (x2 *3 + 600), (int) (y2*3  + 400), (int) (x2*3  + 600),(int) (y2*3 + 400));//注意画线的值;
                    // 乘以的数,控制大小,整体相加改变位置;该图形的乘数不能太大,最好控制在0-5之间
                    x1 = x2;
                    y1 = y2;
    
                }
    
            }
        }
    
    
        public void actionPerformed(ActionEvent e) {
            // 得到按钮上的文字;
            s = e.getActionCommand();
    
            // getActionCommand()的方法是ActionEvent的,也就是e的。
    
        }
    
        @Override // 当鼠标拖动时,在事件源上按下鼠标按键然后拖动鼠标时执行的方法。
        public void mouseDragged(MouseEvent e) {
            if (s.equals("画笔")) {
                /**
                 * 因为在拖动过程当中会不断取点,如果起点固定则画出来的都是在一个点开始的。
                 * 以上一个拖动取到的坐标作为下一段直线的起点,从而画出曲线。
                 */
                g1.setColor(Color.black);
                g1.setStroke(C);
                X2 = e.getX();
                Y2 = e.getY();
                g1.drawLine(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(1), "任意多边形");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
    
                X1 = X2;// 转换坐标。
                Y1 = Y2;
    
            }
            if (s.equals("刷子")) {
                g1.setColor(Color.black);
                g1.setStroke(S);// 要进行转换画笔,再画;设置线条的粗细。
                X2 = e.getX();
                Y2 = e.getY();
                g1.drawLine(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.black, new BasicStroke(10), "刷子");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
                X1 = X2;
                Y1 = Y2;
    
            }
            if (s.equals("橡皮擦")) {
                g1.setStroke(S);
                g1.setColor(Color.WHITE); // 设置线条的颜色为白色来绘制橡皮擦。
                X2 = e.getX();
                Y2 = e.getY();
                g1.drawLine(X1, Y1, X2, Y2);
                Shape shape = new Shape(X1, Y1, X2, Y2, Color.WHITE, new BasicStroke(10), "橡皮擦");
                Sh[i++] = shape;
                if (i == 10000) {
                    i = 0;
                }
                X1 = X2;
                Y1 = Y2;
                // g1.setColor(Color.black);// 在画完之后转换回一般的格式就可以不用在每个都设置。粗细也是。
                // g1.setStroke(new BasicStroke(1));// 设置粗细法二。
    
            }
            if (s.equals("喷枪")) {
                g1.setStroke(C);
                g1.setColor(Color.black);
                X2 = e.getX();
                Y2 = e.getY();
    
                Random rand = new Random();// 实例化一个随机数类的对象
                int size = rand.nextInt(50) + 20;// 随机决定要画的点数,size的范围在20到69之间。没有“+20”时,只有0到49之间
                for (int i = 0; i < size; i++) {
                    int x = rand.nextInt(8);// 在0到7之间随机取点。
                    int y = rand.nextInt(8);
                    g1.drawLine(X2 + x, Y2 + y, X2 + x, Y2 + y);// 有随机数来改变坐标,在X2,Y2附件画点。
    
                    Shape shape = new Shape(X2 + x, Y2 + y, X2 + x, Y2 + y, Color.black, new BasicStroke(1), "喷枪");
                    Sh[this.i++] = shape;
                    if (this.i == 10000) {
                        this.i = 0;// 注意区分变量;
                    }
                }
    
                // g1.setColor(Color.black);
                // g1.setStroke(C);
    
            }
    
        }
    
        @Override
        /*
         * 当在事件源上移动鼠标时执行的方法。
         */
        public void mouseMoved(MouseEvent e) {
    
        }
    
    }

    DrawFrame.java

    package HuaTu;
    
    import java.awt.Color;
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    
    /**
     * 定义一个画图界面类,该类继承自JFrame窗体类.
     */
    public class DrawFrame extends JFrame {
        /**
         * 程序入口主函数
         */
    
        public Shape[] Sh = new Shape[10000];// 两个方法都要用到这个属性时,属性不能定义在一个方法中;应该定义在一个类里面;
        // (刚刚是定义在了public void initUN(){}方法中;但是在paint方法中用的时候就不能用了;)
    
        public static void main(String[] args) {
            // 实例化窗体类的对象,调用初始化界面的方法
            DrawFrame dl = new DrawFrame();
            dl.initUN();
            // dl.setBackground(Color.WHITE);这个是错误的不能改变窗体的颜色。
    
        }
    
        public void initUN() {
            setTitle("简单画图");
            setSize(1000, 600);
            setDefaultCloseOperation(3);
            setLocationRelativeTo(null);
            setLayout(new FlowLayout());
            JButton butLine = new JButton("直线");
            this.add(butLine);
            JButton butSquare = new JButton("矩形");
            add(butSquare);
            JButton butRotundity = new JButton("圆");
            add(butRotundity);
            JButton butS = new JButton("任意多边形");
            add(butS);
            JButton but = new JButton("画笔");
            add(but);
            JButton butt = new JButton("刷子");
            add(butt);
            JButton but1 = new JButton("橡皮擦");
            add(but1);
            JButton but2 = new JButton("喷枪");
            add(but2);
            JButton but3=new JButton("图形1");
            add(but3);
            JButton but4=new JButton("图形2");
            add(but4);
            JButton but5=new JButton("图形3");
            add(but5);
            JButton but6=new JButton("图形4");
            add(but6);
            JButton but7=new JButton("图形5");
            add(but7);
            
            getContentPane().setBackground(Color.WHITE);// getContenPane为 改变窗口的颜色。
    
            /**
             * //实例化一个流失布局类的对象,布局类是针对容器的,容器上要填多个组件,那么必须要设置排列对齐方式;
             * java.awt.FlowLayout fl=new java.awt.FlowLayout();
             * jf.setLayout(fl);//设置窗体的布局方式为流式布局 //定义一个ImageIcon类,该类用来读取一个磁盘的图片文文件。
             */
    
            setVisible(true);
    
            Graphics g = this.getGraphics();// 获取窗体上画笔画布对象(注意:必须要在窗体可见之后才能获取画笔画布对象,否则获取的是null)
    
            // 4.在DrawFrame类中实例化LoginListener事件处理类的对象dn;
            LoginListener dn = new LoginListener();
            addMouseListener(dn);// 5.给事件源窗体对象添加addMouseListener()鼠标监听方法,指定事件处理类对象dl.
            addMouseMotionListener(dn);
            butLine.addActionListener(dn);// 要加监听方法才能获取数值。
            butSquare.addActionListener(dn);// 要加监听方法才能获取数值。
            butRotundity.addActionListener(dn);// 要加监听方法才能获取数值。
            butS.addActionListener(dn);// 要加监听方法才能获取数值。
            but1.addActionListener(dn);
    
            but.addActionListener(dn);
            butt.addActionListener(dn);
            but2.addActionListener(dn);
            but3.addActionListener(dn);
            but4.addActionListener(dn);
            but5.addActionListener(dn);
            but6.addActionListener(dn);
            but7.addActionListener(dn);
            dn.SetG(g);
    
            dn.setSh(Sh);// 对象.方法名(参数);
    
        }
    
        // 方法不能在另一个方法中;
        //画一个图形存一个图形,
        public void paint(Graphics g) {
            super.paint(g);
            /*
             * 输出数组中的值;然后画出图形;
             */
    
            for (int i = 0; i <10000; i++) {
                Shape shape = Sh[i];
            
                if (null != shape) {
                    ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    // 这个变量不用存,直接设置就可以了;
                    // 设置设置画笔抗锯齿,使线条更加平滑。
    
                    g.setColor(shape.color);
                    ((Graphics2D) g).setStroke(shape.stroke);// 如果之前画的时候没有设置这个粗细,那么在调用这里的这个就是空的;
                    System.out.println(shape.type);
                    if (shape.type.equals("直线") || shape.type.equals( "任意多边形")||shape.type.equals("画笔")||shape.type.equals( "刷子")||shape.type.equals( "喷枪")||shape.type.equals( "橡皮擦")) {
                        g.drawLine(shape.x1, shape.y1, shape.x2, shape.y2);
                    } else if (shape.type.equals("圆")) {
                        g.drawOval(shape.x1, shape.y1, shape.x2, shape.y2);
                    } else if (shape.type.equals("矩形")) {
                        g.drawRect(shape.x1, shape.y1, shape.x2, shape.y2);
                    }
    //else if 就相当于break跳出,不会进入死循环;
    //
    //                }
                }
    
            }
    
        }
    }

     注意点:

    1. 两个方法都要用到这个属性时,属性不能定义在一个方法中;应该定义在一个类里面;
    2.注意在方法中,设置的参数不能为空;
  • 相关阅读:
    ASP.NET动态加载用户控件的页面生成过程
    简单的flash与asp.net通信(LoadVars类)
    转 推荐两本FLASH RIA应用开发方面的书籍
    关于代码加密解密保护
    转 利用 SharpZipLib方便地压缩和解压缩文件
    在C#中应用哈希表(Hashtable)
    C#中解析并运行一个本地命令行
    About Windows Live Writer
    安装sql server 2008,提示要删除SQL Server 2005 Express 工具,可我根本没装
    [转] C#中的null
  • 原文地址:https://www.cnblogs.com/hesi/p/5595045.html
Copyright © 2011-2022 走看看