I'm trying to make a rectangle to move across the screen, but instead it's just repainting and not getting rid of the preceding rectangle making it look like a giant rectangle across the screen. Any help would be appreciated, here's my code:
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
#SuppressWarnings("serial")
public class Test extends JFrame implements ActionListener{
int x=50;
Timer tm = new Timer(30,this);
public static void main(String[] args){
new Test();
}
public Test(){
this.setSize(700, 500);
this.setTitle("Drawing Shapes");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void paint(Graphics g){
Graphics2D graph2 = (Graphics2D)g;
Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
graph2.setColor(Color.RED);
graph2.fill(Rect);
tm.start();
}
public void actionPerformed(ActionEvent e){
x=10+x;
repaint();
}
}
Draw in a JPanel that is held by and displayed within the JFrame. Don't draw directly in the JFrame as this risks drawing over things that shouldn't be messed with such as root panes, borders, child components,... Also you lose the benefits of automatic double buffering by drawing directly in the JFrame's paint method leading to choppy animation.
You should override the JPanel's paintComponent method not its paint method.
Always call the super's painting method in your own painting method (here it again should be paintComponent). This is your problem. So again, your paintComponent(Graphics g) override painting method should call super.paintComponent(g); on its first line. This will erase the old images.
You're breaking the paint chain...
public void paint(Graphics g){
// Broken here...
Graphics2D graph2 = (Graphics2D)g;
Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
graph2.setColor(Color.RED);
graph2.fill(Rect);
tm.start();
}
You MUST call super.paint. See Painting in AWT and Swing and Performing Custom Painting for more details about painting in Swing...
Top level containers are not double buffered and it is not recommended to paint directly to them, instead, create a custom component which extends from something like JPanel and override it's paintComponent method (calling super.paintComponent before performing any custom painting)
As a general rule of thumb, you should avoid extending from top level containers like JFrame, as you are not adding any new functionality to the class and they lock you into a single use-case, reducing the re-usability of your classes
DON'T call tm.start inside the paint method, you should do nothing in the paint methods except paint, never try and modify the state or otherwise perform an action which might indirectly modify the state of a component, this is a good way to have you program consume your CPU
To add to what other have already stated,
I've noticed that you use this.setLocationRelativeTo(null) for a simple application. Not saying it is bad, but you might want to check this thread to make sure it is what you want.
How to best position Swing GUIs?
Related
Background:
I'm tearing my hair here because I've used awt and Swing a couple of times, and I always run into a roadblock during my initial graphics setup and I never seem to have an "Of course, this thing again! I just have to something something"-moment. This time I can't seem to get even basic drawing to work!
Problem description:
I wish to draw a number of simple boxes in a JFrame (via a JPanel, if necessary). To this end I have a Sprite extends JComponent class which currently only draws simple geometric shapes but will eventually do things with BufferedImageor somesuch.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class Sprite extends JComponent {
private Rectangle rctBounds;
private Color clrFill;
public Sprite(int x, int y, Color color) {
this.rctBounds = new Rectangle(x, y, 100, 100);
this.clrFill = color;
}// Sprite(int,int,Color)
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(clrFill);
g.fillRect(rctBounds.x, rctBounds.y, rctBounds.width, rctBounds.height);
}// paintComponent(Graphics)
}// Sprite.class
The window for these to go in is obtained by a simple Win extends JFrame class á:
import javax.swing.JFrame;
public class Win extends JFrame{
public Win(){
setSize(800,600);
setLocation(100,100);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
Finally, components are initialized and connected in the main method, in the class run, using JComponent's (I believe) add() method, called on an instance of Win:
import java.awt.Color;
public class run {
public static void main(String[] args) {
Win w = new Win();
Sprite s1 = new Sprite(10,100, Color.BLUE);
Sprite s2 = new Sprite(200,200, Color.RED);
Sprite s3 = new Sprite(300,200, Color.CYAN);
w.add(s1);
w.add(s2);
w.add(s3);
}//main()
}//run.class
Expectations vs. Reality:
Now, I expect a window with three colored boxes, as in this image (all boxes visible). Instead, only the final JComponent to be added shows up, as seen here (only cyan box painted). What's even more infuriating is that attempting to edit the JFrame's layout or adding a JPanel as an intermediary container for the Sprites results in a blank window.
Exasperated Plea:
I'm at a complete loss! Everything I find on here talks about improper use of paintComponent(Graphics) (using paint() instead, forgetting to #Override, not propagating the graphical context properly, etc) or to use a JPanel as an intermediary container. The most infuriating part of all this is that I've built two functioning programs using Swing before (school assignments) and, looking at that code, I cannot for the life of me figure out what I'm doing wrong here! Please help!
First class
package com.mudd.render;
import java.awt.Dimension;
import javax.swing.JFrame;
import com.mudd.game.Game;
public class render {
int width = 500;
int height = 600;
Game g = new Game();
public void show(){
JFrame gameWindow = new JFrame("..");
gameWindow.setPreferredSize(new Dimension(width, height));
//gameWindow.setIconImage(new ImageIcon(imgURL).getImage());
gameWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameWindow.pack();
gameWindow.add(g);
gameWindow.setVisible(true);
}
public static void main(String[] args) {
render game = new render();
game.show();
}
}
Second class
package com.mudd.game;
import java.awt.Graphics;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel {
public void paint(Graphics g){
g.fillOval(10, 10, 500, 500);
System.out.println("Test");
}
}
What is causing my Test print statement to be printed twice? If I add other priintlns it will also print them both out. I've been learning Java from Head First Java and I've done other small command line projects but nothing like this has ever happened to me.
Swing graphics are passive -- you don't call the painting methods directly yourself, but rather the JVM calls them. They are sometimes possibly called at your suggestion such as when you call repaint() but even this is never a guarantee, and they are sometimes possibly called at the suggestion of the platform, such as when it determines that your application has "dirty" pixels that need cleaning. So you have to plan for this -- the painting method should contain no code that changes the state of the object nor should it contain business logic code. Instead it should have code for painting and nothing more.
For more details on this, please see:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
Side recommendations:
Override the JPanel's paintComponent method, not its paint method
Use the #Override annotation for any method override
Don't forget to call the super's method in your override.
I'm trying to create a simple application that draws graphics...
package Tests.Drawing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.border.EtchedBorder;
public class DrawingTest extends JFrame
{
private Canvas drwArea;
private JButton btnClear;
public static void main(String[] args)
{
DrawingTest StartForm = new DrawingTest();
StartForm.setVisible(true);
}
public DrawingTest()
{
//Window...
this.setTitle("Drawing objects test00");
this.setBounds(0,0,510,500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
//Drawing area...
drwArea = new Canvas();
drwArea.setBounds(0, 0, 400, 450);
drwArea.setBackground(Color.WHITE);
drwArea.setOpaque(true);
drwArea.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
drwArea.addMouseMotionListener(new MouseMotionAdapter()
{
#Override
public void mouseDragged(MouseEvent e)
{
//Write code to paint on the image...
}
});
this.getContentPane().add(drwArea);
//Clear button...
btnClear = new JButton("Clear");
btnClear.setBounds(410,50,70,30);
btnClear.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//Write code to clear the image...
}
});
this.getContentPane().add(btnClear);
}
private class Canvas extends JLabel
{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//The idea of overriding this method is
//achieving persistence...
}
}
}
I have seen that the typical component to draw on is a Jlabel (Is there anyone better by the way?). Using the method “getGraphics” I can use an object that has several methods to paint on the component. My problem is that instead of painting directly on the JLabel, I would like to paint on an image (in memory) and once the painting has finished, send the result to the JLabel. How can I do this? I'm a bit lost...
Thanks in advance.
I would like to paint on an image (in memory)
I suggest that you then create a BufferedImage object of the desired size, get its Graphics2D object by calling createGraphics() on it, and draw on it using this Graphics2D object.
and once the painting has finished, send the result to the JLabel
Then create an ImageIcon out of the BufferedImage above by simply calling
Icon myIcon = new ImageIcon(myBufferedImage);
and then setting the JLabel's icon via myLabel.setIcon(myIcon);
Also you could draw to a BufferedImage that is being displayed within a JPanel's paintComponent method, and this may be an even better way to go if you want to update the image while the program is running. For more on this, please have a look at some of these examples.
Other comments:
Don't draw with a Graphics object obtained by calling getGraphics() on a component. This will return a Graphics object that is short lived, risking disappearing graphics or worse, a NullPointerException. Instead, draw in the JPanel's paintComponent(...) method either directly, or indirectly by drawing on a BufferedImage (yes, you can get its Graphics object via getGraphics()) and then drawing the BufferedImage to the GUI within the paintComponent method.
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
I need the simplest way to draw a line between to coordinates.
Since this drawing line in my code will be repeated more than 200 times in a loop i need the easiest way. I'm drawing the lines in a AWT panel component.
If you want to switch to Swing you would use the JPanel and overwrite the paintComponent() method.
import java.awt.Graphics;
import javax.swing.JPanel;
public class PanelWithLine extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(x1,y1,x2,y2);
}
}
You can than redraw everything by calling repaint() on your Jpanel.
You would probably change the coordinates and then call the repaint() method in your loop.
It's been a long time since I've used java.awt.Panel, but it should be something like:
class Foo extends Panel {
public void paint(Graphics g) {
super.paint(g);
g.drawLine(x1,y1,x2,y2);
g.drawLine(x3,y3,x4,y4);
//...
}
}
This code repaints a random oval, rectangle, and line with different colors on the screen. The problem is that I'm trying to change the background color each time it repaints, but the backgrounds doesn't repaint. Please help.
package events;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class Paint extends JPanel {
static Paint g;
boolean change = true;
int x = 0;
int y = 0;
public void paintComponent(Graphics g) {
g.fillOval(x, y, 50, 50);
}
void circles() {
Color color = new Color(0, 0, 0);
repaint();
}
}
Get rid of the static Paint variable. Your class doesn't need any static variables.
how would i change the background then?
The painting method should NOT change the properties of the class. It should only paint the component based on the current properties.
So you need to do something like:
Create a method like setRandomBackground(). This method will create a Color object that you can use in the paintComponent() method. At the end of this method you should invoke repaint(), which will tell Swing to paint the component.
Get rid of new Paint()
get rid of Thread.sleep() // never tell a painting method to sleep.
get rid of repaint() // never tell a painting method to repaint itself, this will create an infinite loop.
Finally, read the Swing tutorial on Custom Painting for more information and working examples to learn the basics.
Also, variable names (R, G, B) should NOT be upper cased.