Draw the line on the JPanel when dragging the mouse - java

I want to draw 2 (or more ) lines on JPanel when the mouse drags. When i use super.paintComponent(g)
in my code, I couldn't draw 2 lines on the panel, however when I don't use super.paintComponent(g);, the result is ugly, like the pic below :
I understand why the lines behaved like that.
How could I draw the lines on the panel when dragging the mouse?
BTW, the line drawn by g2d.draw(line2d) sometimes it's not the smooth line (pic below)
My codes so far :
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class LineDrawing extends JPanel implements MouseMotionListener, MouseListener{
Point point1;
Point point2;
Line2D line2d;
public LineDrawing(){
super();
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(point1!=null && point2!=null){
g2d.setPaint(Color.RED);
g2d.setStroke(new BasicStroke(1.5f));
g2d.draw(line2d);
}
}
#Override
public void mouseDragged(MouseEvent e) {
point2 = e.getPoint();
line2d = new Line2D.Double(point1, point2);
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
point1 = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public static void main(String a[]){
EventQueue.invokeLater(new Runnable(){
#Override
public void run() {
JFrame frame = new JFrame();
LineDrawing linedraw= new LineDrawing();
frame.add(linedraw);
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

..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.

I don't know I get your question, but if you want to draw a continuous line. When dragging you have to update your last point possition.
#Override
public void mouseDragged(MouseEvent e) {
point2 = e.getPoint();
line2d = new Line2D.Double(point1, point2);
point1 = point2; // add this line
repaint();
}

Related

Keeping Shapes on Screen Help, can't figure out how to keep track of X,Y coordinates

I have tried and tried, I looked up many examples for keeping Shapes on the screen but can't seem to adapt to my code. In Summary, a left click prints a square, a right click prints a circle. I would like to fill the window with squares (rects) and circles. Any help and explanation so I can learn the concept would be great. I understand I have to keep track on the coordinates, perhaps in a loop but can seem to get it to work. Thanks again.
import java.awt.*;
import javax.swing.JFrame;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
public class MouseButtonTester extends JFrame implements MouseListener
{
private int mouseX, mouseY;
private int mouseButton;
private boolean isFirstRun;
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
private static final long serialVersionUID = 0; //use this if you do not like warnings
public MouseButtonTester() //constructor
{
super("Mouse Button Tester");
//set up all variables
mouseX = mouseY = 0;
mouseButton = 0;
isFirstRun = true;
//set up the Frame
setSize(WIDTH,HEIGHT);
setBackground(Color.WHITE);
setVisible(true);
//start trapping for mouse clicks
addMouseListener(this);
}
public void mouseClicked(MouseEvent e)
{
mouseX=e.getX(); //Tracks x coordinates
mouseY=e.getY(); //Tracker y coordinates
mouseButton = e.getButton(); //gets button number
repaint();
}
public void paint( Graphics window ) // Draws the Window
{
if(isFirstRun)
{
window.setColor(Color.WHITE);
window.fillRect(0,0,WIDTH, HEIGHT);
//change isFirstRun
}
window.setFont(new Font("TAHOMA",Font.BOLD,12));
window.setColor(Color.BLUE);
window.drawString("MOUSE BUTTON TESTER", 420,55);
draw(window);
}
public void draw(Graphics window)
{
if(mouseButton==MouseEvent.BUTTON1) //left mouse button pressed
{
//window.drawString("BUTTON1", 50,200); //debug code
window.setColor(Color.RED);
window.drawRect(mouseX,mouseY,10,10);
}
//right mouse button pressed
{
if (mouseButton == MouseEvent.BUTTON2)
window.setColor(Color.BLUE);
window.drawOval(mouseX,mouseY,10,10);
}
//any other mouse button pressed
{
}
}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) { }
}
------ Main Method --------------
public class MouseButtonTesterRunner
{
public static void main(String[] args)
{ MouseButtonTester prog = new MouseButtonTester();
}
}
First, start by having a read through:
Performing Custom Painting
Painting in AWT and Swing
So you can get a understanding how painting in Swing works, how you can work with it and your responsibilities when doing so.
Next, have a read through:
How can I set in the midst?
Java JFrame .setSize(x, y) not working?
How to get the EXACT middle of a screen, even when re-sized
Graphics rendering in title bar
for reasons why you should avoid overriding paint of top level containers like JFrame
Finally...
Painting in Swing is destructive, that is, every time your component is painted, you are expected to completely repaint the component state from scratch.
In order to achieve your goal, you will need to maintain a cache of the items you want to paint.
The concept itself it's very difficult, but there might be some "gotchas" along the way
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Point> circles;
private List<Point> squares;
public TestPane() {
circles = new ArrayList<>();
squares = new ArrayList<>();
addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
circles.add(e.getPoint());
} else if (SwingUtilities.isRightMouseButton(e)) {
squares.add(e.getPoint());
}
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// I'm picky
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Point p : circles) {
g2d.drawOval(p.x, p.y, 10, 10);
}
g2d.setColor(Color.BLUE);
for (Point p : squares) {
g2d.drawRect(p.x, p.y, 10, 10);
}
g2d.setFont(new Font("TAHOMA", Font.BOLD, 12));
g2d.setColor(Color.BLUE);
FontMetrics fm = g2d.getFontMetrics();
String text = "MOUSE BUTTON TESTER";
int x = getWidth() - fm.stringWidth(text) - 10;
int y = getHeight() - (fm.getAscent() - fm.getHeight()) - 10;
g2d.drawString(text, x, y);
g2d.dispose();
}
}
}
I suggest creating 2 classes.
1) Circle class
2) Square Class
Those classes will store info that you need, like X, y etc..
Initialize an array list that stores those objects & read from it in your paint method, proceed with painting them just like you do in your code.
(On a click event you simply create new object (circle/square) and add it into your array list)
So here's how i understand how your code works so far: The user left clicks, those coordinates are recorded, and a square is rendered on the screen at those coordinates.
When we click the coordinates are updated and on the next draw, the square is moved to a new position.
You were on the right track about needing a loop.
Here's the logic you need to implement:
Create an ArrayList as a member variable. The type can be a pair<int,int> object. So this arraylist will hold a list of X,Y coordinates. This arraylist will look something like this:
ArrayList<pair<int,int>> myRightClickCoords;
Once you make that list, every time the user clicks, record the click coordinates and insert them into the arraylist. That will look something like this:
myRightClickCoords.insert(new pair<int,int>(e.getX(),e.getY()));
Then, once that is added to your code, in your draw function, you can have a look that runs through the entire myRightClickCoords list and runs drawRect for each set of coordinates.
Once you get that working, you can do the same thing for left click and circles. Good luck!

Line drawing using Applet is all messed up now

We recently started using Applet and doing stuff with it, so the last class was on drawing lines. So after writing a successful code i changed a line of code for some reason unbeknownst to me and now it does not draw a line, rather draws a weird ray sort of.
Here is the code so any help would be appreciated.
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
public class SimplePaint extends Applet {
private int lastx;
private int lasty;
#Override
public void init() {
this.setForeground(Color.BLUE);
this.addMouseListener(new PositionRecorder());
this.addMouseMotionListener(new LineDrawer());
}
public int getLastx() {
return lastx;
}
public void setLastx(int lastx) {
this.lastx = lastx;
}
public int getLasty() {
return lasty;
}
public void setLasty(int lasty) {
this.lasty = lasty;
}
private class PositionRecorder extends MouseAdapter{
public void mousePressed(MouseEvent e) {
setLastx(e.getX());
setLasty(e.getY());
}
}
private class LineDrawer extends MouseMotionAdapter{
public void mouseDragged(MouseEvent e) {
int x=e.getX();
int y=e.getY();
Graphics g;
g=getGraphics();
g.drawLine(lastx, lasty, x, y);
}
}
}
I distinctively remember messing with this part of code if that helps
private class PositionRecorder extends MouseAdapter{
public void mousePressed(MouseEvent e) {
setLastx(e.getX());
setLasty(e.getY());
Painting in AWT is a well documented process. You should start by reading through:
Painting in AWT and Swing, in particular, I suggest you read the section on The Paint Method
A Closer Look at the Paint Mechanism
Quoted from "A Closer Look at the Paint Mechanism"
the paint method (defined by java.awt.Component.) This method will be executed by the painting subsystem whenever you component needs to be rendered.
In AWT, painting is destructive, it is expected that any time paint is called, that you will completely repaint the component state. This raises a number of issue which you will need to solve.
For anything you need to paint, you need a reliable way to store those states and reapply them when paint is called. In you case, something as simple as an ArrayList would be suitable, but you could also use a BufferedImage as a backing buffer and draw the committed drawing elements to it.
The benefit of the first approach is to provides the ability to undo changes, but in terms of painting, isn't as fast as simply painting an image.
A more clever approach would be to use both. Maintaining the draw states in a ArrayList, rendering each one as it's committed to the BufferedImage and when you want to perform an undo, start with a new BufferedImage and paint all the states up to the point you don't need...but that's all beyond the scope of the question
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
public class SimplePaint extends Applet {
private Point clickPoint;
private Point dragPoint;
#Override
public void init() {
this.setForeground(Color.BLUE);
this.addMouseListener(new PositionRecorder());
this.addMouseMotionListener(new LineDrawer());
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (clickPoint != null && dragPoint != null) {
g.drawLine(clickPoint.x, clickPoint.y, dragPoint.x, dragPoint.y);
}
}
private class PositionRecorder extends MouseAdapter {
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
// This is where I'd reset the clickPoint and dragPoint
// to null and apply the line to some structure which can
// redraw it in the `paint` method, maybe something like
// a ArrayList
// Alternativly, you could render the result to a BufferedImage
// and paint that inside the paint method
}
}
private class LineDrawer extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
repaint();
}
}
}

How to stop JComponent from clearing?

I am making a molecule designing application. I can draw the lines and circles, but it clears the old lines each time you click, so basically, you can only design molecules with 2 atoms.
Also, the mouseEvents don't deliver if you click very fast which is also a problem.
Here is the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class MoleculeDesigner extends JComponent implements MouseListener {
private Point op, cp;
private boolean first = true;
public static final Color linecolor = new Color(0, 255, 0);
private static final long serialVersionUID = 1L;
private BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
public MoleculeDesigner() {
JFrame f = new JFrame("Molecule Designer");
f.setBackground(Color.WHITE);
f.addMouseListener(this);
f.add(this);
f.setSize(100, 100);
f.setDefaultCloseOperation(3);
f.setVisible(true);
}
public static void main(String[] args) {
new MoleculeDesigner();
}
#Override
protected void paintComponent(Graphics g) {
if(op != null && cp != null) {
Graphics2D g2 = img.createGraphics();
super.paintComponent(g2);
g2.setColor(linecolor);
g2.drawLine((int) op.getX(), (int) op.getY(), (int) cp.getX(), (int) cp.getY());
g2.setColor(Color.BLACK);
g2.fillOval((int) cp.getX(), (int) cp.getY(), 10, 10);
op = (Point) cp.clone();
g2.dispose();
}
}
#Override
public Dimension getPreferredSize() {
return getParent().getMaximumSize();
}
#Override
public void mouseClicked(MouseEvent e) {
if(!first) {
cp = e.getPoint();
cp.setLocation(cp.getX(), cp.getY() - 8);
}
else {
op = e.getPoint();
first = false;
}
repaint();
}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
}
All help appreciated!
Either 1) draw in a BufferedImage which is then displayed inside of your paintComponent override, or 2) put your data into an ArrayList or other collection, and then iterate through the collection inside of paintComponent. I'd do the latter if I needed the data for other purposes. Also, never ever do this:
public void update(Graphics g) {
paintComponent(g);
}
This is not how Swing graphics are supposed to be done and is potentially dangerous code. Please read:
Basic Swing Graphics Tutorial
Advanced Swing Graphics Information
Edit
More detail regarding option 1:
Create a BufferedImage using one of its constructors.
Do your drawing on the image.
When you need to draw, get a Graphics object from the BufferedImage using getGraphics() or createGrahpics() (for a Graphics2D object)
Draw with this Graphics object
Then dispose() the Graphics object.
Then call repaint() to ask the JVM to repaint the component.
Draw the image in your paintComponent method by calling g.drawImage(...), passing in your buffered image.
Benefits: often the drawing is quicker, and I often use this to draw background images.
Drawbacks: the data points are not available, and so if you need to do manipulation or animation of your data points, this is not the way to go.
You don't, nor should you.
paint in Swing is a destructive process, this is the way it was designed. That is, there is an expectation that when you component is requested to paint itself, it will clean up the Graphics context before painting anything (this is slightly different for transparent components though).
Swing has no concept of what was painted on your component before and because the Graphics context is shared amongst all the components been painted, unless you clear the graphics first, you could end up with unwanted paint artifacts
Possible solutions might include...
Painting to some kind of backing buffer (such as a BufferedImage), which you use the paintComponent method to draw. This is limited in the fact that it just acts like a paint program, painting pixels to the image. You will also need to provide functionality when the size of the viewable area changes, as the BufferedImage won't know.
Place each object you wanted painted into some kind of List and iterate this list when paintComponent is called. This is a little more flexible in that you can control the order of the objects drawn, remove objects and insert new ones where you like

This square I'm animating is leaving a trail behind it, can anyone work out why?

Thanks for checking out this Question. I think I've just about scratched through my skull in frustration.
So what I've got is a 'JFrame' containing a 'JPanel'. The 'JPanel' contains a little colored Square which is supposed to move X pixels whenever I click the window.
Well, essentially everything behaves as it should, with one exception. When the blue square moves to the right, it leaves a trail of other squares behind it. It is not supposed to leave the trail, however, when I re-size the window, the trail vanishes.
Catalyst.java
package Prototype;
import java.awt.*;
public class Catalyst {
public static void main(String[] args){
World theWorldInstance = new World("Prototype", 100,100, 600,100); /*title,xpos,ypos,width,height*/
}
}
World.java
package Prototype;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class World extends JFrame {
Level mainPanel;
public World(String title, int x, int y, int width, int height) {
setTitle(title);
setBounds(x,y,width,height);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBackground(new Color(0x00000000));
initLevel();
}
public void initLevel(){
mainPanel = new Level();
Container visibleArea = getContentPane();
visibleArea.add(mainPanel);
setVisible(true);
add(mainPanel);
}
}
Level.java
package Prototype;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Level extends JPanel implements MouseListener, ActionListener {
Square x;
public Level() {
System.out.println("This is working correctly[JPANEL Cons]");
addMouseListener(this);
x = new Square();
}
public void paintComponent(Graphics g){
x.draw(g);
}
public void actionPerformed(ActionEvent e){
}
public void mousePressed(MouseEvent e){
requestFocus();
x.move();
repaint();
System.out.println("Focus Acquired");
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
}
Square.java
package Prototype;
import java.awt.*;
public class Square {
private Point position;
private int size;
private final int displacement;
public Square(){
position = new Point(10,10);
size = 20;
displacement = 5;
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillRect(position.x-(size/2), position.y-(size/2), size,size );
g.setColor(Color.black);
g.drawRect(position.x-(size/2), position.y-(size/2), size,size );
}
public void move() {
position.x += displacement;
}
}
Those are all my files. I hope I've phrased everything properly and provided all the content required. Whenever I've done something similar in the past this has never happened. I assume I'm missing something small, or I've done something stupid. If you can help me, thanks in advance!
There's another way of doing this. You can simply call the parent object's paintComponent method to clear the panel.
Add this to your constructor in Level :
this.setBackground(Color.BLACK);
And this as the first call in paintComponent:
super.paintComponent(g);
You can use g.clearRect(x, y, width, height), and provide the said coordinates, where you want the painting to be cleared from. Or you can give the dimensions of whole of the JPanel/JComponent where you are drawing, though doing this keep one thing in mind, that the said drawing is not a heavy work, else too much of cleaning will put extra burden on the painting calls.

Drawing a rectangle over an existing Graphics page

I have a Java application which draws a drawing. I want to give the user the possibility to mark an area with the mouse (in order to, for example, zoom into it).
For that I use the MouseMotionListener class, and when the mouse is (clicked and then) moved, I save the location of the currently selected (it isn't final since the user haven't released the mouse) rectangle, and use the repaint() function. I wish to display that rectangle over the original drawing, making it similar to the Selection tool in MSPaint.
The problem is that when I call the repaint() function, the method paintComponent (Graphics page) is invoked, in which I use the method super.paintComponent(page) which erases my drawing. However, if I don't use that method when I know the user is selecting a rectangle, I get that all the selected rectangles are "packed" one above the other, and this is an undesirable result - I wish to display the currently selected rectangle only.
I thought I should be able to save a copy of the Graphics page of the drawing and somehow restore it every time the user moves the mouse, but I could not find any documentation for helpful methods.
Thank you very much,
Ron.
Edit: Here are the relevant pieces of my code:
public class DrawingPanel extends JPanel
{
public FractalPanel()
{
addMouseListener (new MyListener());
addMouseMotionListener (new MyListener());
setBackground (Color.black);
setPreferredSize (new Dimension(200,200));
setFocusable(true);
}
public void paintComponent (Graphics page)
{
super.paintComponent(page);
//that's where the drawing takes place: page.setColor(Color.red), page.drawOval(..) etc
}
private class MyListener implements MouseListener, MouseMotionListener
{
...
public void mouseDragged (MouseEvent event)
{
//saving the location of the rectangle
isHoldingRectangle = true;
repaint();
}
}
}
I'm betting that you are getting your Graphics object via a getGraphics() call on a component, and are disatisfied since this obtains a Graphics object which does not persist. It is for this reason that you shouldn't do this but instead just do your drawing inside of the JPanel's paintComponent. If you do this all will be happy.
As an aside -- we'll be able to help you better if you tell us more of the pertinent details of your problem such as how you're getting your Graphics object and how you're trying to draw with it, key issues here. Otherwise we're limited to taking wild guesses about what you're trying to do.
e.g.,
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MandelDraw extends JPanel {
private static final String IMAGE_ADDR = "http://upload.wikimedia.org/" +
"wikipedia/commons/thumb/b/b3/Mandel_zoom_07_satellite.jpg/" +
"800px-Mandel_zoom_07_satellite.jpg";
private static final Color DRAWING_RECT_COLOR = new Color(200, 200, 255);
private static final Color DRAWN_RECT_COLOR = Color.blue;
private BufferedImage image;
private Rectangle rect = null;
private boolean drawing = false;
public MandelDraw() {
try {
image = ImageIO.read(new URL(IMAGE_ADDR));
MyMouseAdapter mouseAdapter = new MyMouseAdapter();
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter);
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
#Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (image != null) {
g.drawImage(image, 0, 0, null);
}
if (rect == null) {
return;
} else if (drawing) {
g2.setColor(DRAWING_RECT_COLOR);
g2.draw(rect);
} else {
g2.setColor(DRAWN_RECT_COLOR);
g2.draw(rect);
}
}
private class MyMouseAdapter extends MouseAdapter {
private Point mousePress = null;
#Override
public void mousePressed(MouseEvent e) {
mousePress = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
drawing = true;
int x = Math.min(mousePress.x, e.getPoint().x);
int y = Math.min(mousePress.y, e.getPoint().y);
int width = Math.abs(mousePress.x - e.getPoint().x);
int height = Math.abs(mousePress.y - e.getPoint().y);
rect = new Rectangle(x, y, width, height);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MandelDraw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MandelDraw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You need to repaint on every mouse movement:
public void mouseDragged(MouseEvent e){
int x = e.getX();
int y = e.getY();
//Update the rectangle holder object with that point coordinates
repaint();
}
You'll probably have a holder rectangle object to hold the initial and final rectangle points. The initials are set on mouse click, the final are modified on mouse dragged and on mouse released.
In paint method, clear the graphics and draw a rectangle with the coordinates in the holder. This is the basic idea.
UPDATE: How to draw a new shape on top of the existing image:
I'm thinking of two options:
If you are only drawing shapes (such as lines, rectangles and other Java2D stuff) you could have a Collection holding these shapes coordinates, and draw all of them on each paint. Pros: good when there are few shapes, allows undoing. Cons: When the number of shapes increase, the paint method will take more and more time in each pass.
Have a "background image". On each paint call, draw first the image and then the currently active shape on top. when an active shape is made persistent (onMouseReleased), it is saved to the background image. Pros: efficient, constant time. Cons: drawing a big background image on every mouse movement could be "expensive".

Categories

Resources