Draw a Line in a Jpanel With Button Click in Java

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

Draw Line when Button is pressed Swing

A number of issues jump out at me immediately:

  1. You should be using paintComponent, not paintComponents (note the s at the end), you're too high up in the paint chain. There's also no need for either method to be public, no one outside the class should be calling it.
  2. Pane provides no sizing hints, so it's "default" size will be 0x0

Instead, it should look more like...

public class Pane extends JPanel {

public Dimension getPreferredSize() {
return new Dimension(100, 40);
}

protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawLine(0,20,100,20);
}
}

When adding components, Swing is lazy. It won't run a layout/paint pass until it has to or you ask it to. This is an optimisation, as you may add many components before needing to perform a layout pass.

To request a layout pass, call revalidate on the top level container you've updated. As a general rule of thumb, if you call revalidate, you should also call repaint to request a new paint pass as well.

public class Fenetre extends JFrame {

public Fenetre(){
super("Test");
init();
//pack();
setSize(200,200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}

private void init() {
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());

JButton button = new JButton("Draw line");

button.addActionListener((e)->{
Pane s = new Pane();
panel.add(s);
panel.revalidate();
panel.repaint();
//s.repaint();
});

panel.setBackground(new Color(149,222,205));

add(button,BorderLayout.SOUTH);
add(panel,BorderLayout.CENTER);
}

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

}

That should at least get your panel to show up now

Java paint line in existing JPanel with components

What you should do is:

  1. Override paintComponent() instead of paint().
  2. First line of paintComponent should call the parent constructor.
  3. Then draw your line.

So it should be something along the lines of:

public void paintComponent(Graphics g) {
super.paintComponent(g); // this will paint your components
g.drawLine(100,100,400,400); // this will paint your line
}

For more information you should check out the Custom Painting Tutorial.

Draw Clickable Lines Between Buttons Swing

Okay, very basic, but the idea is to paint the lines manually between the objects.

In this case, I created a simple Connection object which maintained the relationships between buttons. I then simply used the paintComponent method of the JPanel to paint the actual lines.

I'll leave you to devise a better path generation method ;)

Path

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

public Test() {
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 JButton[] buttons = new JButton[]{
new JButton("1"),
new JButton("2"),
new JButton("3"),
new JButton("4"),};

private List<Connection> connections;

public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(10, 10, 10, 10);
add(buttons[0], gbc);

gbc.gridx = 2;
gbc.gridy = 0;
add(buttons[1], gbc);

gbc.gridx = 1;
gbc.gridy = 1;
add(buttons[2], gbc);

gbc.gridx = 2;
gbc.gridy = 2;
add(buttons[3], gbc);

connections = new ArrayList<Connection>(25);
connections.add(new Connection(buttons[0], buttons[1]));
connections.add(new Connection(buttons[0], buttons[2]));
connections.add(new Connection(buttons[0], buttons[3]));

connections.add(new Connection(buttons[1], buttons[3]));
connections.add(new Connection(buttons[2], buttons[3]));
}

@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.RED);
for (Connection connection : connections) {
JButton source = connection.getSource();
JButton dest = connection.getDestination();

if (source.getX() == dest.getX()) {
// Same column...
g2d.drawLine(source.getX() + source.getWidth() / 2, source.getY(),
dest.getX() + source.getWidth() / 2, dest.getY());
} else if (source.getY() == dest.getY()) {
// Same row...
g2d.drawLine(source.getX(), source.getY() + source.getHeight() / 2,
dest.getX(), dest.getY() + dest.getHeight() / 2);
} else {

Path2D path = new Path2D.Double();
path.moveTo(horizontalCenter(source), verticalCenter(source));
path.curveTo(horizontalCenter(source), verticalCenter(dest),
horizontalCenter(source), verticalCenter(dest),
horizontalCenter(dest), verticalCenter(dest));
g2d.draw(path);

}
}
g2d.dispose();
}

protected double horizontalCenter(JComponent bounds) {

return bounds.getX() + bounds.getWidth() / 2d;

}

protected double verticalCenter(JComponent bounds) {

return bounds.getY() + bounds.getHeight() / 2d;

}

protected boolean hasIntersection(Line2D line, JComponent... exclude) {
List<JComponent> toExclude = Arrays.asList(exclude);
boolean intersects = false;
for (Component comp : getComponents()) {
if (!toExclude.contains(comp)) {
if (line.intersects(comp.getBounds())) {
System.out.println(line.getP1() + "-" + line.getP2() + " intersets with " + ((JButton)comp).getText() + "; " + comp.getBounds());
intersects = true;
break;
}
}
}
return intersects;
}

protected Line2D lineDownTo(JComponent from, JComponent to) {
return new Line2D.Double(horizontalCenter(from), from.getY(), horizontalCenter(from), verticalCenter(to));
}

protected Line2D lineAcrossTo(JComponent from, JComponent to) {
return new Line2D.Double(from.getX(), verticalCenter(from), horizontalCenter(to), verticalCenter(from));
}

protected Point2D centerOf(Rectangle bounds) {
return new Point2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
}

protected boolean canGoDownTo(Point2D startPoint, Point2D endPoint, JComponent to, JComponent from) {
Point2D targetPoint = new Point2D.Double(startPoint.getX(), endPoint.getY());
return !hasIntersection(new Line2D.Double(startPoint, targetPoint), to, from);
}

public class Connection {

private final JButton source;
private final JButton destination;

public Connection(JButton source, JButton destination) {
this.source = source;
this.destination = destination;
}

public JButton getSource() {
return source;
}

public JButton getDestination() {
return destination;
}

}

}

}

Draw the line on the JPanel when dragging the mouse

..draw 2 lines

That seems like the crux of the matter in this question.

Keep a collection of lines in an expandable list (e.g. ArrayList) when clicking/dragging, add a new line to the list and call repaint(). In paintComponent(Graphics), iterate the collection and draw each line.

BTW - I am guessing you have not minimized and restored your window while testing this. Your lines (beautiful or ugly) would disappear!


..they disappeared. What's the reason?

The methods paint() and paintComponent() are called whenever the GUI needs to redraw. They might be invoked after another window appears in front of the app., then it is brought back to the front. Another time is after being restored from minimized.

The options to retain the lines include:

  • Store the locations of the line(s) and redraw all of them whenever asked (as described above). This can work for most purposes. Even if there are hundreds of lines, the GUI will redraw them in 'the blink of an eye'.
  • Draw each line to a BufferedImage and put the image in (an ImageIcon in) a JLabel. This approach works well if the drawing area is of a fixed size & nothing is ever removed, and can accommodate ..millions of lines, arcs, semi-transparent areas, smaller images, text.. Using an image as a rendering surface, you would no longer need the ArrayList, since all you do is add a new line to the image, and repaint the label to see the new line and all previous lines.

..the line is not the straight line.

That is because of the 'rendering hints' used when drawing the line. A screen made of aligned rows of pixels can only make vertical or horizontal lines perfectly. To give the 'illusion' of a straight & continuous line at any other angle, requires a technique known as dithering. Read the opening sections of Graphics2D for more explanation and description of the RenderingHints.

.drawLine on event (button click) Jpanel/Jbutton/JTabbedPane

Your question didn't make sense to me in your last posting and it still doesn't make sense to me in this posting.

You still haven't posted a SSCCE that attempts to demonstrate what you want to do.

If you have a "login panel", typically that is done by creating a modal JDialog.

If you are trying to draw a diagonal across the top of all components in the frame, then you would need to use a Glass Pane or a Layered Pane.

Read the section from the Swing tutorial on How to Use Root Panes for examples and more detailed information.

Draw Line in a JLabel and add as JPanel in a JFrame does not function

MyFrame has no preferred size until pack() interrogates the preferred sizes of the enclosed components. Give MyLine a preferred size to see the effect. See also Initial Threads.

image

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Bsp {

public static void main(String[] args) {
JFrame frame = new MyFrame();
}
}

class MyFrame extends JFrame {

private class MyLine extends JLabel {

int width;

public MyLine(int width) {
this.width = width;
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.drawLine(0, 0, getWidth(), getHeight());
g.dispose();
}

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

}

public MyFrame() {

JLabel label = new MyLine(32);
System.out.println(getWidth());
JPanel panel = new JPanel(new GridBagLayout());

setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(3, 1));

panel.add(label);

add(new JButton("A"));
add(new JButton("B"));
add(panel);
pack();
setVisible(true);

}
}


Related Topics



Leave a reply



Submit