Drag a painted Shape - java

The Shape interface allows you to create shapes using different classes.
In the code below I create shapes using 3 different classes:
GeneralPath barBell = new GeneralPath();
barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
shapePanel.addShape(barBell, Color.RED);
shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);
Polygon triangle = new Polygon();
triangle.addPoint(0, 0);
triangle.addPoint(100, 0);
triangle.addPoint(50, 100);
triangle.translate(400, 250);
shapePanel.addShape(triangle, Color.GREEN);
Resulting in a screen image:
Unfortunately the Shape interface does not provide any functionality for transforming the Shape.
For example to move the Shape to a new location I use the following:
if (dragShape instanceof Path2D)
((Path2D)dragShape).transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
else if (dragShape instanceof Polygon)
((Polygon)dragShape).translate(deltaX, deltaY);
else if (dragShape instanceof RectangularShape)
{
RectangularShape rs = (RectangularShape)dragShape;
Rectangle r = rs.getBounds();
r.x += deltaX;
r.y += deltaY;
rs.setFrame( r );
}
I don't like the instanceof logic.
Is there a more generic approach to dragging any Shape around a panel that doesn't use instanceof logic?
Full example:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class ShapePanel2 extends JPanel
{
private ArrayList<ColoredShape> coloredShapes = new ArrayList<ColoredShape>();
public ShapePanel2()
{
DragListener dl = new DragListener();
addMouseListener(dl);
addMouseMotionListener(dl);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (ColoredShape cs : coloredShapes)
{
g2.setColor( cs.getForeground() );
g2.fill( cs.getShape() );
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(600, 500);
}
public void addShape(Shape shape, Color color)
{
ColoredShape cs = new ColoredShape(color, shape);
coloredShapes.add( cs );
repaint();
}
class ColoredShape
{
private Color foreground;
private Shape shape;
public ColoredShape(Color foreground, Shape shape)
{
this.foreground = foreground;
this.shape = shape;
}
public Color getForeground()
{
return foreground;
}
public void setForeground(Color foreground)
{
this.foreground = foreground;
}
public Shape getShape()
{
return shape;
}
}
class DragListener extends MouseAdapter
{
private Shape dragShape;
private Point pressed;
#Override
public void mousePressed(MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton(e))
{
pressed = e.getPoint();
for (int i = coloredShapes.size() - 1; i >= 0; i--)
{
ColoredShape cs = coloredShapes.get(i);
if (cs.getShape().contains( pressed ))
{
coloredShapes.remove(i);
coloredShapes.add(cs);
repaint();
dragShape = cs.getShape();
break;
}
}
}
}
#Override
public void mouseDragged(MouseEvent e)
{
if (dragShape != null)
{
int deltaX = e.getX() - pressed.x;
int deltaY = e.getY() - pressed.y;
if (dragShape instanceof Path2D)
((Path2D)dragShape).transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
else if (dragShape instanceof Polygon)
((Polygon)dragShape).translate(deltaX, deltaY);
else if (dragShape instanceof RectangularShape)
{
RectangularShape rs = (RectangularShape)dragShape;
Rectangle r = rs.getBounds();
r.x += deltaX;
r.y += deltaY;
rs.setFrame( r );
}
pressed = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e)
{
dragShape = null;
}
}
private static void createAndShowGUI()
{
ShapePanel2 shapePanel = new ShapePanel2();
GeneralPath barBell = new GeneralPath();
barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
shapePanel.addShape(barBell, Color.RED);
shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);
Polygon triangle = new Polygon();
triangle.addPoint(0, 0);
triangle.addPoint(100, 0);
triangle.addPoint(50, 100);
triangle.translate(400, 250);
shapePanel.addShape(triangle, Color.GREEN);
JFrame frame = new JFrame("ShapePanel2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(shapePanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}

One approach might be to convert each Shape to a GeneralPath as the Shape is added to the panel.
Now the dragging logic only needs to support a single class that implements the Shape interface:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class ShapePanel extends JPanel
{
private ArrayList<ColoredShape> coloredShapes = new ArrayList<ColoredShape>();
public ShapePanel()
{
DragListener dl = new DragListener();
addMouseListener(dl);
addMouseMotionListener(dl);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (ColoredShape cs : coloredShapes)
{
g2.setColor( cs.getForeground() );
g2.fill( cs.getShape() );
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(600, 500);
}
public void addShape(Shape shape, Color color)
{
// Convert the Shape to a GeneralPath so the Shape can be translated
// to a new location when dragged
ColoredShape cs = new ColoredShape(color, new GeneralPath(shape));
coloredShapes.add( cs );
repaint();
}
class ColoredShape
{
private Color foreground;
private GeneralPath shape;
public ColoredShape(Color foreground, GeneralPath shape)
{
this.foreground = foreground;
this.shape = shape;
}
public Color getForeground()
{
return foreground;
}
public void setForeground(Color foreground)
{
this.foreground = foreground;
}
public GeneralPath getShape()
{
return shape;
}
}
class DragListener extends MouseAdapter
{
private GeneralPath dragShape;
private Point pressed;
#Override
public void mousePressed(MouseEvent e)
{
// the clicked Shape will be moved to the end of the List
// so it is painted on top of all other shapes.
if (SwingUtilities.isLeftMouseButton(e))
{
pressed = e.getPoint();
for (int i = coloredShapes.size() - 1; i >= 0; i--)
{
ColoredShape cs = coloredShapes.get(i);
if (cs.getShape().contains( pressed ))
{
coloredShapes.remove(i);
coloredShapes.add(cs);
repaint();
dragShape = cs.getShape();
break;
}
}
}
}
#Override
public void mouseDragged(MouseEvent e)
{
if (dragShape != null)
{
int deltaX = e.getX() - pressed.x;
int deltaY = e.getY() - pressed.y;
dragShape.transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
pressed = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e)
{
dragShape = null;
}
}
private static void createAndShowGUI()
{
ShapePanel shapePanel = new ShapePanel();
GeneralPath barBell = new GeneralPath();
barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
shapePanel.addShape(barBell, Color.RED);
shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);
Polygon triangle = new Polygon();
triangle.addPoint(0, 0);
triangle.addPoint(100, 0);
triangle.addPoint(50, 100);
triangle.translate(400, 250);
shapePanel.addShape(triangle, Color.GREEN);
JFrame frame = new JFrame("ShapePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(shapePanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}

Related

Swing: How to reduce the response time of MouseInputAdapter?

I've made a tool which could allow user to draw on screenshot.Here is my code:
package GUI.Views;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
public class Canvas extends JPanel {
public static int radius = 5;
public class DrawPointListener extends MouseInputAdapter{
private void draw(Point p, int radius){
Graphics2D g2 = (Graphics2D)getGraphics();
int x = (int)p.getX() - radius/2;
int y = (int)p.getY() - radius/2;
g2.fillOval(x, y, radius, radius);
}
#Override
public void mousePressed(MouseEvent e) {
draw(e.getPoint(), radius);
}
#Override
public void mouseDragged(MouseEvent e) {
draw(e.getPoint(), radius);
}
}
private BufferedImage screenShot;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(this.screenShot, 0, 0, null);
}
public Canvas() {
this.setVisible(true);
this.screenShot = getScreenShot();
this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
DrawPointListener drawListener = new DrawPointListener();
this.addMouseListener(drawListener);
this.addMouseMotionListener(drawListener);
}
private BufferedImage getScreenShot() {
try {
return new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
} catch (AWTException e) {
System.out.println("Error");
return null;
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
Canvas canvas = new Canvas();
f.setUndecorated(true);
f.add(canvas);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
}
}
Code worked fine when you try to "draw" on the screen slowly, but when you move your mouse quickly, you would see those points are not consecutive, like this:
I think that because there is a time interval of Listener.How could I improve it?
You can't change the time interval of the listener. You are NOT guaranteed to generate an event for every pixel.
So instead of drawing a single point, you need to draw a line between the current and previous point.
Something like:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DrawingPanel extends JPanel
{
private ArrayList<ArrayList<Point>> previous = new ArrayList<ArrayList<Point>>();
private ArrayList<Point> current = new ArrayList<Point>();
private BasicStroke basicStroke;
public DrawingPanel(int strokeSize)
{
basicStroke = new BasicStroke(strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
MouseAdapter ma = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
}
#Override
public void mouseDragged(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
if (current.size() > 1)
{
previous.add( current );
}
current = new ArrayList<Point>();
}
};
addMouseMotionListener( ma );
addMouseListener( ma );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke( basicStroke );
// Paint lines from previous drags
for (int i = 0; i < previous.size(); i++)
{
drawLines(g, previous.get(i));
}
// Paint line from current drag
drawLines(g, current);
}
private void drawLines(Graphics g, ArrayList<Point> points)
{
for (int i = 0; i < points.size() - 1; i++)
{
int x = (int) points.get(i).getX();
int y = (int) points.get(i).getY();
int x2 = (int) points.get(i + 1).getX();
int y2 = (int) points.get(i + 1).getY();
g.drawLine(x, y, x2, y2);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(15));
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}

Drawing shapes in JPanel over an image

I need to draw shapes (circle or free line) over an image that is shown in a JLabel of a JPanel.
I based my code on the questions How to draw thin line with no gap while dragging the cursor? and Draw a circle using 2 mouse clicks.
The code is bellow. The problem is that when I start drawing the image disappears and only reappears after I stop.
If I comment the line super.paintComponent(g); that doesnt happen but when I draw the circle it maintains a path of the previous positions.
public static void main(String args[]) {
try {
URL url = new URL("http://www.senhoritatours.com/wp-content/uploads/2014/05/Porto-.jpg");
backgroundImage = ImageIO.read(url);
} catch (Exception e) {
e.printStackTrace();
}
loadAnnotation();
loadBackground();
JFrame f;
f = new JFrame();
f.setLayout(new BorderLayout());
f.add(mp);
f.pack();
f.setVisible(true);
}
/* Layer 0:
* Load background picture */
public static void loadBackground() {
JLabel lbImg = new JLabel();
lbImg.setBounds(0, 0, new ImageIcon(backgroundImage).getIconWidth(), new ImageIcon(backgroundImage).getIconHeight());
lbImg.setIcon(new ImageIcon(backgroundImage));
mp = new JPanel(new BorderLayout());
btnCircle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if(btnCircle.isEnabled())
{
btnCircle.setEnabled(false);
btnLine.setEnabled(true);
}
}
});
btnLine.setEnabled(true);
btnCircle.setEnabled(false);
btnLine.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if(btnLine.isEnabled())
{
btnLine.setEnabled(false);
btnCircle.setEnabled(true);
}
}
});
mp.add(btnCircle);
mp.add(btnLine);
mp.add(lbImg);
mp.add(p);
}
/* Layer 1:
* Annotation: Draw on top of background picture anything! */
public static void loadAnnotation() {
p = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.RED);
if (_bufImage == null) {
int w = this.getWidth();
int h = this.getHeight();
_bufImage = new BufferedImage(1024,600, BufferedImage.TRANSLUCENT);
Graphics2D gc = _bufImage.createGraphics();
}
g2.drawImage(_bufImage, null, 0, 0);
if (_state == State.DRAGGING) {
if (!btnLine.isEnabled())
{
g.drawLine(_start.x, _start.y, _end.x , _end.y);
}
}
if (!btnCircle.isEnabled())
{
g.drawOval(x, y, width, height);
}
}
public Dimension getPreferredSize() {
return new Dimension(1024, 600);
}
};
p.setLayout(new OverlayLayout(p));
p.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent me) {
}
#Override
public void mousePressed(MouseEvent me) {
last = me.getPoint();
dragging = isInsideEllipse(last);
if (!dragging) {
x = last.x;
y = last.y;
width = 0;
height = 0;
}
p.repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
//_state = State.IDLE;
last = null;
dragging = false;
_state = State.IDLE;
p.repaint();
}
#Override
public void mouseEntered(MouseEvent me) {
}
#Override
public void mouseExited(MouseEvent me) {
}
});
p.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent me) {
if(!btnLine.isEnabled())
{
_state = State.DRAGGING;
_end = me.getPoint();
if (_state == State.DRAGGING) {
Graphics2D g2 = _bufImage.createGraphics();
g2.setColor(Color.red);
g2.setStroke(new BasicStroke(2));
g2.drawLine(_start.x, _start.y, _end.x, _end.y);
p.repaint();
//
}
_start = _end;
}
else
{
int dx = me.getX() - last.x;
int dy = me.getY() - last.y;
if (dragging) {
x += dx;
y += dy;
} else {
width += dx;
height += dy;
}
last = me.getPoint();
p.repaint();
}
}
#Override
public void mouseMoved(MouseEvent me) {
//System.out.println("move");
_start = me.getPoint();
}
});
}
Either
Display the image in the same paintComponent method that you're doing your drawing in, via Graphics#drawImage(...). You would call this immediately after the super.paintComponent(g) call.
Or do your drawing in the paintComponent(...) method of your JLabel, the one displaying the image.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private final static String PATH = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/7/7c/Thomas_Hicks_-_Leopold_Grozelier_-_Presidential_Candidate_"
+ "Abraham_Lincoln_1860_-_cropped_to_lithographic_plate.jpg/"
+ "463px-Thomas_Hicks_-_Leopold_Grozelier_-_Presidential_Candidate_"
+ "Abraham_Lincoln_1860_-_cropped_to_lithographic_plate.jpg";
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private BufferedImage backgroundImg;
private Point startPt = null;
private Point endPt = null;
private Point currentPt = null;
private int prefW;
private int prefH;
public DrawingPanel() throws IOException {
URL imgUrl = new URL(PATH);
BufferedImage bImg = ImageIO.read(imgUrl);
prefW = bImg.getWidth();
prefH = bImg.getHeight();
backgroundImg = new BufferedImage(prefW, prefH,
BufferedImage.TYPE_INT_ARGB);
Graphics g = backgroundImg.getGraphics();
g.drawImage(bImg, 0, 0, this);
g.dispose();
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseMotionListener(myMouseAdapter);
addMouseListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
if (startPt != null && currentPt != null) {
g.setColor(DRAWING_COLOR);
int x = Math.min(startPt.x, currentPt.x);
int y = Math.min(startPt.y, currentPt.y);
int width = Math.abs(startPt.x - currentPt.x);
int height = Math.abs(startPt.y - currentPt.y);
g.drawRect(x, y, width, height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
public void drawToBackground() {
Graphics g = backgroundImg.getGraphics();
g.setColor(FINAL_DRAWING_COLOR);
int x = Math.min(startPt.x, endPt.x);
int y = Math.min(startPt.y, endPt.y);
int width = Math.abs(startPt.x - endPt.x);
int height = Math.abs(startPt.y - endPt.y);
g.drawRect(x, y, width, height);
g.dispose();
startPt = null;
repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent mEvt) {
currentPt = mEvt.getPoint();
DrawingPanel.this.repaint();
}
#Override
public void mouseReleased(MouseEvent mEvt) {
endPt = mEvt.getPoint();
currentPt = null;
drawToBackground();
}
#Override
public void mousePressed(MouseEvent mEvt) {
startPt = mEvt.getPoint();
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = null;
try {
mainPanel = new DrawingPanel();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Adding multiple MouseListeners dynamically JPanel

I am trying to add shapes onto a window using JPanel and then be able to click and drag them around the window. This works if I only have one shape; but when I add more shapes, the click and drag is very funky. It does drag but not with the mouse, it isn't proportional and doesn't drag with the mouse.
Any help is appreciated. Thanks!
public class SimpleDraw {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
// Display the window.
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Squares squares = new Squares();
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(squares);
JMenuBar menubar = new JMenuBar();
JMenu shapes = new JMenu("Shapes");
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
shapes.add(rectangleMenuItem);
menubar.add(shapes);
setJMenuBar(menubar);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> squares = new ArrayList<Path2D>();
// private Path2D rect = new Path2D.Double();
int currentIndex;
public void addSquare(int x, int y, int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
squares.add(rect2);
// rect = rect2;
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Path2D rect : squares) {
g2.draw(rect);
}
repaint();
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < squares.size(); i++) {
if (squares.get(i) != null
&& squares.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
squares.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
pressed = false;
}
}
}
Lot of problems...
Most important, no you don't want to add a bunch of MouseListeners/MouseMotionListeners to your JPanel. You only want to add one, and have it control any and all squares that the JPanel holds.
Don't put a repaint() in your paintComponent method as that's a poor way to try to create an animation loop, a loop that you have absolutely no control over. Plus there's no need. The MouseAdapter should drive all the animation by itself.
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
public Squares() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
private List<Path2D> squares = new ArrayList<Path2D>();
// private Path2D rect = new Path2D.Double();
int currentIndex;
public void addSquare(int x, int y, int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
squares.add(rect2);
repaint(); // !!
// rect = rect2;
// !! MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
// addMouseListener(myMouseAdapter);
// addMouseMotionListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Path2D rect : squares) {
g2.draw(rect);
}
// !! repaint();
}

How to draw on the JFrame?

I have a pdf file, which is rendered as an image and added as a JLabel in the JFrame. Now i want to draw on the contents in the JFrame by mouseclick and keystrokes. And these drawings should appear on the image(JLabel). I have pasted my code below. Can anyone please help me to figure it out!!!
public class LinePanel extends JPanel{
public static final String RESOURCE = "G:/resource/A0TestFile.pdf";
private MouseHandler mouseHandler = new MouseHandler();
private Point p1;
private Point p2;
private Point p3;
private Point p4;
ArrayList<Point> points = new ArrayList<Point>();
public LinePanel() {
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(1,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL));
crop(g);
}
public void crop(Graphics g){
if(points != null && points.size()>0){
for(int i=0;i<points.size();i++){
p1 = points.get(i);
g.drawLine(p1.x, p1.y, p1.x-20, p1.y);
g.drawLine(p1.x, p1.y, p1.x, p1.y+20);
}
for( i=1;i<points.size();i++){
p2 = points.get(i);
g.drawLine(p2.x, p2.y, p2.x-20, p2.y);
g.drawLine(p2.x, p2.y, p2.x, p2.y-20);
}
for( i=2;i<points.size();i++){
p3 = points.get(i);
g.drawLine(p3.x, p3.y, p3.x+20, p3.y);
g.drawLine(p3.x, p3.y, p3.x, p3.y-20);
}
for( i=3;i<points.size();i++){
p4 = points.get(i);
g.drawLine(p4.x, p4.y, p4.x+20, p4.y);
g.drawLine(p4.x, p4.y, p4.x, p4.y+20);
}
}
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if(points.size()<4){
points.add(e.getPoint());
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 1;
public ControlPanel() {
new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0);
new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA);
new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0);
new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA);
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code,
final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
if(points.size()==1){
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.repaint();
}
if(points.size()==2){
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
if(points.size()==3){
LinePanel.this.p3.translate(dx, dy);
LinePanel.this.repaint();
}
if(points.size()==4){
LinePanel.this.p4.translate(dx, dy);
LinePanel.this.repaint();
}
}
});
ControlPanel.this.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(k, k.toString());
ControlPanel.this.getActionMap()
.put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() throws IOException{
File file = new File(RESOURCE);
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_WRITE,
0, channel.size());
PDFFile pdffile = new PDFFile(buf);
PDFPage page = pdffile.getPage(1);
Rectangle rect = new Rectangle(0,0,(int)page.getBBox().getWidth(),(int)page.getBBox().getHeight());
Image img = page.getImage(rect.width, rect.height,rect,null,true,true);
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setPreferredSize(new Dimension(800, 800));
JPanel panel = new JPanel();
JLabel image = new JLabel(new ImageIcon(img));
panel.add(image);
JScrollPane jspane=new JScrollPane(panel);
f.add(jspane, BorderLayout.CENTER);
f.add(this);
f.add(new ControlPanel(),BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
new LinePanel().display();
} catch (IOException ex) {
Logger.getLogger(LinePanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
Try this. It's a Panel with an Image. Add the Panel to the Frame;
public class ImagePanel extends JPanel{
private BufferedImage image;
public ImagePanel() {
try {
image = ImageIO.read(new File("image name and path"));
} catch (IOException ex) {
// handle exception...
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
// draw other stuff
}
}
If you want your drawing to be painted on top of any component added to the panel/frame, you need to override the paint() method.
Take a look at A Closer Look at the Paint Mechanism

Graphics in repaint draws random lines

So I'm creating a free hand drawing JPanel, that reacts to mouse movements and draws lines. I got it mostly working except for a bug where it'll randomly draw a straight line between lines. That random straight line isn't intentional, what's drawn on the buffered image is supposed to be strictly what the user draws. These random drawn lines are not done by the user and it's confusing. Below is my code, can anyone take a look? The image included gives you a visual representation of what it is doing.
public class NoteDocument extends JPanel implements MouseListener, MouseMotionListener {
private Frame commands;
private JDesktopPane desktop;
private JInternalFrame colorFrame;
private JPanel colorPanel;
private JColorChooser colorChooser;
private enum State { IDLING, DRAGGING };
private enum ButtonPosition { PRESSED, RELEASED };
private enum Shape { SQUARE, RECTANGLE, CIRCLE, OVAL, LINE };
private State state = State.IDLING;
private ButtonPosition position = ButtonPosition.RELEASED;
private Shape shape = null;
//private ArrayList<Point> points = new ArrayList<Point>();
private ArrayList<Point> pressedPoints = new ArrayList<Point>();
private ArrayList<Point> draggedPoints = new ArrayList<Point>();
private ArrayList<Point> releasedPoints = new ArrayList<Point>();
private BufferedImage bufferedImage = null;
public NoteDocument(Frame commands) {
this.commands = commands;
setBackground(Color.WHITE);
addMouseListener(this);
addMouseMotionListener(this);
createColorChooser();
}
private void createColorChooser() {
for (int i = 0; i < commands.getLayeredPane().getComponentCount(); i++) {
if (commands.getLayeredPane().getComponent(i) instanceof JDesktopPane) {
desktop = (JDesktopPane) commands.getLayeredPane().getComponent(i);
}
}
colorChooser = new JColorChooser();
colorPanel = new JPanel();
colorFrame = new JInternalFrame();
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(colorPanel);
colorPanel.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(colorChooser, javax.swing.GroupLayout.PREFERRED_SIZE, 434, javax.swing.GroupLayout.PREFERRED_SIZE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(colorChooser, javax.swing.GroupLayout.PREFERRED_SIZE, 328, javax.swing.GroupLayout.PREFERRED_SIZE)
);
colorFrame.add(colorPanel);
desktop.add(colorFrame);
colorFrame.pack();
colorFrame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(bufferedImage == null)
{
int panelHeight = this.getHeight();
int panelWidth = this.getWidth();
bufferedImage = (BufferedImage) this.createImage(panelHeight, panelWidth);
Graphics2D gc = bufferedImage.createGraphics();
gc.setColor(Color.WHITE);
gc.fillRect(0, 0, panelWidth, panelHeight);
g2.drawImage(bufferedImage, null, 0, 0);
}
//draw pressed points
for (int a = 0; a < pressedPoints.size(); a++) {
Point p1 = pressedPoints.get(a);
g.drawLine(p1.x, p1.y, p1.x, p1.y);
}
//draw draggedPoints
for (int b = 0; b < draggedPoints.size() - 2; b++) {
Point p1 = draggedPoints.get(b);
Point p2 = draggedPoints.get(b + 1);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
//draw released points
for (int c = 0; c < releasedPoints.size(); c++) {
Point p1 = releasedPoints.get(c);
g.drawLine(p1.x, p1.y, p1.x, p1.y);
}
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
position = ButtonPosition.PRESSED;
state = State.DRAGGING;
pressedPoints.add(e.getPoint());
this.repaint();
} else if (e.getButton() == MouseEvent.BUTTON2) {
//TODO right click popup
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (state == State.DRAGGING) {
releasedPoints.add(e.getPoint());
position = ButtonPosition.RELEASED;
state = State.IDLING;
this.repaint();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if ((state == State.DRAGGING) && (position == ButtonPosition.PRESSED)) {
draggedPoints.add(e.getPoint());
this.repaint();
} else if ((state == State.IDLING) && (position == ButtonPosition.RELEASED)) {
return;
}
}
#Override
public void mouseMoved(MouseEvent e) {
if ((state == State.DRAGGING) && (position == ButtonPosition.PRESSED)) {
draggedPoints.add(e.getPoint());
this.repaint();
} else if ((state == State.IDLING) && (position == ButtonPosition.RELEASED)) {
return;
}
}
}
You will likely want to nest Lists to achieve your goal:
List<List<Point>>
This way, when the mouse is pressed, start an ArrayList, and when released, finish it. Then you can use nested for loops to draw all the curves.
For example (from a previous answer of mine):
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
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.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
public class LineDrawEg {
private static void createAndShowGui() {
JPanel mainPanel = new JPanel(new GridLayout(1, 0));
mainPanel.setPreferredSize(new Dimension(800, 400));
MyMouseAdapter mouseAdapter = new MyMouseAdapter();
JPanel[] panels = {new Panel1(), new Panel2()};
for (int i = 0; i < panels.length; i++) {
String title = "Panel " + (i + 1);
Border border = new TitledBorder(title);
panels[i].setBorder(border);
panels[i].addMouseListener(mouseAdapter);
panels[i].addMouseMotionListener(mouseAdapter);
mainPanel.add(panels[i]);
}
JFrame frame = new JFrame("Line Draw Eg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Panel1 extends JPanel implements Positionable {
private int xPos = 0;
private int yPos = 0;
#Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(xPos, yPos, 5, 5);
}
#Override
public void mouseDragged(Point p) {
xPos = p.x;
yPos = p.y;
repaint();
}
#Override
public void mousePressed(Point p) {
xPos = p.x;
yPos = p.y;
repaint();
}
#Override
public void mouseReleased(Point p) {
xPos = p.x;
yPos = p.y;
repaint();
}
}
class Panel2 extends JPanel implements Positionable {
private List<List<Point>> listOfLists = new ArrayList<List<Point>>();
private List<Point> currentPoints;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5f));
if (currentPoints != null && currentPoints.size() > 1) {
g2.setColor(Color.blue);
for (int i = 1; i < currentPoints.size(); i++) {
int x1 = currentPoints.get(i - 1).x;
int y1 = currentPoints.get(i - 1).y;
int x2 = currentPoints.get(i).x;
int y2 = currentPoints.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
g2.setColor(Color.red);
for (List<Point> pointList : listOfLists) {
if (pointList.size() > 1) {
for (int i = 1; i < pointList.size(); i++) {
int x1 = pointList.get(i - 1).x;
int y1 = pointList.get(i - 1).y;
int x2 = pointList.get(i).x;
int y2 = pointList.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
}
}
#Override
public void mousePressed(Point p) {
currentPoints = new ArrayList<Point>();
currentPoints.add(p);
repaint();
}
#Override
public void mouseDragged(Point p) {
currentPoints.add(p);
repaint();
}
#Override
public void mouseReleased(Point p) {
if (currentPoints != null) {
currentPoints.add(p);
listOfLists.add(currentPoints);
}
currentPoints = null;
repaint();
}
}
class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent mEvt) {
Positionable positionable = (Positionable) mEvt.getSource();
positionable.mouseDragged(mEvt.getPoint());
}
#Override
public void mousePressed(MouseEvent mEvt) {
Positionable positionable = (Positionable) mEvt.getSource();
positionable.mousePressed(mEvt.getPoint());
}
#Override
public void mouseReleased(MouseEvent mEvt) {
Positionable positionable = (Positionable) mEvt.getSource();
positionable.mouseReleased(mEvt.getPoint());
}
}
interface Positionable {
void mouseDragged(Point p);
void mousePressed(Point p);
void mouseReleased(Point p);
}

Categories

Resources