In Java, I have successfully displayed an image on the screen. Now I want to make it move by running it through a for loop. The for loop runs 10 times and sleeps for 1 second each time. Instead of moving the image every second as expected, I have to wait 10 seconds, then 10 images show up.
Here's my code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class ImageDraw extends JComponent {
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Image img1 = Toolkit.getDefaultToolkit().getImage("player.png");
int x = 0;
int y = 0;
for(int i = 0;i<10;i++){
try {
Thread.sleep(1000);
x+=10;
y+=10;
g2.drawImage(img1, x, y, this);
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(0);
}
} //end for
} //end paint
} //end class
How would I make it so the image looks as if it's moving everytime it runs through the loop?
In addition to other suggestions:
Never read the image in the painting method. Read the image when you construct the class or pass in the image with a setImage(...) method.
Custom painting is done by overriding the paintComponent() method, not the paint() method.
Use a Timer for this, and get rid of the Thread.sleep(). You're running this code on the UI thread, and when you call Thread.sleep() you're putting the UI thread to sleep, which means no updating until the entire loop is complete.
Related
I have this code and its supposed to have the text "Simple Animation" scroll across the screen in swirling colors. Right now, it does that, but even after the text moves along, the color still stays. I was wondering if there was a way to have text in the background. For example, I was thinking I could just print out the exact same "Simple Animation" but in the same color as the background and about 10 pixels behind the actual text. However, when I tried this, the white text (that's the background color) just covered the swirling colors. I tried googling if I could have background text, but from I read, the only thing that a background can do is set the color. So, is there a way to have text in the background in a Java Graphics file?
Here is my Code:
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import javax.swing.*;
public class Scrolling_Sign extends JApplet implements Runnable {
String mesag = "Simple Animation";
Font f = new Font("Bauhaus 93",Font.BOLD,72);
Color colors[] = new Color[100000];
Thread runThread;
int Xposition = 600;
public void init() {
setBackground(Color.white);
}
public void start() {
if (runThread == null) {
runThread = new Thread(this);
runThread.start();
}
}
public void stop() {
if (runThread != null) {
runThread.stop();
runThread = null;
}
}
public void run() {
float c = 0;
for (int i = 0; i < colors.length; i++) {
colors[i] = Color.getHSBColor(c, (float)1.0,(float)1.0);
c += .02;
}
int i = 0;
while (true) {
setForeground(colors[i]);
repaint();
i++;
try { Thread.sleep(100); }
catch (InterruptedException e) { }
if (i == colors.length ) i = 0;
}
}
public void paint(Graphics g) {
g.setFont(f);
g.drawString(mesag,Xposition,100);
Xposition--;
if (Xposition < -290) {
Xposition = 600;
}
}
}
Thank you!
Suggestions:
Never draw directly within a JApplet or other top-level window.
Instead draw in the paintComponent of a JPanel that is displayed within the applet. The Swing tutorials will show you how.
Be sure to call the super.paintComponent(g) method within your override, and again read the Swing tutorials to see why. For more tutorials see: Swing Info
This is Swing -- use a Swing Timer to drive your animation, not threads.
If you ever do use Threads, never call Thread#stop() or use any other deprecated methods. Please read Why is Thread.stop deprecated?.
Please look at this answer for an example of Swing animation using a Swing Timer.
Unless this is for a class assignment, don't create JApplets as this is a dead technology, something even Oracle will tell you.
To display text in the background use the java.awt.Graphics method for writing text: drawString(...). Either that or place a JLabel over your background image.
Hi I have googled and can't figured out why my paintComp method isnt being called
I have the following code
package com.vf.zepto.view;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import com.vf.zepto.view.interfaces.ProcessorPanel;
public class CountryDetailsPanel extends JPanel implements ProcessorPanel, Runnable {
private GridBagConstraints c = new GridBagConstraints();
private String countryName;
private Properties prop = new Properties();
private BufferedImage image;
public CountryDetailsPanel() {
try {
prop.load(new FileInputStream("country.props"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//this.setLayout(new GridBagLayout());
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(5, 5, 5, 5);
this.setPreferredSize(new Dimension(200, 200));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
try {
if(countryName != null) {
String asset = prop.getProperty(countryName+".flag");
if(!asset.equals(null)) {
image = ImageIO.read(new File(asset));
g.drawImage(image, 0, 0, null);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void updateDetails(Object o) {
countryName = (String)o;
SwingUtilities.invokeLater(this);
}
#Override
public void run() {
this.repaint();
}
}
and when calling this.repaint() expect the paintComponent method to be called but for love nor money it isnt.
Have tried to force it to use the EDT incase that was the issue but its not.
any ideas?
You should never call paintComponent() the repaint() method consolidates all requests to change the component (there may be several repaint requests between screen refreshes). It adds an update request to the GUI event queue so that the update will be properly coordinated with other GUI actions (Swing and AWT are not thread-safe). This update request, when processed, calls update(), which calls paint(), which calls your paintComponent()
Why have this:
#Override
public void run() {
this.repaint();
}
It does not seem of any use (creating a new thread to repaint once? Not to mention the thread is not on EDT rather call repaint() on the JPanel instance (if modified externally other than that you shouldnt even worry). However to start a thread which modifeis UI componets use s SwingTimer/SwingWorker or SwingUtilities#invokeXXX()
This might not be related but in your code I see this:
if(!asset.equals(null)) {
image = ImageIO.read(new File(asset));
g.drawImage(image, 0, 0, null);
}
dont use equals() to compare to a null value as this might throw a NullPointerException because you are attempting to
deference a null pointer, for example this code throws a NPE:
String s=null;
if(!s.equals(null)) {//throws NPE
System.out.println("Here");//is never printed
}
Also as mKorbel has said (+1 to him) dont do long running tasks in paintComponent() declare your Image globally, assign it in the constructor and then use it in paintComponent(). F.i
rather do:
public class TestPanel extends JPanel {
private Image image;
public TestPanel() {
image = ImageIO.read(new File(asset));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(asset!=null) {
g.drawImage(image, 0, 0, null);
}
}
}
Do not load image or another hard or long running code in paintComponent
Load this Object as a local variable and only one time
paintComponent() is called
implicitly, when JComponent requires repaint, or
explicitly, for example on every of mouse event if one were to invoke paintComponent() from a MouseMotionListener.
If there is animation, then to use Swing Timer and call repaint().
Below is a simple applet im writing to display a single picture. The code compiles fine, and the applet loads but the image file is never drawn to the applet. Im thinking that it cant find the image using the this.getImage(appletBaseURL, filename); I have the image file stored in all the folders associated with this package but its still not drawing it.
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
public class imageTest extends Applet {
private Image spaceShip;
private final String filename = "spaceshipcropped.jpg";
public void init() {
java.net.URL appletBaseURL = getCodeBase();
File file = new File("spaceshipcropped.jpg");
try {
spaceShip = ImageIO.read(file);
} catch (IOException ex) {
Logger.getLogger(imageTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(spaceShip, 0,0, null);
}
public void update(Graphics g) {
repaint();
}
}
After i made theses changes it worked. thank you all very much for your help!
Don't call setSize() in an applet. The size is set by HTML.
Don't code in AWT in this millennium.
The object passed to a Swing component should be a Graphics2D object, but I've never heard the same said of an Applet. Are you checking the Java Console?
That code has some redundant imports.
In the paint method, check to see if the image is null.
The JavaDocs for the getImage(URL,String) method state "This method always returns immediately, whether or not the image exists." Either add a MediaTracker or join us in the 3rd millennium and use ImageIO.read(URL) - which blocks until the image is loaded.
I expect that fixing point 6 will solve the problem, but attend to the other 5 points as well.
add this
public void update(Graphcs g) {
repaint();
}
my final goal for this application is to animate several items in the same JPanel at a different speed using a thread for each item.the first part is done however the items move at the same speed and i have no idea on how to fix this problem.
package javagamestutos;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class Board extends JPanel implements Runnable {
private Star star;
private Thread animator;
ArrayList<Star> items=new ArrayList<Star>();
public Board() {
setBackground(Color.BLACK);
setDoubleBuffered(true);
star=new Star(25,0,0);
Star star2=new Star(50,20,25);
items.add(star2);
items.add(star);
}
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
for (Star s : this.items) {
g2d.drawImage(s.starImage, s.x, s.y, this);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void run() {
while(true){
try {
for (Star s : this.items) {
s.move();
}
repaint();
Thread.sleep(star.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Board.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
here is the star class wich is the moving item.
package javagamestutos;
import java.awt.Image;
import javax.swing.ImageIcon;
/**
*
* #author fenec
*/
public class Star {
Image starImage;
int x,y;
int destinationX=200,destinationY=226;
boolean lockY=true;
int delay;
public Star(int delay,int initialX,int initialY){
ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png"));
starImage = ii.getImage();
x=initialX;
y=initialY;
this.delay=delay;
}
void moveToX(int destX){
this.x += 1;
}
boolean validDestinatonX(){
if(this.x==this.destinationX){
this.lockY=false;
return true;
}
else
return false;
}
void moveToY(int destY){
this.y += 1;
}
boolean validDestinatonY(){
if(this.y==this.destinationY)
return true;
else
return false;
}
void move(){
if(!this.validDestinatonX() )
x+=1;
if(!this.validDestinatonY() && !this.lockY)
y+=1;
/*if(!this.validDestinatonY())
y+=1;
*/
}
}
and here is the skeleton of the animation that extends a JFrame :
package javagamestutos;
import javax.swing.JFrame;
public class Skeleton extends JFrame {
public Skeleton() {
add(new Board());
setTitle("Stars");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 280);
setLocationRelativeTo(null);
setVisible(true);
setResizable(false);
}
public static void main(String[] args) {
new Skeleton();
}
}
do you have any idea how to achieve my goals?am i using threads proprely?
thank you in advance.
That's because you're invoking the "move" method at a fixed rate specified by the delay of the first "start"
Thread.sleep(star.delay);
So if you move them a little every "n" milliseconds, they will seems to move at the same peace.
If you want them to move at different speed, you have to move them in different thread ( you are using only one now ) Bear in mind the comment by omry,
EDIT
I did something similar just recently
I have two different things so animate so I have two timers ( timers use threads underneath, but they can repeat the execution code every fixed rate ).
The first apply the text to a JLabel every second ( 1000 ms )
final Timer timer = new Timer();
timer.scheduleAtFixedRate( new TimerTask() {
public void run(){
setText();
}
}, 0, 1000 );
And other change the displaying image every 10 seconds ( 10,000 ms )
final Timer imageTimer = new Timer();
imageTimer.scheduleAtFixedRate( new TimerTask() {
public void run() {
setImage();
}
}, 0, 10000 );
I have a video of the result here:
For more advanced ( and nice ) time management you MUST take a look at the "Timing Framework" project which adds additional capabilities to timers.
You should be painting in the AWTDispatchThread. To do that you will want to use something like SwingUtilities.invokeLater(Runnable); This applies not only to your animation, but to the creation and setting visible of your JFrame as well. Failing to do this could result in deadlocks with the painting thread. Also, when moving your painting operations into the SwingUtilites methods, you will not want to include any while(true) loops, as that will commandeer your painting thread.
Generally Swing components should be used from the AWT Event Dispatch Thread (EDT). repaint is one of the methods that is supposedly okay to use off EDT. However, your Star is not and should not be thread-safe.
The easiest approach is to go for EDT-only (at least to start with). Instead of using Thread use javax.swing.Timer which fires on the EDT.
Misc comments: There should be no need for your paint method to dispose of the graphics object sent to it, or for it to sync using Toolkit. The component need not be set to double-buffered, but should be set opaque (JPanel is not guaranteed to be opaque). You should just extend JComponent instead of JPanel, as this is not a panel. It's generally not a great idea for outer classes to implement Runnable. Prefer private variables.
I would suggest you take a look at the open source library trident which does just that, its author, Kirill Grouchnikov is well-known in the Swing world (he is the author of the famous Substance look & feel).
Trident should help you solve the problem of having different objects move at different speeds, without having to create one thread per object (which is a problem in the end).
if you are sure you want to paint in the threads, you can use :
update(getGraphics());
instead of repaint.
this is generally considered bad practice, as you normally paint stuff in the AWT thread.
I'm learning Java by making a small game in a JApplet.
I got a little problem with my sprite's animation.
Here is the code :
this.sprite.setBounds(0,0,20,17);
this.sprite.setIcon(this.rangerDown);
for(int i = 0; i< 16;i++)
{
this.sprite.setBounds(this.sprite.getX(), this.sprite.getY()+1, 20, 17);
this.sprite.update(this.sprite.getGraphics());
try{
Thread.currentThread().sleep(100);
}catch(InterruptedException e){
}
}
It left some flicker during the animation. Once the animation end, the flicker disappears, but it's kind of ugly... I guess there is some step I missed.
I use this method because it gives the better result for now, but I would like to stay without AWT if possible, using Swing instead.
Any ideas how to get rid of the flicker?
Thanks for reading.
Screenshoot (Can't post images, sorry).
This is not a shadow. Its the border of your sprite. It just happens to be black and appears as a shadow. If you change the amount you shift your sprite (lets say by 50 pixels, not just 1) you will see what i mean.
To fix it what you need to do is to draw the background as well each time you update the location of your sprite. Although this will probably produce flickering.
The correct way to do it is to change the way you draw your objects. You need to override the paintComponent method of your panel and then simply call repaint each time you have updated the locations of your sprites.
EDIT:
See this code sample for basic usage. NOTE: This is NOT how you should write animation using Threads. I wrote that to show you what goes in the paintComponent method and wrote the animation Thread to show you that the "shadow" you mentioned is gone. NEVER have a non ending run loop in a thread :)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
MyPanel c = new MyPanel();
f.getContentPane().add(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(350, 100);
f.setVisible(true);
}
}
class MyPanel extends JPanel {
int x = 0;
boolean toTheRight = true;
public MyPanel() {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
x = (toTheRight)?x+5:x-5;
if (x>300)
toTheRight = false;
if (x<0)
toTheRight = true;
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g.create();
g2.setPaint(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setPaint(Color.red);
g2.fillOval(x-2, 50, 4, 4);
}
}
The problem is double buffering.
In Applets:
Double buffering is done almost automatically. Call repaint() instead of paint in your method.
In Swing, there are many ways to do it. I usually go for the BufferStrategy route. When you're initializing your frame, do this:
JFrame frame;
... code to init frame here
frame.createBufferStrategy(2);
Then in your draw methods:
Graphics g = getBufferStrategy().getDrawGraphics();
..code to do drawing here...
g.dispose();
getBufferStrategy().show();