I am very new to graphics with Java, so just ask if any additional information is needed :)
I am trying to paint shapes based on where the mouse clicks on the screen. Because of this, I need to pass the x and y coordinates of where I clicked to the paintComponent() method so that it will know where to paint the shape.
public void mouseClicked(MouseEvent e) {
System.out.println("Adding Shape");
repaint();
}
class CanvasDrawArea extends JPanel{
//this should run when the program first starts
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
canvas.setBackground(CANVAS_COLOR);
}
//here is where the question is
public void paintComponent(Graphics g, int x, int y){
super.paintComponent(g);
g.fillRect(x, y, RECTANGLE_WIDTH, RECTANGLE_HEIGHT);
}
}
basically I am trying to overload the paintComponent by having one that runs right when the program starts by calling the repaint() / pack() method, and one that will run when I give it the x and y coordinates. I am unsure, however, how I am supposed to go about passing the x and y parameters, as there is no way to pass them in the repaint() method.
You should never have the need to call paintComponent or paint directly these are called automatically by the repaint manager when required...
Create java.util.List and store the Point of each mouse click in it, call repaint from the mouseClicked method after you have done this.
In your paintComponent(Graphics) method, iterate over the List of points and paint the shape you need.
As a simple example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
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.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Dotty {
public static void main(String[] args) {
new Dotty();
}
public Dotty() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DottyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DottyPane extends JPanel {
private List<Point> points;
public DottyPane() {
points = new ArrayList<>(25);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Point p : points) {
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
}
g2d.dispose();
}
}
}
Related
I have a 3d-object composed of multiple polygons that I draw using graphics2D. When I rotate it it, it seems as if it has not enough time to draw the entire object at every frame since at some frames, some of the polygons are simply missing(not drawn). I don't understand how that can be since I in paintComponent first draw all the polygons onto the bufferedImage myImg, and then draw the finished image onto the screen. When I remove clearRect, this issue is resolved but then of course it doesn't remove the last frame's drawing before drawing the next.
Note: I'm an amateur but I've tried really hard understanding and so this is my last resort and would be really glad to get some help. The code (with unnecessary code removed is as follows) :
public class Main {
long temp = System.currentTimeMillis() + frameRate;
public static void main(String[] args) {
myGUI = new GUI(width, height);
while(true) {
if (System.currentTimeMillis() >= temp) {
temp += frameRate;
rotateObject();
myGUI.myCanvas.myLabel.repaint();
}
}
}
}
public class GUI extends JFrame {
public Canvas myCanvas;
public GUI(int w, int h) {
this.setSize(w, h);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myCanvas = new Canvas(w, h);
this.getContentPane().add(myCanvas);
this.setVisible(true);
this.pack();
}
}
public class Canvas extends JPanel {
public BufferedImage myImg;
public Graphics2D g2d;
public JLabel myLabel;
public Canvas(int w, int h) {
myImg = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
myLabel = new JLabel(new ImageIcon(myImg));
this.add(myLabel);
g2d = myImg.createGraphics();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d.clearRect(0, 0, myImg.getWidth(), myImg.getHeight());
g2d.setColor(Color.RED));
g2d.fillPolygon(pointsX, pointsY, 3);
g.drawImage(myImg, 0, 0, null);
}
}
This is how my object is flickering
You really need to take the time to read through:
Performing Custom Painting
Painting in AWT and Swing
2D Graphics
Concurrency in Swing
How to Use Swing Timers
These aren't "beginner" topics and a reasonable understanding of Swing in general and the language in particular would be very advantageous.
Don't, ever, use getGraphics on a component. This is simply a bad idea (and I have no idea why this method is public).
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage myImg;
private double rotation;
public TestPane() throws IOException {
myImg = ImageIO.read(getClass().getResource("/images/happy.png"));
Timer timer = new Timer(33, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rotation += Math.toRadians(5);
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = AffineTransform.getTranslateInstance((getWidth() - myImg.getWidth()) / 2, (getHeight() - myImg.getHeight()) / 2);
at.rotate(rotation, myImg.getWidth() / 2, myImg.getHeight() / 2);
g2d.transform(at);
g2d.drawImage(myImg, 0, 0, this);
g2d.dispose();
}
}
}
I'm newbie in Java and I want to move a rectangle down in a JFrame. I want to see this movement.
Why can't I see the rectangle moving down? Do I have to use other library or what?
import java.awt.Color;
import java.awt.Graphics;
import java.lang.Thread;
import javax.swing.JComponent;
public class Draw extends JComponent {
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 128, 128, 128));
try {
for(int i = 70; i < 100; i++) {
g.fillRect(40, i, 100, 70);
Thread.sleep(10); // To see the moviment.
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
import javax.swing.JFrame;
public class Main extends JFrame {
public Main() {
setSize(300, 200);
Draw draw = new Draw();
add(draw);
}
public static void main(String[] args) {
Main m = new Main();
m.setVisible(true);
}
}
Swing is single threaded
This means that if you perform any long running or blocking operations within the context of the Event Dispatching Thread, you will prevent Swing from performing any painting operations or processing using input
Swing is not thread safe
This means that you should never try and update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread, this can run you into all kinds of weirdness and unpredictable states
See Concurrency in Swing for more details
A simple solution...
Possibly the simplest solution is to use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Draw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Draw extends JPanel {
private int yPos = 0;
public Draw() {
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
yPos += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 128, 128, 128));
g2d.fillRect(40, yPos, 100, 70);
g2d.dispose();
}
}
}
See How to Use Swing Timers for more details
Why use a JPanel over a JComponent?
For simplicity. A JPanel will paint it's background (using the background property) automatically (if you call super.paintComponent) saving your the hassle
Your "time" consideration is not following Swing UI threading concept.
Think of it this way: Swing has a Frame, you paint the content, then wait for something to change, and paint again.
A suggestion could be a Thread, that every x milliseconds, requests the JFrame to update its content and then by using repaint() method.
An example:
Draw() {
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Move x and y of the desired rectangle and request repaint
x+=10;
repaint();
}}, 50);
}
I am trying to create a graphics drawing program that allows the user to draw red pixels on the screen by dragging their mouse over it. So in a way, you can think of this program as Microsoft's Paint program but with only the pencil drawing tool and color red.
Unfortunately the mouseDragged() function in my program is not working properly. It will skip some of the pixels on the screen if I move my mouse too fast, like this:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameView extends JFrame {
JPanel panel;
Graphics2D drawingContext;
public static void main(String[] args) {
new FrameView();
}
public FrameView() {
panel = new JPanel();
panel.addMouseMotionListener(new MouseControls());
panel.setBackground(Color.WHITE);
this.add(panel);
this.setSize(new Dimension(500, 500));
this.setTitle("Drawing Program");
this.setVisible(true);
drawingContext = (Graphics2D)panel.getGraphics();
}
private class MouseControls extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getX();
int y = e.getY();
final int WIDTH = 1;
final int HEIGHT = 1;
Shape pixel = new Rectangle(x, y, WIDTH, HEIGHT);
drawingContext.setColor(Color.RED);
drawingContext.draw(pixel);
}
}
}
getGraphics is NOT how painting works in Swing, instead, you should be overriding the paintComponent method of the component and performing your custom painting there.
Painting is destructive, it is expected that when ever paintComponent is called, you will completely repaint the current state of the component.
Have a look at Painting in AWT and Swing and Performing Custom Painting for more details
As to you "mouse" problem, this is actually how it works, you won't be notified of EVERY pixel position the mouse has to pass through, your mouse would lag horribly across the screen if it did. Instead, the OS moves the mouse in ever increasing steps based on the speed of the movement of the user input.
Instead of drawing just the points, draw lines between them, for example
nb: I've deliberately painted the points larger so you can see where they are been reported, you will see that all the dots (for a single drag) are connected
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<List<Point>> points = new ArrayList<>(25);
private List<Point> activeList;
public TestPane() {
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (activeList != null) {
activeList.add(e.getPoint());
repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
activeList = new ArrayList<>(25);
points.add(activeList);
}
#Override
public void mouseReleased(MouseEvent e) {
if (activeList != null && activeList.isEmpty()) {
points.remove(activeList);
}
activeList = null;
}
};
addMouseMotionListener(ma);
addMouseListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (List<Point> group : points) {
Point previous = null;
for (Point p : group) {
// You can get rid of this, it's simply to show
// where the points would actually be rendered
g2d.fill(new Ellipse2D.Float(p.x - 2, p.y - 2, 4, 4));
if (previous != null) {
g2d.draw(new Line2D.Float(previous, p));
}
previous = p;
}
}
g2d.dispose();
}
}
}
So Im drawing a BufferedImage 'bird' but I want to rotate it according to the angle that it is falling. I have a bird object which contains the BufferedImage and a render() method which draw it rotated.
public void render(Graphics2D g, ImageObserver io) {
double theta = Math.tan((height - pastHeight) / .875);
System.out.println(theta);
Graphics2D g2 = (Graphics2D) bird.getGraphics();
g2.drawImage(bird, 100, (int) height, null);
g2.rotate(theta);
g2.drawImage(bird, 100, (int) height, io);
}
I call this as such
bird.render(g2, ???);
in my paintcomponent method in my jcomponent.
only problem is I dont know what to use as my ImageObserver... I've tried passing in my JFrame and my JComponent but the image no longer appears when I do that... what would I pass in for the image to appear in my window and/or how else would I achieve this rotation?
Assuming that you are doing this in something that extends JComponent, you should use
bird.render(g2, this);
As JComponent implements ImageObserver
The problem with the image disappearing isn't an issue with the ImageObserver but the point around which the rotation is occurring, which I believe is the top/left corner of the Graphics context.
Try using Graphics2D#rotate(double, int, int) which will allow you to specify the origin points of the rotation (pivot point).
Don't forget to reset your translations, as they will effect everything that is painted after your supply them and may be re-used in subsequent paint cycles.
Updated with simple example
This is a basic example that demonstrates the different uses of rotate.
First, I simply used Graphics#rotate(double)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateImage {
public static void main(String[] args) {
new RotateImage();
}
public RotateImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage img;
private double angel = 0d;
public TestPane() {
try {
img = ImageIO.read(...);
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angel += 5;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.rotate(Math.toRadians(angel));
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
}
}
Then I replaced g2d.rotate(Math.toRadians(angel)); with g2d.rotate(Math.toRadians(angel), getWidth() / 2, getHeight() / 2);, which used the center position of the component (or the Graphics context) as the anchor point around which the rotation would occur...
Now, because you only want to rotate your image, you're going to need to calculate the anchor point around the current position of the image's center position (assuming you want it to rotate around the middle)
I'm working on a software solution for a small workflow editor. For this I created an own JPanel with some functionality like deleting itself or editing the main information.
This is how it looks:
The point is, that i need a dynamic connector like a arrow or something like that.
I tried it with drawline but its not dynamic and looks not well. I mean if I move one of the boxes so the drawed line have to change its position too.
The boxes in the big JPanel are movable and resizable. The connection point right and left are JButtons. The structure is that any outgoing connections startes from the right and incomes to the left JButton.
Any ideas how to set it up?
I can't post much of the source code, because the software is for a company.
Did you convert Graphics object to Graphics2D and set the RenderHints? i.e
Graphics2D g2d=(Graphics2D)g;
g2d.setRenderingHint(RenderHints.KEY_ANTIALIASING,RenderHints.VALUE_ANTIALIASING _ON);
This will add some nice anti aliasing effects and might make the line appearance straighter.
Also increasing the stroke width via Graphics2D#setStroke will make the jaggeder edges disappear as its now thicker.
See this example (press, drag and release mouse to create a line):
With g2d.setRenderingHint(..) and g2d.setStroke(..) within paintComponent(..) commented out:
With g2d.setRenderingHint(..) and g2d.setStroke(..) uncommented:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Test gui = new Test();
}
});
}
public Test() {
initComponents();
}
private void initComponents() {
JFrame frame = new JFrame("Line Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MyPanel());
frame.pack();
frame.setVisible(true);
}
}
class MyPanel extends JPanel {
Point point1;
Point point2;
Line2D line2d;
public MyPanel() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
super.mousePressed(me);
point1 = me.getPoint();
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
super.mouseDragged(me);
point2 = me.getPoint();
line2d = new Line2D.Double(point1, point2);
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//Set anti-alias!
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (point1 != null && point2 != null) {
g2d.setPaint(Color.RED);
g2d.setStroke(new BasicStroke(1.5f));//set stroke size
g2d.draw(line2d);
}
}
}
If above does not help, posting an SSCCE would enable us to test and see what could be at fault/make it better.