I am doing a GUI that is supposed to work like a Paint application. My current problem is adding proper functionality to my draw line and draw rectangle buttons. They currently don't work as I expected them to work. Help will be greatly appreciated.
I searched many code snippets on learning how to draw shapes but none of them show how to make them work based on if they are activated based on the buttons, and how to alternate from drawing a line to drawing rectangles.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class paintGUI extends JComponent {
// Image in which we're going to draw
private Image image;
// Graphics2D object ==> used to draw on
private Graphics2D g2;
// Mouse coordinates
private int currentX, currentY, oldX, oldY;
public paintGUI() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
// save coord x,y when mouse is pressed
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
// coord x,y when drag mouse
currentX = e.getX();
currentY = e.getY();
if (g2 != null) {
// draw line if g2 context not null
//g2.drawLine(oldX, oldY, currentX, currentY);
//Need to implement these to their button
//g2.drawRect(oldX, oldY, currentX, currentY);
//g2.fillRect(oldX, oldY, currentX, currentY);
// refresh draw area to repaint
repaint();
// store current coords x,y as olds x,y
oldX = currentX;
oldY = currentY;
}
}
});
}
@Override
protected void paintComponent(Graphics g) {
if (image == null) {
// image to draw null ==> we create
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
// clear draw area
clear();
}
g.drawImage(image, 0, 0, null);
}
// now we create exposed methods
public void clear() {
g2.setPaint(Color.white);
// draw white on entire draw area to clear
g2.fillRect(0, 0, getSize().width, getSize().height);
g2.setPaint(Color.black);
repaint();
}
public void thin() {
g2.setStroke(new BasicStroke(3));
}
public void thick() {
g2.setStroke(new BasicStroke(10));
}
public void red() {
// apply red color on g2 context
g2.setPaint(Color.red);
}
public void black() {
g2.setPaint(Color.black);
}
public void magenta() {
g2.setPaint(Color.magenta);
}
public void drawLine() {
g2.drawLine(oldX, oldY, currentX, currentY);
}
public void drawRectangle() {
g2.drawRect(oldX, oldY, currentX, currentY);
g2.fillRect(oldX, oldY, currentX, currentY);
}
}
class GUIPaint {
JButton clearBtn, blackBtn, redBtn, magentaBtn, filledRectangleBtn, lineBtn, thinBtn, thickBtn;
paintGUI paintGUI;
ActionListener actionListener = e -> {
if (e.getSource() == clearBtn) {
paintGUI.clear();
} else if (e.getSource() == thinBtn) {
paintGUI.thin();
} else if (e.getSource() == thickBtn) {
paintGUI.thick();
} else if (e.getSource() == blackBtn) {
paintGUI.black();
} else if (e.getSource() == redBtn) {
paintGUI.red();
} else if (e.getSource() == magentaBtn) {
paintGUI.magenta();
} else if (e.getSource() == filledRectangleBtn) {
paintGUI.drawLine();
} else if (e.getSource() == lineBtn) {
paintGUI.drawRectangle();
}
};
public static void main(String[] args) {
new GUIPaint().show();
}
public void show() {
// create main frame
JFrame frame = new JFrame("Swing Paint");
Container content = frame.getContentPane();
// set layout on content pane
content.setLayout(new BorderLayout());
// create draw area
paintGUI = new paintGUI();
// add to content pane
content.add(paintGUI, BorderLayout.CENTER);
// create controls to apply colors and call clear feature
JPanel controls = new JPanel();
clearBtn = new JButton("Clear");
clearBtn.addActionListener(actionListener);
blackBtn = new JButton("Black");
blackBtn.addActionListener(actionListener);
redBtn = new JButton("Red");
redBtn.addActionListener(actionListener);
magentaBtn = new JButton("Magenta");
magentaBtn.addActionListener(actionListener);
lineBtn = new JButton("Line");
lineBtn.addActionListener(actionListener);
filledRectangleBtn = new JButton("Filled Rectangle");
filledRectangleBtn.addActionListener(actionListener);
thickBtn = new JButton("Thick Line");
thickBtn.addActionListener(actionListener);
thinBtn = new JButton("Thin Line");
thinBtn.addActionListener(actionListener);
controls.add(lineBtn);
controls.add(filledRectangleBtn);
controls.add(thinBtn);
controls.add(thickBtn);
controls.add(blackBtn);
controls.add(redBtn);
controls.add(magentaBtn);
controls.add(clearBtn);
// add to content pane
content.add(controls, BorderLayout.NORTH);
frame.setSize(800, 800);
// can close frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// show the swing paint result
frame.setVisible(true);
}
}
I need the program to properly respond if I click the line button to be able to draw a line or if the rectangle button is pressed than draw rectangle.
The basic idea is to let the controls (buttons, mouse) change the attributes (color, shape, stroke, coordinates) and invoke repaint.
paintComponent
uses those attributes to draw the right shape.
Note the commented modifications of your code.
The code is one-file mre: the entire code can be copy pasted into GUIPaint.java
and run:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import swing_tests.PaintGUI.SHAPE;
public class GUIPaint {
private PaintGUI paintGUI;
public void showGui() {
JFrame frame = new JFrame("Swing Paint");
Container content = frame.getContentPane();
content.setLayout(new BorderLayout());
paintGUI = new PaintGUI();
content.add(paintGUI, BorderLayout.CENTER);
// create controls to apply colors and call clear feature
JPanel controls = new JPanel();
//todo: reduce duplicate code by having a method that constructs
//and adds a button
//todo: use button groups where only one button can be selected
JButton clearBtn = new JButton("Clear");
JButton blackBtn = new JButton("Black");
JButton redBtn = new JButton("Red");
JButton magentaBtn = new JButton("Magenta");
JButton lineBtn = new JButton("Line");
JButton filledRectangleBtn = new JButton("Filled Rectangle");
JButton thickBtn = new JButton("Thick Line");
JButton thinBtn = new JButton("Thin Line");
//todo: register an Action listner to each button by using lambda
//for example clearBtn.addActionListener(e-> paintGUI.clear());
ActionListener actionListener = e -> {
if (e.getSource() == clearBtn) {
paintGUI.clear();
}else if (e.getSource() == thinBtn) {
paintGUI.thin();
} else if (e.getSource() == thickBtn) {
paintGUI.thick();
} else if (e.getSource() == blackBtn) {
paintGUI.black();
} else if (e.getSource() == redBtn) {
paintGUI.red();
} else if (e.getSource() == magentaBtn) {
paintGUI.magenta();
} else if (e.getSource() == filledRectangleBtn) {
paintGUI.setShape(SHAPE.RECTANGLE);
} else if (e.getSource() == lineBtn) {
paintGUI.setShape(SHAPE.LINE);
}
};
clearBtn.addActionListener(actionListener);
blackBtn.addActionListener(actionListener);
redBtn.addActionListener(actionListener);
magentaBtn.addActionListener(actionListener);
lineBtn.addActionListener(actionListener);
filledRectangleBtn.addActionListener(actionListener);
thickBtn.addActionListener(actionListener);
thinBtn.addActionListener(actionListener);
controls.add(lineBtn);
controls.add(filledRectangleBtn);
controls.add(thinBtn);
controls.add(thickBtn);
controls.add(blackBtn);
controls.add(redBtn);
controls.add(magentaBtn);
controls.add(clearBtn);
content.add(controls, BorderLayout.NORTH);
frame.setSize(800, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new GUIPaint().showGui());
}
}
class PaintGUI extends JComponent {
//states defined by enum
enum SHAPE {RECTANGLE, LINE}
private SHAPE shape; // store state
private static final Color BACKGROUND_COLOR = Color.WHITE;
private int startX, startY, endX, endY; //shape coordinates
private Color color = Color.BLACK; //draw color
private BasicStroke stroke = new BasicStroke(3); //draw stroke
private boolean isClear = false;
public PaintGUI() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
//save coord x,y where mouse was pressed
startX = e.getX();
startY = e.getY();
endX = startX; endY = startY;
clear(); //clear draw board
}
});
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
//update end coord as mouse dragged
endX = e.getX();
endY = e.getY();
repaint(); //keep repainting while drag lasts
}
});
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// draw white on entire draw area to clear
g2.setColor(BACKGROUND_COLOR);
g2.fillRect(0, 0, getSize().width, getSize().height);
if(isClear || shape == null){
isClear = false;
return;
}
//draw using color , stroke and shape attributes
g2.setColor(color);
g2.setStroke(stroke);
switch (shape){
case RECTANGLE:
drawFilledRectangle(g2);
break;
case LINE:
drawLine(g2);
break;
default:
break;
}
}
public void clear() {
isClear = true;
repaint();
}
public void drawLine(Graphics2D g2) {
g2.drawLine( startX, startY, endX, endY);
repaint();
}
public void drawFilledRectangle(Graphics2D g2) {
//to allow rectangle dragged from bottom up and left to right
//use min x and min y as origin
int x = Math.min(startX, endX);
int y = Math.min(startY, endY);
int width = Math.abs(endX-startX); //to account for negative width
int height = Math.abs(endY-startY); //or height
g2.fillRect(x,y,width,height);
}
public void thin() {
setStroke(3);
}
public void thick() {
setStroke(10);
}
public void setStroke(int width) {
stroke = new BasicStroke(width);
repaint();
}
public void red() {
setColor(Color.red);
}
public void black() {
setColor(Color.black);
}
public void magenta() {
setColor(Color.magenta);
}
void setColor(Color color){
this.color = color;
repaint();
}
void setShape(SHAPE shape) {
this.shape = shape;
clear();
}
}