Draw a line in a JPanel with button click in Java

It may be easier to draw lines using the following approach:

  1. click to mark the first endpoint
  2. drag to show the line in progress
  3. release to mark the second endpoint

This related example may offer some additional guidance.

Addendum

  1. The example below implements the outline above.
  2. I've update the example to show how to use a panel of buttons to affect the drawing.
  3. See also this related example that uses the Action interface with key bindings.
  4. I've updated this example to use Key Bindings.

LinePanel.java

import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 * @see https://stackoverflow.com/questions/6991648
 * @see https://stackoverflow.com/questions/6887296
 * @see https://stackoverflow.com/questions/5797965
 */
public class LinePanel extends JPanel {

    private MouseHandler mouseHandler = new MouseHandler();
    private Point p1 = new Point(100, 100);
    private Point p2 = new Point(540, 380);
    private boolean drawing;

    public LinePanel() {
        this.setPreferredSize(new Dimension(640, 480));
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.blue);
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(8,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
        g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            drawing = true;
            p1 = e.getPoint();
            p2 = p1;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            drawing = false;
            p2 = e.getPoint();
            repaint();
        }

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

    private class ControlPanel extends JPanel {

        private static final int DELTA = 10;

        public ControlPanel() {
            this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
            this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
            this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
            this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
        }

        private class MoveButton extends JButton {

            KeyStroke k;
            int dx, dy;

            public MoveButton(String name, int code,
                    final int dx, final int dy) {
                super(name);
                this.k = KeyStroke.getKeyStroke(code, 0);
                this.dx = dx;
                this.dy = dy;
                this.setAction(new AbstractAction(this.getText()) {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        LinePanel.this.p1.translate(dx, dy);
                        LinePanel.this.p2.translate(dx, dy);
                        LinePanel.this.repaint();
                    }
                });
                ControlPanel.this.getInputMap(WHEN_IN_FOCUSED_WINDOW)
                    .put(k, k.toString());
                ControlPanel.this.getActionMap()
                    .put(k.toString(), new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        MoveButton.this.doClick();
                    }
                });
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("LinePanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.add(new ControlPanel(), BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new LinePanel().display();
            }
        });
    }
}

Is this going to work like an etch-a-sketch? Then you need to track the current position of the point.

   Point current = new Point(0, 0); //for example.

Then when the user clicks the buttons you can simply increment or decrement x and y accordingly.

On left arrow:

   current.setX(current.getX() - INC);

where INC could be a variable that specifies the length of the distance to draw the line. Maybe 5? Always set a second point p1 to the previous location though.

It is always easier to create a class that extends Canvas or JPanel to draw on rather than draweing directly on the JFrame.

e.g.

public class Circuit extends JFrame {
  Point p1, current;

  JPanel drawPanel;

  //your other declarations

  public Circuit(){
         super();
         drawPanel = new DrawPanel();
         p1 = new Point(0, 0);
         current = new Point(0, 0);
         add(drawPanel, BorderLayout.CENTER);
         //your other code
  }

  class DrawingPanel extends JPanel{

        public void paintComponent(Graphics g){
             g.drawLine(p1.getX(), p1.getY(), current.getX(), current.getY());
        }
  }


   //the rest of your code.
}

There is a simple answer for triggering graphics: e.g. The following code can be placed inside a click event and used for drawing a few simple objects on a jPanel. jPanel1 in this case was situated on one side of a tabbed jPanel7 and next to the triggering button. To do this in netbeans GUI, the code was placed inside the button action event. Once the usual errors appeared for not having the proper imports, right click on the code and click on "fix imports". Bingo, all is well :-) Warning: the setBackground command for the panel will override the graphics object. If you set the background color without using the graphics object, you will not see your objects!

Graphics g = jPanel1.getGraphics();  
g.setColor(Color.blue);
    g.drawLine( 0, 50,  20, 50);
    g.setColor(Color.white); 
    g.fillRect(40, 50,  20, 20); 
    g.setColor(Color.blue); 
    g.drawRect(40, 50,  20, 20); 
    g.drawOval(80, 50,  20, 20); 
    g.setColor(Color.green); 
    g.fillOval(80, 50,  18, 18);   

This restores your faith in true love :-) The difficulty here is that any change or repaint will erase your effort. This approach is specifically discouraged by the Java founders. But in the current rendition of Netbeans Swing, where extending the jPanel is made difficult by locking code changes, this approach could be your only short term solution. A simple persistent graphic extension for the jPanel would be a most welcome addition to the current Netbeans Swing environment, a graphics panel. This would allow you to drag and drop the graphics panel and then get on with the event driven use of that panel. 40 other IDE's already have this, it seems Java has been slow to add this feature.