I have a simple gui and when I run is it is perfect, but occasionally it doesn't display right. I have a circle in a JPanel. When it runs right it displays in the middle for the JFrame like I intended it to, but when it does not work right the circle appears closer to the bottom of the screen. How can I fix this so it displays right each time? Is my code incorrect (I hope not! :) ), or is it some bug in java. So here is my code:
Update: It seems that the window height is changing.
Run.java--------------------------------------------------------------------------------
import javax.swing.JFrame;
public class Run {
public static void main(String args[]) {
Window w = new Window();
w.setSize(800, 500);
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.setVisible(true);
}
}
Window.java----------------------------------------------------------------------------
import javax.swing.JFrame;
public class Window extends JFrame {
public Window() {
super("Wheel");
Gui g = new Gui();
add(g);
}
}
Gui.java--------------------------------------------------------------------------------
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Gui extends JPanel {
private Color wheelColor = new Color(0, 0, 255);
public Gui() {
setOpaque(true);
setBackground(new Color(255, 0, 0));
}
public void paintComponent(Graphics g) {
g.setColor(wheelColor);
g.fillOval(40, 40, 420, 420);
}
}
Also I'm using Ubuntu, I don't know if that would affect it at all. Thanks in advance.
Make sure you create the UI within the context of the Event Dispatching Thread, see Initial Threads for more details.
Make sure you are calling super.paintComponent within the paintComponent method before you do any custom painting, see Painting in AWT and Swing for more details
Don't rely on magic numbers, the size of the window is the size of it's content + its frame decorations. You should get using getWidth and getHeight to determine the size of the Gui panel and overriding getPreferredSize to allow the window to determine how much room it needs to (optimally) display it
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Run {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Window w = new Window();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
}
});
}
public static class Window extends JFrame {
public Window() {
super("Wheel");
Gui g = new Gui();
add(g);
}
}
public static class Gui extends JPanel {
private Color wheelColor = new Color(0, 0, 255);
public Gui() {
setOpaque(true);
setBackground(new Color(255, 0, 0));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(440, 440);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(wheelColor);
int width = getWidth();
int height = getHeight();
int x = (width - 420) / 2;
int y = (height - 420) / 2;
g.fillOval(x, y, 420, 420);
}
}
}
Also beware of naming your classes after classes that already exist within the default API, java.awt.Window already exists and can cause confusion not only for yourself, but for other developers ;)
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'm sure this is a simple answer, but I can't figure it out.
I am trying to make a basic shape that I can control in a window. Obviously it will be more involved when the whole project is complete, but I am working on the early steps still. I am using WindowBuilder to make the layout, and have a JPanel and a JButton. The JPanel draws a rectangle, and has a method to move it. The JButton calles that moving command. And that's it. The problem is in the repaint. The shape keeps all old versions of itself, and the button makes weird copies of itself. All these go away when I resize the window, which I thought was the same as calling repaint. Again, I'm sure is something simple I am missing. Below are my 2 classes.
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.BorderLayout;
import javax.swing.JPanel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Drawing {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Drawing window = new Drawing();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Drawing() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
drawpanel panel = new drawpanel();
panel.setBounds(58, 68, 318, 182);
frame.getContentPane().add(panel);
JButton btnMove = new JButton("move");
btnMove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.moves();
}
});
btnMove.setBounds(169, 34, 89, 23);
frame.getContentPane().add(btnMove);
}
}
^ This one, besides the buttonListener, was autocreated by WindowBuilder.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class drawpanel extends JPanel {
int x = 50, y = 50;
int sizeX = 50, sizeY = 50;
public void paintComponent( Graphics g) {
super.paintComponents(g);
g.setColor(Color.BLACK);
g.drawRect(x, y, sizeX, sizeY);
}
public void moves() {
x +=5;
repaint();
}
}
^ This one has my drawing of the shape and the moving/repainting method. It was written mostly from other examples I found on this site.
Thanks to any help in advanced.
public void paintComponent(Graphics g) {
super.paintComponents(g); // wrong method! (Should not be PLURAL)
Should be:
public void paintComponent(Graphics g) {
super.paintComponent(g); // correct method!
This question already has an answer here:
netBeans gui problem
(1 answer)
Closed 8 years ago.
I'm trying to use canvas in java. I'm using netbeans GUI builder to add several canvases to the window but I'm not sure how to for example draw the line or rectangle on them. I read several manuals how to do that but I'm still beginner in Java and I didn't quite understand what am I supposed to do. The constructor of the class looks like this:
public Classname() {
initComponents();
canvas1.setBackground(Color.red); // That works.
// Now I want to (for example) draw a line on the canvas1 (or some other canvas)
}
Could somebody please explain me what code should I write and where to put it? Thanks in advance. (Sorry for my english.)
Assuming your mean java.awt.Canvas, I'd recommend that you shouldn't be using it. Two main reasons, one, it's a heavy weight component, which introduces a list of issues when mixed with Swing/lightweight components and two, it's not double buffered, which just adds additional overheads you're going to have to deal with.
The preferred means by which to perform custom painting is to generally create a new class that extends from JPanel and the override it's paintComponent method for example
public class PaintPane extends JPanel {
public PaintPane () {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
g2d.dispose();
}
}
This will draw a simple line across the middle of the panel.
You can then drag the class from the "Projects" tab into the form editor (and onto an existing form container), like you would with components from the palette
Take a look at Performing Custom Painting and 2D Graphics for more details
Updated with example based on comments
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawExample {
public static void main(String[] args) {
new DrawExample();
}
private DrawPane drawPane;
public DrawExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
drawPane = new DrawPane();
JButton addRect = new JButton("Add Rectangle");
addRect.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int width = drawPane.getWidth() - 1;
int height = drawPane.getHeight() - 1;
int x = (int)(Math.random() * (width - 5));
int y = (int)(Math.random() * (height - 5));
width -= x;
height -= y;
int rectWidth = (int)(Math.random() * width);
int rectHeight = (int)(Math.random() * height / 2);
drawPane.addRect(x, y, rectWidth, rectHeight);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(drawPane);
frame.add(addRect, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawPane extends JPanel {
private List<Shape> shapes;
public DrawPane() {
shapes = new ArrayList<Shape>(25);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape shape : shapes) {
g2d.draw(shape);
}
g2d.dispose();
}
public void addRect(int x, int y, int width, int height) {
shapes.add(new Rectangle(x, y, width, height));
repaint();
}
}
}
I am trying to use JLayeredPane to overlay one JPanel on top of another. For some reason the added panels don't show up.
This is the code that creates the JLayeredPane and adds elements to it:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.SwingUtilities;
public class CarAnimator extends JFrame {
public CarAnimator()
{
JLayeredPane racingOverlay = new JLayeredPane();
CarAnimatorJPanel animation = new CarAnimatorJPanel();
Racetrack racetrack = new Racetrack();
racingOverlay.add(racetrack,JLayeredPane.DEFAULT_LAYER);
racingOverlay.add(animation,new Integer(2));
racingOverlay.setBorder(BorderFactory.createTitledBorder("Can't see a thing"));
this.getContentPane().add(racingOverlay,BorderLayout.CENTER);
this.setLocationByPlatform(true);
this.setPreferredSize(new Dimension(850,650));
this.setMaximumSize(new Dimension(850,650));
this.setMinimumSize(new Dimension(850,650));
this.setResizable(false);//prevents user from resizing the window
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> {
(new CarAnimator()).setVisible(true);
});
}
}
This is the code for the racing track JPanel:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Racetrack extends JPanel
{
Graphics g = this.getGraphics();
#Override
public void paintComponent(Graphics g)
{
Color c1 = Color.green;
g.setColor( c1 );
g.fillRect( 150, 200, 550, 300 ); //grass
Color c2 = Color.black;
g.setColor( c2 );
g.drawRect(50, 100, 750, 500); // outer edge
g.drawRect(150, 200, 550, 300); // inner edge
Color c3 = Color.yellow;
g.setColor( c3 );
g.drawRect( 100, 150, 650, 400 ); // mid-lane marker
Color c4 = Color.white;
g.setColor( c4 );
g.drawLine( 425, 500, 425, 600 ); // start line
}
}
And this is the JPanel animation example, taken from Java: How to Program book (http://java.uom.gr/~chaikalis/javaLab/Java_HowTo_9th_Edition.pdf), adapted to work with my code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CarAnimatorJPanel extends JPanel
{
protected ImageIcon images[];
private int currentImage=0;
private final int ANIMATION_DELAY=50;
private int width;
private int height;
private Timer animationTimer;
public CarAnimatorJPanel()
{
try
{
File directory = new File("C://Users/eltaro/Desktop/Car images");
File[] files = directory.listFiles();
images = new ImageIcon[files.length];
for (int i=0;i<files.length;i++)
{
if (files[i].isFile())
{
images[i]=new ImageIcon(ImageIO.read(files[i]));
}
}
width = images[0].getIconWidth();
height = images[0].getIconHeight();
}
catch(java.io.IOException e)
{
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
images[currentImage].paintIcon(this,g,0,0);
if(animationTimer.isRunning())
{
currentImage=(currentImage+1)%images.length;
}
}
public void startAnimation()
{
if(animationTimer==null)
{
currentImage=0;
animationTimer=new Timer(ANIMATION_DELAY, new TimerHandler());
animationTimer.start();
}
else
{
if(!animationTimer.isRunning())
{
animationTimer.restart();
}
}
}
public void stopAnimation()
{
animationTimer.stop();
}
#Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(width,height);
}
private class TimerHandler implements ActionListener
{
#Override
public void actionPerformed(ActionEvent actionEvent)
{
repaint();
}
}
}
When I add the two JPanels to the JLayeredPane inside CarAnimator constructor, they fail to show:
Some recommendations:
Since I'm guessing that your CarAnimatorJPanel sits on top of a background JPanel, you'd better set CarAnimatorJPanel to be non-opaque via setOpaque(false) so that the JPanel behind it can be seen.
When adding components to a JLayeredPane, you're essentially adding the components to a null layout. This means that you absolutely need to set the sizes (or override getSize()) of those components, not the preferred sizes.
As always, when dealing with image files, debug that you're able to adequately read in the image in a separate small test program. As usual, consider getting the image in as a resource and not as a File.
You have some program logic code in your paintComponent(Graphics g) method -- you change the state of the currentImage field. Never do this since you never have full control over when or even if paintComponent is called. This bit of code belongs in your animation loop, your Swing Timer's ActionListener.