Drawing shapes in JPanel over an image - java

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();
}
});
}
}

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();
}
});
*/
}
}

How to clip a BufferedImage with a shape created with Graphics2D?(Java)

Currently, I'm working on my first Java application with which a user can look through photos, cut them and rotate. I have an issue with clipping an image. What I want to achieve is the following:
User clicks on the "Cut" option
Rectangle shape called by repaint method appears on the image
By stretching the rectangle user chooses the area for cutting
When the user releases the mouse(which stops stretching the rectangle) the area that is surrounded with the rectangle is left and all the rest of the image is cut.
As of for now I have several issues:
My image is centralized on JLabel which in its turn is added to JPanel and the last is added to JFrame, so now, when I want to add a rectangle above JLable (so it is to be located on the picture) it's invisible and is added only on JPanel directly.
I drew an image with paintComponent but can't figure out how to move and stretch it and repaint the rectangle again.
Below is the part of my code which (I hope) will describe my problems more precisely:
public class GraphicalUserInterface {
static JPanel background;
static JLabel labelIcon;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GraphicalUserInterface().go();
}
});
}
public void go() {
buildGui();
}
public void buildGui() {
frame = new JFrame("PicMove");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BorderLayout layout = new BorderLayout();
background = new JPanel(layout);
/**To center picture on the background**/
labelIcon = new JLabel();
labelIcon.setHorizontalAlignment(JLabel.CENTER);
labelIcon.setVerticalAlignment(JLabel.CENTER);
background.add(BorderLayout.SOUTH, bottom);
background.add(BorderLayout.PAGE_START, bar);
background.add(BorderLayout.CENTER, labelIcon);
background.add(BorderLayout.EAST, chatPanel);
frame.getContentPane().add(BorderLayout.CENTER, background);
frame.setJMenuBar(menuBar);
frame.setVisible(true);
frame.setSize(1300, 1200);}
static class CutImage extends JPanel implements Runnable {
boolean clip;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (clip) {
BasicStroke bs = new BasicStroke(50, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
10, null, 0);
g2d.setStroke(bs);
QuadCurve2D.Float qc = new QuadCurve2D.Float(20, 50, 100, 140, 460, 170);
g2d.setClip(qc);
}
BasicStroke bs = new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
10, new float[]{10}, 0);
g2d.setStroke(bs);
g2d.drawRect(260, 50, 80, 120);
}
#Override
public void run() {
CutImage cutPanel = new CutImage();
GraphicalUserInterface.background.add(cutPanel).repaint();
}
}
public class PicChanges implements Runnable{
static BufferedImage newImage;
static File [] selectedFile;
static int currentImage;
FileNameExtensionFilter filter;
JFileChooser fileChooser;
public void openPic() {
currentImage = 0;
try {
fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new java.io.File((System.getProperty("user.home"))));
filter = new FileNameExtensionFilter("*.images", "jpg", "gif", "png");
fileChooser.addChoosableFileFilter(filter);
fileChooser.setMultiSelectionEnabled(true);
int result = fileChooser.showOpenDialog(null);
if (result == JFileChooser.OPEN_DIALOG) {
selectedFile = fileChooser.getSelectedFiles();
for (File image : selectedFile) {
if ((image.isFile()) && (selectedFile.length > 0)){
newImage = ImageIO.read(selectedFile[0]);
GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
new ImageIcon(newImage).getImage().getScaledInstance(
450, 620, Image.SCALE_DEFAULT)));
} else if (result == JFileChooser.CANCEL_OPTION) {
System.out.println("No Pics Selected");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
Thread.currentThread().interrupt();
openPic();
}
public static void nextPic() {
currentImage++;
try {
newImage = ImageIO.read(selectedFile[currentImage]);
} catch (IOException e) {
e.printStackTrace();
System.out.println("No pictures left");
System.out.println("next"+currentImage);
}
GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
new ImageIcon(newImage).getImage().getScaledInstance(
450, 620, Image.SCALE_DEFAULT)));
}
static class NextPicture implements Runnable{
#Override
public void run() {
Thread.currentThread().interrupt();
nextPic();
}
}
public static void previousPic () {
currentImage--;
try {
newImage = ImageIO.read(selectedFile[currentImage]);
} catch (IOException e) {
e.printStackTrace();
System.out.println("previous "+currentImage);
}
GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
new ImageIcon(newImage).getImage().getScaledInstance(
450, 620, Image.SCALE_DEFAULT)));
}
static class PreviousPic implements Runnable{
#Override
public void run() {
Thread.currentThread().interrupt();
previousPic();
}
}
}
My idea was to add MouseListeners but can I add it to the shape created with Graphics2D?
I would be greatful for the help :)
Thank you
Being in search of finding a solution to this question I've asked two more questions(maybe it will be helpful for someone) :
1) Why BufferedImage is not cut according to the drawn in paintComponent method rectangle(its height is calculated wrong)?
2) Repaint() method doesn't invoke paint() & paintComponent() methods one by one, only paintComponent () method is working
that further helped me to find out the way.
My solution is to create a separate frame for displaying copied BufferedImage in it and on this frame to draw a rectangle with the help of MouseListeners. This is the piece of code:
public class ImageScreenShot extends JFrame implements MouseListener, MouseMotionListener {
#Override
public Dimension getPreferredSize() {
return super.getPreferredSize();
}
private static Thread screenShotThread;
public static Thread getScreenShotThread() {
return screenShotThread;
}
public static void setScreenShotThread(Thread screenShot) {
ImageScreenShot.screenShotThread = screenShot;
}
private int drag_status = 0, c1, c2, c3, c4;
public int getC1() {
return c1;
}
public int getC2() {
return c2;
}
public int getC3() {
return c3;
}
public int getC4() {
return c4;
}
class AdditionalPanel extends JLabel {
private BufferedImage img;
public BufferedImage getImg() {
return img;
}
public AdditionalPanel(BufferedImage img) {
this.img = img;
setPreferredSize(new Dimension(2560, 1600));
getPreferredSize();
setLayout(null);
}
#Override
public Dimension getPreferredSize() {
return super.getPreferredSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
System.out.println("Additional panel class paint method was invoked");
}
}
public void cut() {
AdditionalPanel apanel = new AdditionalPanel(PicChanges.getNewImage());
JScrollPane scrollPane = new JScrollPane(apanel);
scrollPane.addMouseMotionListener(this);
scrollPane.addMouseListener(this);
getContentPane().add(scrollPane, BorderLayout.CENTER);
setPreferredSize(new Dimension(2560, 1600));
getPreferredSize();
pack();
setVisible(true);
}
private void draggedScreen() throws Exception {
int w = c1 - c3;
int h = c2 - c4;
w = w * -1;
h = h * -1;
Robot robot = new Robot();
BufferedImage img = robot.createScreenCapture(new Rectangle(c1, c2, w, h));
File save_path = new File("screen1.jpg");
ImageIO.write(img, "JPG", save_path);
GraphicalUserInterface.getLabelIcon().setIcon(new ImageIcon(new ImageIcon(img).getImage().getScaledInstance(img.getWidth(), img.getHeight(), Image.SCALE_SMOOTH)));
JOptionPane.showConfirmDialog(this, "Would you like to save your cropped Pic?");
System.out.println("Cropped image saved successfully.");
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
repaint();
c1 = arg0.getXOnScreen();
c2 = arg0.getYOnScreen();
System.out.println("pressed");
}
#Override
public void mouseReleased(MouseEvent arg0) {
repaint();
if (drag_status == 1) {
c3 = arg0.getXOnScreen();
c4 = arg0.getYOnScreen();
try {
repaint();
draggedScreen();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void mouseDragged(MouseEvent arg0) {
repaint();
drag_status = 1;
c3 = arg0.getXOnScreen();
c4 = arg0.getYOnScreen();
}
#Override
public void mouseMoved(MouseEvent arg0) {
}
#Override
public void paint(Graphics g) {
super.paint(g);
int w = c1 - c3;
int h = c2 - c4;
w = w * -1;
h = h * -1;
if (w < 0)
w = w * -1;
g.setColor(Color.RED);
g.drawRect(c1, c2, w, h);
System.out.println("Paint component was invoked in imagescreenshot class");
}
P.S. I know that adding JFrame is not the best solution but I'm still in search for the best way to implement it so do not hesitate to comment on my code and tell what is wrong or what is good)

How to draw image through file path

I am attempting to have an image show up through file path. I have the image in my src folder, and I've tried maybe 5 different types of code.
Ignore the commented out code, those are just shapes i was drawing to test
I've tried it with both file path and file name and nothing has worked. Its not giving me any errors or anything either.
public void ImagePanel() {
try {
image = ImageIO.read(new File("BMan.jpg"));
} catch (IOException ex) {
System.out.println(ex);
}
}
public static void main(String[] args) {
JPanel panel = new MyPanel();
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
//g.setColor(Color.RED);
//g.fillOval(CircleX, CircleY, CircleH, CircleW);
//g.setColor(Color.GREEN);
//g.fillRect(SquareX, SquareY, SquareW, SquareH);
g.drawImage(image, 50, 50, this);
}
Here is my implementation of the component:
public class ImageView extends JComponent
implements ComponentListener {
private static final long serialVersionUID = 3761966077344495154L;
private BufferedImage image;
private int imageX;
private int imageY;
private int imageWidth;
private int imageHeight;
/** Creates a new instance of ImageView */
public ImageView() {
addComponentListener(this);
}
public BufferedImage getImage() {
return image;
}
public Rectangle getImageBounds() {
if (image == null) {
return null;
} else {
return new Rectangle(imageX, imageY, imageWidth, imageHeight);
}
}
public void setImage(final BufferedImage newValue) {
image = newValue;
computeBounds();
repaint();
}
#Override
public void paint(Graphics g) {
long tm = System.currentTimeMillis();
if (isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
BufferedImage img = image;
if (img != null) {
g.drawImage(img, imageX, imageY, imageWidth, imageHeight, null);
}
tm = System.currentTimeMillis()-tm;
}
public void componentResized(ComponentEvent e) {
computeBounds();
}
public void componentMoved(ComponentEvent e) {
}
public void componentShown(ComponentEvent e) {
computeBounds();
}
public void componentHidden(ComponentEvent e) {
}
private void computeBounds() {
BufferedImage img = image;
if (img != null) {
int width = this.getWidth();
int height = this.getHeight();
int wi = img.getWidth();
int hi = img.getHeight();
imageWidth = width;
imageHeight = height;
imageX = 0;
imageY = 0;
if (wi*height < hi*width) {
imageWidth = wi*height/hi;
imageX = (width-imageWidth)/2;
} else {
imageHeight = hi*width/wi;
imageY = (height-imageHeight)/2;
}
}
}
You need to create a JFrame where you will put the panel:
public static void main(String[] args) {
JFrame frame = new JFrame("title");
frame.setSize(400,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//all the other things for the jframe
JPanel panel = new MyPanel();
frame.add(panel);
frame.setVisible(true);
frame.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

Swing custom component dummy mouselistener

I am just starting to put together a logging tool for my own use that would log statistics from gym/running and the only experience I have with swing/awt is active rendering for games where you have full control over the Graphics2D object and don't rely on implementing swing components with overriden paints.
Anyway, I was hoping to create a dummy JComponent that I can add to one of my panels (this panel will display graphics, statistics etc depending on what I select from another different sidepanel with options) that does nothing else but listen for mouseevents inside the panel mentioned earlier and draws a selection rectangle on mousedrags so that I can zoom in the data if higher resolutions exist. I just don't know how, I have added the component to the panel but it registers nothing inside the panel, instead it seems to have a local space that is limited to the bottom of the panel/frame.
Here is the component
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
private Color color = new Color(0, 255, 0, 100);
public MarkerRectangle(int width, int height) {
setPreferredSize(new Dimension(width, height));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked# " + e.getX() + "," + e.getY());
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered # " + e.getX() + "," + e.getY());
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse left # " + e.getX() + "," + e.getY());
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed# " + e.getX() + "," + e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse was released # " + e.getX() + "," + e.getY());
}
#Override
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse being dragged, currently# " + e.getX() + ","
+ e.getY());
}
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("I registered a move# " + e.getX() + "," + e.getY());
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
The panel
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
this.add(graph);
this.add(new MarkerRectangle(800, 600));
this.setBackground(Color.gray);
this.setPreferredSize(new Dimension(800, 600));
}
}
The graph, holds random data atm
public class Graph extends JComponent {
private GeneralPath data;
private Stroke stroke;
public Graph() {
this.data = new GeneralPath();
this.stroke = new BasicStroke(3f);
this.setPreferredSize(new Dimension(750, 550));
init();
}
private void init() {
data.moveTo(0, 0);
double cntr = 0;
double[][] points = new double[10][1];
for (double[] point : points) {
cntr += 100;
point[0] = Math.random() * 100;
point[1] = Math.random() * 100;
data.lineTo(cntr, point[1]);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(stroke);
g2d.draw(data);
g2d.dispose();
}
}
I want to implement something like the above because eventually I imagine the GUI to become quite complex and simple events like drawing a rectangle to mark data should not be in the main controller (so as to prevent a lot of if-tests and clutter in code).
Screenshot of what I get:
What I want:
EDIT
While the accepted answer below is the better solution I am posting this in the event that someone may want to use it. It will not work if you resize smaller the prefferedSize.
public class Test {
public static void main(String[] args) {
GeneralJFrame frame = new GeneralJFrame(1200, 800);
frame.addPanel(new GraphPane(new Graph()), BorderLayout.CENTER);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.WEST);
frame.addPanel(new ButtonPane(), BorderLayout.EAST);
frame.addPanel(new ButtonPane(), BorderLayout.NORTH);
frame.start();
}
}
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 0;
this.setLayout(new GridBagLayout());
this.add(graph);
this.add(new MarkerRectangle(), c);
this.setBackground(new Color(205, 201, 201));
}
}
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
public MarkerRectangle() {
this.addMouseListener(this);
this.addMouseMotionListener(this);
setOpaque(false);
setPreferredSize(new Dimension(800, 600));
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
draw = true;
}
#Override
public void mouseReleased(MouseEvent e) {
draw = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
System.out.println(getSize());
if (!draw)
return;
int w = endX-startX;
int h = endY - startY;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(255, 165, 0));
g2d.fillRect(startX, startY, w, h);
g2d.dispose();
}
}
public class ButtonPane extends JPanel {
public ButtonPane() {
add(new JButton("HELLO"));
setBackground(Color.gray);
setBorder(BorderFactory.createEtchedBorder(Color.white,
Color.gray.darker()));
}
}
public class GeneralJFrame {
private JFrame frame;
public GeneralJFrame(int width, int height) {
this.frame = new JFrame("Le Muscles");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addPanel(JPanel panel, String location) {
this.frame.add(panel, location);
}
public void start() {
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Outputs: (orange is dragged with mouse)
I still don't fully understand your question. As I see it, you've
created several JPanels
Given one JPanel a MouseListener and MouseMotionListener
You've added that JPanel to the bottom of another JPanel.
The JPanel that's sitting on the bottom registers all mouse events as it has been told to do
So your program is behaving as expected based on your code.
The question I have is this: how is it not behaving as you expect it to?
If you expect that adding a JPanel with MouseListeners and MouseMotionListeners attached will result in all the JPanels of the GUI to be listened to, well of course that won't happen. To have more of the GUI register mouse events, you'll have to add MouseListeners and MouseMotionListeners to those components. And that is my answer so far to your question as I see it. If I didn't answer the true question you currently face, then please clarify it for us.
You state:
What I want is an invisible (transparent) panel on top of the blue one in the above screenshot that is just as large as the blue one, not a subpanel that is sitting in the bottom. I want the blue one to contain this one (should not be a problem since it is just a jcomponent). What I hope to achieve is a sort over "invisible" overlay that registers mousevents so I don't have to implement these events in the blue panel.
Consider using a JLayer for this. As per the JLayer API:
JLayer is a good solution if you only need to do custom painting over compound component or catch input events from its subcomponents.
OK, I've experimented with it a bit and came up with something like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
#SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
private static final int GRAPH_WIDTH = 1000;
private static final int GRAPH_HEIGHT = 750;
private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);
public GraphPane2() {
LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
setLayout(new BorderLayout());
add(panelLayer);
myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
Rectangle rect = (Rectangle) evt.getNewValue();
System.out.println(rect);
}
}
});
}
private static void createAndShowGui() {
GraphPane2 mainPanel = new GraphPane2();
JFrame frame = new JFrame("Graph Pane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
public static final String MOUSE_RELEASED = "mouse released";
private Point pressedPt;
private Point draggedPt;
private Rectangle rect;
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (rect != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FILL_COLOR);
g2.fill(rect);
}
}
public void installUI(JComponent c) {
super.installUI(c);
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
}
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
((JLayer)c).setLayerEventMask(0);
}
#Override
public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
MouseEvent mEvt = (MouseEvent) e;
int id = mEvt.getID();
int btn = mEvt.getButton();
if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
pressedPt = mEvt.getPoint();
rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
}
if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
pressedPt = null;
}
if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
}
if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
firePropertyChange(MOUSE_RELEASED, null, rect);
}
l.repaint();
}
}
#SuppressWarnings("serial")
class Graph2 extends JPanel {
private static final int MAX_DATA_POINTS = 100;
private static final int STEP = 10;
private static final Stroke STROKE = new BasicStroke(3f);
private Path2D path2D;
private int width;
private int height;
private int[] data = new int[MAX_DATA_POINTS + 1];
private Random random = new Random();
public Graph2(int width, int height) {
this.width = width;
this.height = height;
init();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
path2D = new Path2D.Double();
int w = getWidth();
int h = getHeight();
double x = 0;
double y = ((double) MAX_DATA_POINTS - data[0]) * h
/ MAX_DATA_POINTS;
path2D.moveTo(x, y);
for (int i = 1; i < data.length; i++) {
x = (i * w) / (double) MAX_DATA_POINTS;
y = ((double) MAX_DATA_POINTS - data[i]) * h
/ (double) MAX_DATA_POINTS;
path2D.lineTo(x, y);
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (path2D != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(STROKE);
g2d.draw(path2D);
}
};
private void init() {
// create and fill random data
data[0] = 0;
boolean up = true;
// max and min data values -- used for normalization
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 1; i < data.length; i++) {
up = random.nextInt(4) < 3 ? up : !up;
if (up) {
data[i] = data[i - 1] + random.nextInt(STEP);
} else {
data[i] = data[i - 1] - random.nextInt(STEP);
}
if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}
// normalize the data
for (int i = 0; i < data.length; i++) {
int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
data[i] = datum;
}
}
}
This will look like:

Categories

Resources