I'm making an application for creating convex polygon.
I imagined it to be so that I first set the vertices of the polygon and then create it.
I was able to make the addition of points (vertices). Now I need help connecting the dots with a line.
Here's the GUI with four points
Here's the GUI with a polygon
I'm not showing it, but the Reset button clears the drawing area.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the Swing application at a time.
A Swing application model consists of one or more plain Java getter/setter classes.
A Swing view consists of a JFrame
and one or more JPanels
.
Swing controllers are the listeners that are attached to JButtons
and drawing JPanels
.
Model
For this application, I created a PolygonModel
class. The class holds a boolean
that tells me whether or not to draw the polygon and a java.util.List
of java.awt.Point
instances. The Point
class holds an X and Y int
value.
View
All Swing applications must start with a call to the SwingUtilities
invokeLater
method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I broke up your main
method into a couple of methods. I separate the creation of the JFrame
from the creation of the JPanels
. This allows me to separate my concerns and focus on one part of the GUI at a time.
The JFrame
methods must be called in a specific order. This is the order I use for most of my Swing applications.
I changed your PadDraw
class to extend a JPanel
. I moved the MouseAdapter
code to its own class. Your drawing panel should draw. Period. Nothing else.
The paintComponent
method always starts with a call to the super.paintComponent
method. This maintains the Swing paint chain and helps to eliminate unwanted drawing artifacts.
The drawing JPanel
is cleared before every repaint. Therefore, you have to completely redraw your image each time. That's why we store the List
of Point
instances in the model.
Controller
I created three controller classes.
The PointListener
class extends MouseAdapter
. Notice how simple the mousePressed
method becomes with an application model.
The two JButtons
each have their own ActionListener
. Since they are so simple, I made each of them lambdas.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could make them public and post the code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
public class PolygonImage implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PolygonImage());
}
private final PolygonModel model;
private final PadDraw drawPad;
public PolygonImage() {
this.model = new PolygonModel();
this.drawPad = new PadDraw(this, model);
}
@Override
public void run() {
JFrame frame = new JFrame("Draw Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawPad, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel
.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
JButton buttonDrawPolygon = new JButton("Draw Polygon");
buttonDrawPolygon.addActionListener(event -> {
model.setConnectPoints(true);
repaint();
});
buttonPanel.add(buttonDrawPolygon);
JButton buttonReset = new JButton("Reset");
buttonReset.addActionListener(event -> {
model.setConnectPoints(false);
model.clearList();
repaint();
});
buttonPanel.add(buttonReset);
return buttonPanel;
}
public void repaint() {
drawPad.repaint();
}
public class PadDraw extends JPanel {
private static final long serialVersionUID = 1L;
private final PolygonModel model;
public PadDraw(PolygonImage view, PolygonModel model) {
this.model = model;
this.addMouseListener(new PointListener(view, model));
this.setPreferredSize(new Dimension(450, 300));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
// Draw points
for (Point p : model.getPoints()) {
int radius = 6;
int diameter = radius radius;
g2d.fillOval(p.x - radius, p.y - radius, diameter, diameter);
}
// Draw polygon
if (model.isConnectPoints()) {
g2d.setStroke(new BasicStroke(5));
List<Point> points = model.getPoints();
if (points.size() >= 1) {
Point old = points.get(0);
for (int index = 1; index < points.size(); index ) {
Point p = points.get(index);
g2d.drawLine(old.x, old.y, p.x, p.y);
old = p;
}
Point p = points.get(0);
g2d.drawLine(p.x, p.y, old.x, old.y);
}
}
}
}
public class PointListener extends MouseAdapter {
private final PolygonImage view;
private final PolygonModel model;
public PointListener(PolygonImage view, PolygonModel model) {
this.view = view;
this.model = model;
}
@Override
public void mousePressed(MouseEvent event) {
model.addPoint(event.getPoint());
view.repaint();
}
}
public class PolygonModel {
private boolean connectPoints;
private final List<Point> points;
public PolygonModel() {
this.points = new ArrayList<>();
this.connectPoints = false;
}
public void setConnectPoints(boolean connectPoints) {
this.connectPoints = connectPoints;
}
public boolean isConnectPoints() {
return connectPoints;
}
public void clearList() {
this.points.clear();
}
public void addPoint(Point point) {
this.points.add(point);
}
public List<Point> getPoints() {
return points;
}
}
}