I'm trying to draw multiple rectangles on a panel. I created an ArrayList<Shape> and initialized in my constructor. In my paintComponent method, I draw a rectangle and then add it to the ArrayList. But when I do that, the drawing outcome on my panel turns out weird. As I drag to draw my first rectangle, I get this:
Here's part of my code:
public class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(final MouseEvent theEvent) {
myStartPoint = theEvent.getPoint();
repaint();
}
#Override
public void mouseReleased(final MouseEvent theEvent) {
myEndPoint = theEvent.getPoint();
repaint();
}
}
public class MyMouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(final MouseEvent theEvent) {
myEndPoint = theEvent.getPoint();
repaint();
}
}
/**
* Paints some rectangles.
*
* #param theGraphics The graphics context to use for painting.
*/
#Override
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
// for better graphics display
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(new Color(51, 0, 111));
g2d.setStroke(new BasicStroke(3));
final double x = myStartPoint.getX();
final double y = myStartPoint.getY();
final double xEnd = myEndPoint.getX();
final double yEnd = myEndPoint.getY();
if (xEnd> x && yEnd > y) {
final Shape rectangle = new Rectangle2D.
Double(x, y, xEnd - x, yEnd - y);
g2d.draw(rectangle);
myDrawings.add(rectangle);
}
for (Shape s : myDrawings) {
g2d.draw(s);
}
}
Don't do any code logic within paintComponent -- that method is for drawing and drawing only, and that is the source of your bug. Add the rectangle to the ArrayList in the mouse listener, on mouse release.
When I have done a similar project, I usually have one Rectangle field that I use to draw with the mouse listener on mouse drag, and which is draw within paintComponent. Then on mouse release I place that rectangle into the ArrayList, and set the class field as null.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class RectangleDraw extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final Color TEMP_RECT_COLOR = Color.LIGHT_GRAY;
private static final Color SHAPE_COLOR = Color.RED;
private Rectangle tempRect = null;
private List<Shape> shapes = new ArrayList<>();
public RectangleDraw() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw the temporary rectangle if not null
if (tempRect != null) {
g2.setColor(TEMP_RECT_COLOR);
g2.draw(tempRect);
}
// draw all the rectangles in the list
g2.setColor(SHAPE_COLOR);
for (Shape shape : shapes) {
g2.draw(shape);
}
}
// size the GUI to my specification
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
// My mouse listener and mouse motion listener
private class MyMouse extends MouseAdapter {
private Point p1; // start point
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
// create temporary rectangle
tempRect = createRectangle(e);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
tempRect = null; // null temp rectangle and
// add rectangle to List
shapes.add(createRectangle(e));
repaint();
}
// create a rectangle from start point and current point
private Rectangle createRectangle(MouseEvent e) {
Point p2 = e.getPoint();
int x = Math.min(p1.x, p2.x);
int y = Math.min(p1.y, p2.y);
int w = Math.abs(p1.x - p2.x);
int h = Math.abs(p1.y - p2.y);
Rectangle rect = new Rectangle(x, y, w, h);
return rect;
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Rectangle Draw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RectangleDraw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Related
I'm new to Java (particularly graphics) and am currently working on a task that requires me to have multiple JComponent shapes bouncing off the JFrame walls. The core of the "bouncing off the walls" code was provided (RectangleMover).
I have 3 separate classes. While I was able to get one rectangle to bounce correctly, I can't seem to figure out how to get multiple shapes into the frame.
This is what I came up with: having a JPanel extended class paint the multiple objects. However, now in the RectangleMover class, the shapes appear, but the moveBox method is no longer working despite the objects being of the correct type (Shape) as underlying Rectangle objects. I'm referring specifically to the shapes I have in the shapes array and the for-each loop in the Timer listener.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.Timer;
import java.awt.event.ActionEvent;
public class Shape extends JComponent {
public static int i=-1;
public static int j=0;
private Rectangle box;
private Graphics2D g2;
Color c1 = randomColor();
Color c2 = randomColor();
Color c3 = randomColor();
Color c4 = randomColor();
public Shape(int x, int y,int width,int height) {
box = new Rectangle(x, y, width, height);
}
public void draw(Graphics g) {
g2 = (Graphics2D) g;
if(box.width<125) {
g2.setColor(c1);
g2.fill(box);
}
else if(box.width>=125 && box.width<=200) {
g2.setColor(c2);
g2.fill(box);
}
else if(box.width>200 && box.width<250) {
g2.setColor(c3);
g2.fill(box);
}
else {
g2.setColor(c4);
g2.fill(box);
}
}
public Color randomColor() {
Random random = new Random();
int a = random.nextInt(255);
int b = random.nextInt(255);
int c = random.nextInt(255);
Color myColor = new Color(a,b,c);
return myColor;
}
public void moveBox(int dx, int dy) {
box.translate(dx, dy);
repaint();
}
public void growSize() {
box.width+=1;
box.height+=1;
}
public void shrinkSize() {
box.width-=1;
box.height-=1;
}
public class ShapePanel extends JPanel {
private Graphics2D g2;
Shape box1 = new Shape(0,0,20,20);
Shape box2 = new Shape(0,0,80,30);
Shape box3 = new Shape(0,0,40,80);
public Shape[] shapes = {box1, box2, box3};
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fillRect(0, 0, 500, 600);
for(Shape item: shapes) {
item.draw(g);
}
}
public Shape[] getArray() {
Shape[] newArray = shapes;
return newArray;
}
}
public class RectangleMover {
private static final int FRAME_WIDTH = 500;
private static final int FRAME_HEIGHT = 600;
private static int BOX_WIDTH = 20;
private static int BOX_HEIGHT = 20;
public static int x=0;
public static int y=0;
private static int dx=1;
private static int dy=1;
private Graphics2D g2;
private static ShapePanel sp = new ShapePanel();
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("Screensaver");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(sp);
frame.revalidate();
frame.repaint();
frame.setVisible(true);
//Shape[] shapes = sp.getArray();
for(Shape item: sp.shapes)
System.out.println(item.toString());
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
if(x+BOX_WIDTH>= FRAME_WIDTH-260) {
dx=-1;
}
if(x<=0) {
dx=1;
}
if(y+BOX_HEIGHT>= FRAME_HEIGHT-180) {
dy=-1;
}
if(y<=0) {
dy=1;
}
x=x+dx;
y=y+dy;
//shapes.moveBox(dx,dy);
for(Shape item: sp.shapes) {
item.moveBox(dx, dy);
frame.revalidate();
frame.repaint();
}
if(dx==-1) {
//shapes.shrinkSize();
//rect.shrinkSize();
}
if(dx==1) {
//shapes.growSize();
//rect.growSize();
}
}
}
}
}
I want to accomplish something very similar to the image a Rectangle whit a Selector Line.
Basically, I have a Rectangle and I want to have a selector line all around it.
For that, I wanted to create an additional JComponent.
At the moment I can only draw the Rectangle. How could I get the parentPanel JPanel inside the Rectangle class, so that I could add the selector?
public class TestPanel extends JFrame {
public class Rectangle extends JComponent {
public Rectangle(){
setBounds(x1, y1, x2, y2);
JPanel Selector = new JPanel();
//Adds Selector to parentPanel within Rectangle
//setBounds(x1-1, y1-1, x2+1, y2+1)
//!Problem parent is initially null! cant even a use property
//Life hacks?
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(0, 0, getWidth(), getHeight());
}
}
public TestPanel() {
Rectangle Rectangle = new Rectangle();
JPanel parentFrame = new JPanel();
parentFrame.add(Rectangle);
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new TestPanel();
}
}
If I try to add the selector inside the rectangle, it will get out of the drawing area. If I resize the drawing area, it won't be scalable for later development.
If possible I would avoid dual binding like:
public TestPanel() {
Rectangle Rectangle = new Rectangle();
JPanel parentPanel = new JPanel();
parentPanel.add(Rectangle);
Rectangle.addParent(parentPanel)
...
}
Again, I'm not 100% clear on what you're trying to achieve. If what you wish to create is a user-created dashed line, one that can change with mouse press/drag/release, then you don't need to create a new component but rather use a MouseAdapter as a MouseListener and MouseMotionListener, all to help you create the Rectangle, and then simply draw the Rectangle with a dashed line using an appropriate Stroke, as per this answer.
For example, something like would create a dashed line that is user-selectable:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class SelectorPanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final Stroke DASHED_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL, 0, new float[] { 5 }, 0);
private static final Color DASHED_COLOR = Color.LIGHT_GRAY;
private Rectangle rectangle = null;
public SelectorPanel() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
private class MyMouse extends MouseAdapter {
private Point p1 = null;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
rectangle = null;
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
createRectangle(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
createRectangle(e);
p1 = null;
}
}
private void createRectangle(MouseEvent e) {
Point p2 = e.getPoint();
int x = Math.min(p1.x, p2.x);
int y = Math.min(p1.y, p2.y);
int width = Math.abs(p1.x - p2.x);
int height = Math.abs(p1.y - p2.y);
rectangle = new Rectangle(x, y, width, height);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (rectangle != null) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(DASHED_COLOR);
g2.setStroke(DASHED_STROKE);
g2.draw(rectangle);
g2.dispose();
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
SelectorPanel mainPanel = new SelectorPanel();
JFrame frame = new JFrame("SelectorPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I want to add a Panel (which is on other panel) on mouse position. When I add now, panel's location is next to previous panel.
jPanel1.setLayout(new FlowLayout());
JPanel newPanel = new JPanel();
newPanel.setBackground(Color.red);
jPanel1.add(newPanel);
newPanel.setLocation(300,300);
jPanel1.revalidate();
jPanel1.repaint();
Point point = newPanel.getLocation();
int x = point.x;
int y = point.y;
newPanel.setLocation(x+5,y+5);
If you need to place a Swing component in a random position, then you will need a layout manager that would allow this, and FlowLayout, along with most standard managers, won't. The most common one to use is the simplest -- a null layout, e.g., someJPanel.setLayout(null); -- that is complete absence of a layout manager, but this comes with its own host of troubles, and so I try to avoid use of these as much as possible.
If your goal however is to move a red square, then best to keep things as simple as possible, and instead of creating and moving a JPanel, create and move something much lighter in weight, a Rectangle.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class MovingRect extends JPanel {
private static final long serialVersionUID = 1L;
private static final Color RECT_COLOR = Color.RED;
private static final int RECT_W = 300;
private Rectangle rect = new Rectangle(0, 0, RECT_W, RECT_W);
public MovingRect() {
setPreferredSize(new Dimension(800, 650));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
((Graphics2D) g).fill(rect);
}
private class MyMouse extends MouseAdapter {
private Point p0;
private Point pRect;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
if (rect.contains(e.getPoint())) {
p0 = e.getPoint();
pRect = rect.getLocation();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 != null) {
drag(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 != null) {
drag(e);
p0 = null;
}
}
private void drag(MouseEvent e) {
// use simple geometry to move the rectangle
Point p1 = e.getPoint();
int x = p1.x - p0.x + pRect.x;
int y = p1.y - p0.y + pRect.y;
rect = new Rectangle(x, y, RECT_W, RECT_W);
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
MovingRect mainPanel = new MovingRect();
JFrame frame = new JFrame("Moving Rectangle");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Code explanation
The rectangle to draw, initially placed at 0, 0:
private Rectangle rect = new Rectangle(0, 0, RECT_W, RECT_W);
In the constructor, set the drawing JPanel's preferred size, and create our mouse listener (actually a MouseAdapter) that will move the rectangle, and add the MouseAdapter as a MouseListener and MouseMotionListener to our drawing (main) JPanel:
public MovingRect() {
setPreferredSize(new Dimension(800, 650));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
Draw the rectangle within this JPanel's paintComponent method after doing clean-up painting by calling the super's method:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
((Graphics2D) g).fill(rect);
}
The mouse adapater that does the moving. It uses simple geometry of vector addition to calculate where to move
private class MyMouse extends MouseAdapter {
private Point p0;
private Point pRect;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
// if not button 1, then get out of here
return;
}
if (rect.contains(e.getPoint())) {
// get the first point of the mouse press and the rectangle's first position
p0 = e.getPoint();
pRect = rect.getLocation();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 != null) {
drag(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 != null) {
drag(e);
p0 = null; // set the first pressed point to null -- stop the listener
}
}
private void drag(MouseEvent e) {
// use simple geometry to move the rectangle
Point p1 = e.getPoint();
int x = p1.x - p0.x + pRect.x;
int y = p1.y - p0.y + pRect.y;
// create a new Rectangle with the position calculated above
rect = new Rectangle(x, y, RECT_W, RECT_W);
// ask Java to repaint the main JPanel
repaint();
}
}
This code is for drawing on a JPanel. In the paintComponent(Graphics) I am trying to draw curves via repeated Graphics2D#fillOval(x, y, with, height).
The app is working OK, and when I drag the mouse cursor slowly; it draws a continuous curve as I need. But when I speed up dragging the mouse cursor, the result is separated dots and not a continuous curve.
So how to make it draw a continuous curve even if I speed up dragging?
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class Painter extends JPanel {
int x, y;
ArrayList<Point> points;
public Painter() {
setBackground(Color.white);
points = new ArrayList<>();
MouseHandler listener = new MouseHandler();
this.addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
Point point = new Point(e.getX(), e.getY());
points.add(point);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Point point : points) {
g2d.fillOval(point.x, point.y, 15, 15);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
As mentioned in comment to your previous similar question:
Don't draw discrete ovals in your paintComponent method.
Instead connect the points hold in the List in the paintComponent by drawing lines between adjacent points.
If you need to make the line thicker, change the Stroke property of the Graphics2D object, using one that has a wider thickness.
Be careful with Strokes however since often you don't want the property change to propagate down the paint chain. This means that sometimes you will want to copy the Graphics object and set the Stroke on the new Graphics object and paint with that, then dispose of it.
The simplest way to create a Stroke is to use the BasicStroke class, e.g., new BasicStroke(6f) will get you a nice thick curve.
For example:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class Painter2 extends JPanel {
private static final float STROKE_WIDTH = 15f;
private static final Stroke STROKE = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
int x, y;
ArrayList<Point> points;
public Painter2() {
setBackground(Color.white);
points = new ArrayList<>();
MouseHandler listener = new MouseHandler();
this.addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
Point point = new Point(e.getX(), e.getY());
points.add(point);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(STROKE);
for (int i = 1; i < points.size(); i++) {
int x1 = points.get(i - 1).x;
int y1 = points.get(i - 1).y;
int x2 = points.get(i).x;
int y2 = points.get(i).y;
g2d.drawLine(x1, y1, x2, y2);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter2());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
Or better still:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Painter2 extends JPanel {
private static final float STROKE_WIDTH = 15f;
private static final Stroke STROKE = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
private static final Color CURVES_COLOR = Color.BLUE;
private static final Color TEMP_CURVE_COLOR = Color.PINK;
private List<List<Point>> curvesList = new ArrayList<>();
private List<Point> tempCurve = null;
public Painter2() {
setBackground(Color.white);
MouseHandler listener = new MouseHandler();
addMouseListener(listener);
addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mousePressed(MouseEvent e) {
tempCurve = new ArrayList<>();
tempCurve.add(e.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
tempCurve.add(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
tempCurve.add(e.getPoint());
curvesList.add(tempCurve);
tempCurve = null;
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setStroke(STROKE);
g2.setColor(CURVES_COLOR);
for (List<Point> curve : curvesList) {
drawCurve(g2, curve);
}
if (tempCurve != null) {
g2.setColor(TEMP_CURVE_COLOR);
drawCurve(g2, tempCurve);
}
g2.dispose();
}
private void drawCurve(Graphics2D g2, List<Point> ptList) {
for (int i = 1; i < ptList.size(); i++) {
int x1 = ptList.get(i - 1).x;
int y1 = ptList.get(i - 1).y;
int x2 = ptList.get(i).x;
int y2 = ptList.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter2());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
I have an application where the user draws lines. There is a JButton btnClear which, when the user clicks, must clear the drawings, so that the user can draw anew. I use an ActionListener on btnClear to know when it is clicked. I set a boolean Clear so that the correct IF statement is executed in paintComponent(). However, the boolean Clear keeps a False value in paintComponent() although I set it to True just before repaint(). Why is it so?
Note: I tried setting the frame's background to red just for to test the boolean in paintComponent().
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
public class Clipping extends JPanel implements MouseListener, ActionListener
{
static JFrame frame;
static JComboBox cboDraw;
static JButton btnClear;
static JButton btnClip;
double x1, y1, x2, y2;
boolean FirstPoint;
boolean Clear = false;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
CreateFrame();
}
});
}
private static void CreateFrame()
{
frame = new JFrame("Clipping");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Clipping());
frame.setSize(500,500);
frame.setVisible(true);
}
public Clipping()
{
setLayout(new BorderLayout());
JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
PopulateToolBar(toolbar);
add(toolbar, BorderLayout.WEST);
addMouseListener(this);
cboDraw.addMouseListener(this);
btnClip.addActionListener(this);
btnClear.addActionListener(this);
}
private static void PopulateToolBar(JToolBar toolbar)
{
String[] cboList = {"Line", "Polygon"};
cboDraw = new JComboBox(cboList);
cboDraw.setMaximumSize(new Dimension(70,30));
btnClip = new JButton("Set clip area");
btnClear = new JButton("Clear");
toolbar.add(cboDraw);
toolbar.addSeparator();
toolbar.add(btnClip);
toolbar.addSeparator();
toolbar.add(btnClear);
cboDraw.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClip.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClear.setAlignmentX(Component.CENTER_ALIGNMENT);
toolbar.setMargin(new Insets(10,10,10,10));
toolbar.setFloatable(false);
toolbar.setBackground(Color.black);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
if (cboDraw.getSelectedIndex() == 0) //draw line
{
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
else if (Clear == true)
{
frame.setBackground(Color.red); //ONLY FOR TESTING PURPOSE
}
}
public void mousePressed(MouseEvent e)
{
if (e.getSource() != cboDraw) //to prevent line coordinates from being saved when selecting from combobox
{
if (cboDraw.getSelectedIndex() == 0) //user wants to draw line
{
if (FirstPoint == false) //first coordinates
{
x1 = e.getX();
y1 = e.getY();
FirstPoint = true;
}
else //second coordinates
{
x2 = e.getX();
y2 = e.getY();
repaint();
FirstPoint = false;
}
}
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == btnClear)
{
Clear = true;
repaint();
Clear = false;
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
You should just do setBackground(Color.red); to call it on your JPanel instead of the JFrame
I think your actionPerformed and mousePressed are both executed at the same time. In mousePressed your if condition inside mousePressed is satisfied also if btnClear() is the source. So repaint method is called anyway, and you see no changes.
You're not calling the super.paintComponent(g) method within your paintComponent method override. Call this first, else, clear won't work.
Note that in your current code, this will not work for you since you're painting incorrectly in that you're not specifying, iterating through and drawing all the lines that need to be drawn within your paintComponent method. The way to solve this is via one of two ways:
Either create a List<Line2D> such as an ArrayList<Line2D>, fill it in your MouseListener/MouseMotionListener code, and then iterate through this List in your paintComponent method, drawing each line. If you do this, then your clear button's action would be to simply clear the List via clear() and call repaint(). No need for a boolean.
Or you could draw your lines onto a BufferedImage, and then draw the BufferedImage in your paintComponent method via the g.drawImage(...) method. If you do this, always check that the image is not null before drawing. Then in your mouse listening code, you'd draw to this image. In your clear button action, you'd create a new BufferedImage, or clear the current BufferedImage.
Also get rid of setBackground(...) calls from within paintComponent as they really shouldn't be in there.
For example of use of an ArrayList of lines:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaArrayList extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
private static final Stroke STROKE = new BasicStroke(3f);
private List<Line2D> lineList = new ArrayList<>();
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
public DrawPanelViaArrayList() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw all the lines held in the linesList.
Stroke oldStroke = g2.getStroke();
g2.setColor(LINES_COLOR);
g2.setStroke(STROKE); // draw thicker lines
for (Line2D line2d : lineList) {
g2.draw(line2d);
}
g2.setStroke(oldStroke); // reset stroke
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// add line to ArrayList
lineList.add(line);
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
// draw temporary line
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
lineList.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaArrayList());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
And what the heck, a BufferedImage version
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaBufferedImage extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
public static final Color CLEAR_COLOR = new Color(0, 0, 0, 0);
public static final Stroke STROKE = new BasicStroke(3f);
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
private BufferedImage img = new BufferedImage(PREF_W, PREF_W, BufferedImage.TYPE_INT_ARGB);
public DrawPanelViaBufferedImage() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
// if the GUI is to be re-sizeable, then consider adding a
// ComponentListener here, and resizing the BufferedImage in it
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw the BufferedImage if not null
if (img != null) {
g2.drawImage(img, 0, 0, null);
}
}
// size of our GUI
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// draw to the BufferedImage
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(STROKE);
g2.setColor(LINES_COLOR);
g2.draw(line);
g2.dispose();
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2 = img.createGraphics();
g2.setBackground(CLEAR_COLOR);
g2.clearRect(0, 0, getWidth(), getHeight());
g2.dispose();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaBufferedImage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}