简体   繁体   中英

Java paint not drawing in Swing

I'm trying to make a paint program, but I'm having trouble with drawing lines when dragging the mouse. It appears that the paint keeps on refreshing and so it only draws the current position of my mouse. I a bit new to this, so how would I be able to get all the lines to show on the JPanel when dragging the mouse? Thanks, and here is what I have:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JPanel;

public class DrawingPanel extends JPanel{

    Point start;
    Point end;
    static Color c = Color.black;
    DrawingPanel(){
        addMouseMotionListener(new ml());
        addMouseListener(new ml());
    }
    public class ml implements MouseMotionListener, MouseListener{
        public void mouseMoved(MouseEvent ev){}
        public void mousePressed(MouseEvent e){
            end = e.getPoint();
        }
        public void mouseDragged(MouseEvent e){
            start = end;
            end=e.getPoint();
            repaint();
        }
        public void mouseReleased(MouseEvent e){
            start=null;
            end=null;
        }
        public void mouseClicked(MouseEvent e){}
        public void mouseEntered(MouseEvent e){}
        public void mouseExited(MouseEvent e){}
    }
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(c);
        if(start!=null){
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(new BasicStroke(5));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.drawLine(start.x, start.y, end.x, end.y);


        }

    }
}

It appears that the paint keeps on refreshing

Yes, that's how painting works, take a look at Painting in AWT and Swing for more details

As a possible solution, you could add each new Point a List and simply draw a line between each point within the paintComponent method by iterating over the List .

You could also create a Shape and draw the lines within in and paint this shape within the paintComponent method.

Take a look at 2D Graphics for more details

Example for drawing single lines

画线

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class DrawLine {

    public static void main(String[] args) {
        new DrawLine();
    }

    public DrawLine() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Point startPoint, endPoint;

        private List<Point[]> lines;

        public TestPane() {

            lines = new ArrayList<>(25);

            MouseAdapter ma = new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent e) {
                    startPoint = e.getPoint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    endPoint = e.getPoint();
                    Point[] points = new Point[]{startPoint, endPoint};
                    lines.add(points);
                    startPoint = null;
                    endPoint = null;
                    repaint();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    endPoint = e.getPoint();
                    repaint();
                }

            };

            addMouseListener(ma);
            addMouseMotionListener(ma);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.BLACK);
            for (Point[] p : lines) {
                g2d.drawLine(p[0].x, p[0].y, p[1].x, p[1].y);
            }
            if (startPoint != null && endPoint != null) {
                g2d.setColor(Color.RED);
                g2d.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
            }
            g2d.dispose();
        }

    }

}

Example for drawing multiple, connected lines

连接线

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class DrawLine {

    public static void main(String[] args) {
        new DrawLine();
    }

    public DrawLine() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Point> points;
        private Point mousePoint;

        public TestPane() {

            points = new ArrayList<>(25);

            MouseAdapter ma = new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    points.add(e.getPoint());
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    mousePoint = e.getPoint();
                    repaint();
                }

            };

            addMouseListener(ma);
            addMouseMotionListener(ma);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.BLACK);
            Point startPoint = null;
            for (Point p : points) {
                if (startPoint == null) {
                    startPoint = p;
                } else {
                    g2d.drawLine(startPoint.x, startPoint.y, p.x, p.y);
                    startPoint = p;
                }
            }
            if (startPoint != null) {
                g2d.setColor(Color.RED);
                g2d.drawLine(startPoint.x, startPoint.y, mousePoint.x, mousePoint.y);
            }
            g2d.dispose();
        }

    }

}

You have done almost everything right. The reason for your program only showing the current mouse position is because you don't save the starting position on mousePressed. Try replacing:

public void mousePressed(MouseEvent e){
    end = e.getPoint();
}

with:

public void mousePressed(MouseEvent e){
    start = e.getPoint();
}

And also:

public void mouseDragged(MouseEvent e){
    start = end;
    end=e.getPoint();
    repaint();
}

with:

public void mouseDragged(MouseEvent e){
    end = e.getPoint();
    repaint();
}

That will get it working to draw one line. If you want more lines you can just add each finished line to a list in mouseReleased. Try adding this to your DrawingPanel class:

private ArrayList<Point> points = new ArrayList<Point>();

also replace this:

public void mouseReleased(MouseEvent e){
    start = null;
    end   = null;
}

with:

public void mouseReleased(MouseEvent e){
    points.add(start);
    points.add(end);
    start = null;
    end   = null;
}

And also replace:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    g.setColor(c);
    if(start!=null){
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(5));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.drawLine(start.x, start.y, end.x, end.y);
    }
}

with:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setColor(c);
    g2.setStroke(new BasicStroke(5));
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    //Draw all previous lines
    for (int i = 0; i < points.size(); i+=2) {
        g2.drawLine(points.get(i).x, points.get(i).y, points.get(i+1).x, points.get(i+1).y);
    }

    //Draw the current line if there is one
    if(start != null && end != null){
        g2.drawLine(start.x, start.y, end.x, end.y);
    }
}

Now we save each finished line in the list, each even index is the start of a line and each odd is the end of a line. If you want to be able to draw "curves" that looks exactly like you moved the mouse you have to change this code a bit, but at least this should give you something to work with. Just submit an additional question if you need further help.

There are multiple ways to solve your problem. @MattiasF and @MadProgrammer are correct: Swing is doing what it should be doing. Your paintComponent method is supposed to repaint the entire scene, not add onto the previous one.

The solutions suggested so far result in an application that is doing vector graphics: you are remembering the original drawing operations, and on every paint, you are executing every one of them (Java2D optimises some of it, because it will not really redraw the areas that are not currently visible on the screen, but it also takes time to figure out which areas are visible and which ones not)

The advantages are that you can scale the drawing operations perfectly if you need a bigger or smaller image. The disadvantages are that it may be slower once you have stored many drawing operations, and that you cannot (easily) do bitmap manipulations.

Another approach to this is the bitmap approach. You build up a bitmap of your drawing so far in memory, and in the paintComponent method you draw the bitmap onto the screen.

The advantage is that it's usually faster. It also allows bitmap operations, and it is also often easier to program against this model as you can just draw when you need it, rather than build op a list of drawing operations in memory. The disadvantages are that it uses more memory (until you have many drawing operations) and that you cannot perfectly scale your image up and down anymore.

To make your example work with a bitmap kept in memory, add the fields image and imageGraphics into your class, and replace your mouse listener ml as well as the paintComponent method with the code below:

private BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
private Graphics2D imageGraphics = image.createGraphics();

public class ml extends MouseAdapter implements MouseMotionListener, MouseListener {

    public void mousePressed(MouseEvent e) {
        end = e.getPoint();
    }

    public void mouseDragged(MouseEvent e) {
        start = end;
        end = e.getPoint();

        imageGraphics.setColor(c);
        imageGraphics.setStroke(new BasicStroke(5));
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        imageGraphics.drawLine(start.x, start.y, end.x, end.y);

        repaint();
    }

    public void mouseReleased(MouseEvent e) {
        start = null;
        end = null;
    }
}

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.drawImage(image, null, 0, 0);
}

You will immediately see the problem with scaling. The bitmap buffer is 500x500 pixels, and anything outside it will not be drawn. This is basically the same way that Microsoft paint works: you need to know the canvas size before you start drawing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM