I'm having issues attempting to draw a rectangle in the negative direction from a point clicked on a screen. I have the following class that simulates a screen capture software like Gyazo:
class DrawSquare extends JPanel implements MouseListener, MouseMotionListener {
// Components
public JDialog frame;
public Rectangle rectangle;
public BufferedImage bufferedImage;
public Point start, end;
// Variables
public String capturedImage;
public DrawSquare(JDialog frame) {
this.frame = frame;
// Read in crosshair image to replace mouse icon
Toolkit tool = Toolkit.getDefaultToolkit();
Image newImage = getToolkit().getImage("components/cursor.png");
Cursor cursor = tool.createCustomCursor(newImage, new Point (this.frame.getX(), this.frame.getY()), "img");
this.frame.setCursor(cursor);
this.frame.addMouseListener(this);
this.frame.addMouseMotionListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// draw background overlay
g2d.drawImage(bufferedImage, WIDTH, 0, this);
if (rectangle != null) {
//g2d.setColor(new Color(225, 225, 255, 128));
frame.setOpacity(0.6f);
//g2d.fill(rectangle);
System.out.println(rectangle);
g2d.setColor(new Color(72,119,205));
g2d.draw(rectangle);
}
g2d.dispose();
}
#Override
public void mouseDragged(MouseEvent e) {
this.end = e.getPoint();
int width = end.x - start.x;
int height = end.y - start.y;
rectangle.setSize(new Dimension(width, height));
frame.validate();
frame.repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
this.start = e.getPoint();
frame.validate();
frame.repaint();
}
#Override
public void mousePressed(MouseEvent e) {
// Get the X and Y point from the mouse pressed
rectangle = new Rectangle(start);
System.out.println(rectangle);
// Repaint the screen
frame.validate();
frame.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
Now the reason for the issue as mentioned already, is that when I attempt to draw the rectangle box in the opposition or negative direction of a point clicked on the screen, it doesn't draw, the rectangle information looks like this during such an attempt:
java.awt.Rectangle[x=635,y=395,width=-316,height=-194]
However, when I drag the rectnagle in the positive direction it works as it is supposed to:
What I'd like to know is how I can fix this using negative values for width/height, or doing it another way entirely.
You should actually have 2 Points - drag start Point and current drag Point.
The rectangle is calculated:
x=min(dragStartPoint.x, dragCurrentPoint.x)
y=min(dragStartPoint.y, dragCurrentPoint.y)
width=abs(dragStartPoint.x - dragCurrentPoint.x)
height=abs(dragStartPoint.y - dragCurrentPoint.y)
Increasing width and height are okay.
When they decrease, and reach 0, you need to decrease x and y.
Or otherwise seen: a rectangle is between two diagonal point topleft and bottomright. These roles change when for one coordinate (x or y) they cross.
Positive: you drag the bottom-right point (I guess).
Both coordinates zero: TL and BT cooincide, W and H zero.
Both coordinates negative: you leave the bottom-right point at the left-right position and drag the top-left point of the rectangle, W and H increasing from 0.
The easiest is:
Keep the mouse-down point from
Trag the mouse-drag point to
Calculate the rectangle between them:
Point min = new Point(Math.min(from.x, to.x), Math.min(from.y, to.y));
Point max = new Point(Math.max(from.x, to.x), Math.max(from.y, to.y));
Then the rectangle is calculated easily.
Related
I'm trying to draw a rectangle to the screen based on mouse listeners. I'm having a few problems:
To start with the shape, it has an offset by a few pixels relative to the
release location of the mouse.
Secondly I can get only the shape to display if I set the screen to
visible each time after adding a JPanel component.
Could anyone tell me what I'm doing wrong or point me in the direction of some documentation? (I’ve read the Oracle documentation but I didn’t really understand it)
I know this isnt the greatest code but i just want to get it working for the moment.
This is my first time asking a question on stackoverflow so if I'm doing something wrong pls tell me.
This is my code
public class Frame {
private JFrame frame;
public Frame() {
this.frame = frameSetup("Graph");
this.frame.addMouseListener(new Mouse());
}
private JFrame frameSetup(String heading) {
JFrame f = new JFrame(heading);
f.setBackground(Color.WHITE);
f.setSize(800, 800);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
return f;
}
public void draw(int xIn, int yIn, int widthIn, int heightIn) {
int x = xIn;
int y = yIn;
int width = widthIn;
int height = heightIn;
this.frame.add(new Panel(x, y, width, height));
this.frame.pack();
//this.frame.setVisible(true);
}
private class Mouse implements MouseListener {
int x = 0;
int y = 0;
int height = 0;
int width = 0;
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 1) {
x = e.getX();
y = e.getY();
System.out.println(x + ": " + y);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == 1) {
width = (e.getX() - x);
height = (e.getY() - y);
System.out.println(width + ": " + height);
draw(x - 7, y -30, width, height);
}
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
}
and
public class Panel extends JPanel {
private int x;
private int y;
private int width;
private int height;
public Panel(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
}
Welcome to the Site!
The offset is something I've had plenty of trouble with in the past aswell. It's caused by adding the MouseListener to the JFrame rather than it's contentPane. Basically, the JFrame is the thin border and the bar with the name, the icons and the buttons at the top. The content pane sits inside that, here it's the white surface. When you add the MouseListener to the JFrame, the coords are relative to this outline (the top left corner of the frame), rather than the white panel. This is fixed as follows:
// in the Frame constructor
this.frame.getContentPane().addMouseListener(new Mouse());
// in Mouse.mouseReleased(), remove the offset you added.
draw(x, y, width, height);
The Panel problem is a different story. I don't know how the code is doing what it does or how it works at all in the first place, but I can still give you some general tips.
Don't pack() the frame in the drawing method. It's used to calculate the window size while respecting the layout and the content of it. For me, it made the window as small as the OS allowed it to be.
Don't use JPanels like that! You'll run into weird problems like this, since they're made to be used in GUIs. For this reason, they're also way to heavy-weight to be used as a graphic. #user16320675 commented a tutorial on custom painting, I suggest you check that out.
Lastly, I suggest reworking the rectangle drawing. Right now, it only works when dragging from the top left to the bottom right.
I need to create a colorPicker tool for my little ms paint app.
I originally asked how to switch from my Graphics 2D implementation to a Graphics2D-->BufferedImage one, (and then it would be easy to get the pixels) but I have instead been suggested to get the pixel colors thought the robot class.
First of all, here's my MCEVE: NB. It cannot be a single class, it won't save.
import java.awt.*;
import javax.swing.*;
public class Runner {
public static void main(String[] args){
JFrame Maiframe = new JFrame("Paint");
Canvas DrawingBoard = new Canvas();
Maiframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Maiframe.setSize(700, 500);
Maiframe.add(DrawingBoard);
Maiframe.setVisible(true);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
public class Canvas extends JPanel {
public int xp; //present x
public int yp; //present y
public int xo; //old x
public int yo; //old y (Drawing from starting to new point as mouse drags)
public Canvas(){
super();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println("RGB : Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
addMouseMotionListener(new MouseMotionAdapter() { //get coords as mouse drags
#Override
public void mouseDragged(MouseEvent e) {
xp = e.getX();
yp = e.getY();
if(SwingUtilities.isLeftMouseButton(e))
repaint(); //call paintcomponent
}
public void mouseMoved(MouseEvent e1){ //keep trak of coords when mouse is not dragging
xo = e1.getX();
yo = e1.getY();
}
});
}
public void draw(int x, int y, Graphics g){ //draw the line
if(xo != 0)
g.drawLine(xo, yo, x, y);
xo = x; //old x is now present x and so on
yo = y;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(xp, yp, g);
}
}
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
And it works just fine.
I needed to implement something to get the color from any pixel, when the mouse clicks.
I did this: (adding this method to Canvas, and calling it from the mouse clicker listener)
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
Example of call:
//replace old mouseListener with this
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println(" da tela Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
Unfortunately, from this implementation I get undefined behaviour. The color read is always 255, 255, 255. Exept if I color the hole panel, then it gets it right 9 times out of 10, but in some areas it's still missing it.
I have also tried wrapping the whole thing into a bufferedImage with robot#screenCap but that doesn't even remotely work.
What am I doing wrong here?
Thank you very much.
EDIT1:
There are doubts as to how a line can remain on screen after a second one has been drawn. I'll provide a screenshot:
NBB. This works because an instance of Canvas is create inside of a JFrame into Runnable, so the changes are saved, avoiding the need for shapes and arrayLists.
I'll also add a full version of the code which prints wrong RGB results, remember that this does not save the lines as it stands. Please refer to the two separate classes above for testing.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
public class Canvas extends JPanel{
public int xp; //present x
public int yp; //present y
public int xo; //old x
public int yo; //old y (Drawing from starting to new point as mouse drags)
public Robot robot;
public static void main(String[] args){
JFrame Maiframe = new JFrame("Paint");
Canvas DrawingBoard = new Canvas();
Maiframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Maiframe.setSize(700, 500);
Maiframe.add(DrawingBoard);
Maiframe.setVisible(true);
}
public Canvas(){
super();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println("RGB --> Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
addMouseMotionListener(new MouseMotionAdapter() { //get coords as mouse drags
#Override
public void mouseDragged(MouseEvent e) {
xp = e.getX();
yp = e.getY();
if(SwingUtilities.isLeftMouseButton(e))
repaint(); //call paintcomponent
}
public void mouseMoved(MouseEvent e1){ //keep trak of coords when mouse is not dragging
xo = e1.getX();
yo = e1.getY();
}
});
}
public void draw(int x, int y, Graphics g){ //draw the line
if(xo != 0)
g.drawLine(xo, yo, x, y);
xo = x; //old x is now present x and so on
yo = y;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(xp, yp, g);
}
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
}
my little ms paint app
Well its not much of a paint app. All it does is draw a single line. Whenever you attempt to draw the second line the first will be removed.
So the first step you need to do is decide how you want the painting to work. There are two common approaches:
Store Objects you want to paint in an ArrayList and then the paintComponent(...) method will paint each Object in the List.
Paint directly to a BufferedImage and then the paintComponent(...) method can just paint the BufferedImage.
Check out Custom Painting Approaches for working examples of both of these approaches and give the pros/cons of using each approach.
I have instead been suggested to get the pixel colors thought the robot class
It depends which painting approach you want to use.
If you use the Draw on Component approach then you would use the MouseInfo and Robot to get the pixel color:
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
System.out.println( robot.getPixelColor(p.x, p.y) );
If you use the Draw on Image approach then you would get the pixel color from the BufferedImage:
int rgb = bufferedImage.getRGB(mouseEvent.getX(), mouseEvent.getY());
Color color = new Color( rgb );
System.out.println( color );
Final Update
You still have not posted a SSCCE. The code you posted does NOT draw a line. Even if it did draw a line, how do you expect us to click (with accuracy) a single pixel line.
The point of a SSCCE is to demonstrate the concept you are asking about. You are asking how to get the Color of a pixel on your panel. How the drawing gets on the panel is irrelevant to the question so the painting code should be as simple as possible.
Following is a proper SSCCE. Note:
The createAndShowGUI() and main() methods will be the same for all SSCCE. The difference is the `DrawingPanel code/class will change to demonstrate your problem.
The custom painting is hardcoded. There is no need for mouseMoved/mouseDragged logic (unless that is the problem) you are trying to solve. All you care about is having different colors to click on.
Just use a simple System.out.println(...) to display the value of the Color object.
All I did was copy the relevant pieces of code from you class and remove the irrelevant code to keep the code simple and straight forward. Anybody can do that.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DrawingCanvas extends JPanel
{
private Robot robot;
public DrawingCanvas()
{
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
try
{
robot = new Robot();
}
catch(Exception re) { System.out.println(re); }
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
System.out.println(pixelColor);
}
});
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor( Color.RED );
g.fillRect(0, 0, 40, 40);
g.setColor( Color.GREEN );
g.fillRect(40, 40, 40, 40);
g.setColor( Color.BLUE );
g.fillRect(80, 80, 40, 40);
}
private static void createAndShowGUI()
{
JPanel panel = new DrawingCanvas();
JFrame frame = new JFrame("DrawingCanvas");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() ); // Java 8 only
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Just copy/paste/compile/execute to see that the code works.
I'll let you figure out why your code doesn't appear to work.
NBB. This works because an instance of Canvas is create inside of a JFrame into Runnable, so the changes are saved, avoiding the need for shapes and arrayLists.
That statement is completely wrong. Painting done with the Graphics Object in the paintComponent() method is only temporary until the next time the paintComponent() method is invoked.
For example add the following code after the frame.setVisible() statement:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Graphics g = panel.getGraphics();
g.setColor( Color.YELLOW );
g.fillRect(120, 120, 40, 40);
}
});
The yellow square will disappear when the frame is resized. For permanent painting you need to use one of the two approaches I suggested in my original answer.
Edit 2:
This is the output I get when I click on the red, green, blue, background respectively:
C:\Java>java DrawingCanvas
java.awt.Color[r=255,g=0,b=0]
java.awt.Color[r=0,g=255,b=0]
java.awt.Color[r=0,g=0,b=255]
java.awt.Color[r=238,g=238,b=238]
First of all, here is the related code:
canvas = new CanvasPanel();
canvas.setBackground(Color.white);
canvas.addMouseListener(new PointListener());
canvas.addMouseMotionListener(new PointListener());
JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvas);
class CanvasPanel extends JPanel
{
public void paintComponent(Graphics page)
{
super.paintComponent(page);
if (mouseDragged == true)
{
page.drawRect(x1, y1, x3, y3);
canvas.repaint();
}
}
}
class PointListener implements MouseListener, MouseMotionListener
{
public void mousePressed (MouseEvent event)
{
mouseDragged = true;
x1 = event.getX();
y1 = event.getY();
}
public void mouseReleased (MouseEvent event)
{
// some code
}
public void mouseDragged(MouseEvent event)
{
x3 = event.getX();
y3 = event.getY();
canvas.repaint();
}
So what this code is doing is when I click on the canvas component, it will draw an outline of a rectangle and the size changes as I drag the mouse
However, when I click and start to drag the mouse, there's an offset in the bottom right corner of the rectangle. It seems to jump to a bigger size the second I drag the mouse. Interestingly, the closer to the upper left corner of the canvas component I click, the closer the rectangle size is to the rectangle I draw with the mouse.
How can I fix this?
Remember, drawRect uses x, y, width, height as it's parameters, you should actually be using the delta between the click point and the drag point
Maybe something like...
public void paintComponent(Graphics page)
{
super.paintComponent(page);
if (mouseDragged == true)
{
int x = Math.min(x1, x3);
int y = Math.min(y1, y3);
int width = Math.max(x1, x3) - x;
int height = Math.max(y1, y3) - y;
page.drawRect(x, y, width, height);
}
}
And, don't call repaint from within the paint methods
i'm trying to write a jigsaw puzzle application where an image is cut in pieces, scrambled, and the user have to rearrange them with drag&drop to reassemble the original image. (something like this: http://www.jigzone.com/puzzles/74055D549FF0?z=5).
i have to write this in java with Graphics2d.
so, at first i'm trying to make some kind of component which can show a part of the image (a rectangle for now), and can be dragged with mouse.
the code below works well when there is only one one such component. the problem is, when i add the second component, the first one is no longer visible.
i'm really stuck here. i have a feeling i'm missing something really basic. or maybe i'm on a wrong way. any help will be greatly appreciated.
edit: i changed a bit the code according to suggestions, however, still not working as expected.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
public class GraphicDragAndDrop extends JPanel {
Rectangle rect;
Image img;
public GraphicDragAndDrop(String imgFile, int x0, int y0){
rect = new Rectangle(x0, y0, 150, 75);
img = new ImageIcon(imgFile).getImage();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
g2d.drawImage(img, x, y, this);
}
public void setRect(int x, int y) {
rect.setLocation(x, y);
repaint();
}
public static void main(String[] args) {
// first piece
GraphicDragAndDrop piece1 = new GraphicDragAndDrop("a.png", 0, 0);
piece1.setRect(0, 0);
new GraphicDragController(piece1);
// second piece --> only this will be visible
GraphicDragAndDrop piece2 = new GraphicDragAndDrop("a.png", 200, 200);
//GraphicDragAndDrop piece2 = new GraphicDragAndDrop("b.png", 200, 200); // does'n work either
piece2.setRect(150, 150);
new GraphicDragController(piece2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(piece1);
f.add(piece2);
f.setSize(500,500);
f.setLocation(300,100);
f.setVisible(true);
}
}
class GraphicDragController extends MouseInputAdapter {
GraphicDragAndDrop component;
Point offset = new Point();
boolean dragging = false;
public GraphicDragController(GraphicDragAndDrop gdad) {
component = gdad;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Rectangle r = component.rect;
if(r.contains(p)) {
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
}
}
public void mouseReleased(MouseEvent e) {
dragging = false;
}
public void mouseDragged(MouseEvent e) {
if(dragging) {
int x = e.getX() - offset.x;
int y = e.getY() - offset.y;
component.setRect(x, y);
}
}
}
Your code above is written to draw only one image:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
// here
g2d.drawImage(new ImageIcon("a.png").getImage(), x, y, this);
}
If you need to draw more than one image, then consider creating a collection of images and iterating through the collection in paintComponent using a for loop:
also, never read in the image from within paintComponent since this method should be lean, mean and fast, and should concern itself with painting only. Also, there's no need to read the image in each time your program has to draw it as that's very inefficient and will slow the program unnecessarily. Instead read the image in once in the constructor or a similar init method.
For example,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Image img: myImageCollection) {
g2d.drawImage(img, 0, 0, this);
}
}
Edit
You state:
also, my plan was to have more objects of GraphicDragAndDrop class, each of them showing a different piece of the original image. is my approach wrong?
You could use components, but I have a feeling that it would be easy to drag images. I think it would be easier to rotate them for instance if you want your program to have this functionality. If not, though then sure use a component, but if you go this route, I would recommend using a JLabel and simply setting its ImageIcon rather than dragging JPanels.
I want to draw rectangle based on mousedrag event. if user dragging the mouse, then the rectangle on the applet should increase or decrease basing on current mouse coordinates.
i have the following code.
in the following code i am using SelectionArea class which extends a canvas on which i am performing drawing operation. i am using image variable in this class for double buffering to reduce flickering and to save the applet's previous state(i.e drawing content of applet)
but the code is working fine if i draw first rectangle. if i start to draw second rectangle the previously drawn rectangle is disappearing. i want the previously drawn rectangle to be on the screen
plz tell me how to solve this.
What you need to do, is save the previously drawn rectangle in some sort of data structure, so you can draw it again later.
This code (sorry about the length, will do something similar to what you are describing.
To use it, just slap the JPanel inside of a JFrame.
public class DrawPane extends JPanel {
private List<DrawnShape> drawings;
private DrawnShape curShape;
public DrawPane() {
drawings = new ArrayList<DrawnShape>();
setBackground(Color.WHITE);
setPreferredSize(new Dimension(300, 300));
addMouseListener(clickListener);
addMouseMotionListener(moveListener);
}
#Override
protected void paintComponent(Graphics g2) {
super.paintComponent(g2);
Graphics2D g = (Graphics2D) g2;
for (DrawnShape s : drawings) {
s.draw(g);
}
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(2));
if (curShape == null)
return;
curShape.draw(g);
}
private MouseListener clickListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
curShape = new DrawnShape(e.getPoint(), e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
drawings.add(new DrawnShape(curShape.getClickP(), e.getPoint()));
curShape = null;
}
};
private MouseMotionListener moveListener = new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
curShape = new DrawnShape(curShape.getClickP(), e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
};
}
class DrawnShape {
private Point p1, p2;
public DrawnShape(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
}
public Point getClickP() {
return p1;
}
public void draw(Graphics2D g) {
g.drawLine(p1.x, p1.y, p2.x, p1.y);
g.drawLine(p1.x, p1.y, p1.x, p2.y);
g.drawLine(p2.x, p2.y, p2.x, p1.y);
g.drawLine(p2.x, p2.y, p1.x, p2.y);
}
}
Custom Painting Approaches shows two techniques for doing this.