Java Lang nullpointer exception in animation example? - java

import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class Images extends JFrame {
public static void main(String[] args) {
DisplayMode dm = new DisplayMode(800,600,32, DisplayMode.REFRESH_RATE_UNKNOWN);
Images I = new Images();
I.run(dm);
}
private Screen s;
private Image bg;
private Image pic;
private boolean nLoaded;
Animation a;
public void run(DisplayMode dm)
{
nLoaded = false;
s = new Screen();
try{
s.Setfullscreen(dm, this);
LoadPics();
MovieLoop();
try{
Thread.sleep(50000);
}catch(Exception ex){}
}finally{
s.restoreScreen();
}
}
public void MovieLoop(){
long startingtime = System.currentTimeMillis();
long cumTime = startingtime;
while(cumTime-startingtime < 5000)
{
long timepassed = System.currentTimeMillis() - cumTime;
cumTime += timepassed;
a.update(timepassed);
Graphics g = s.getFullScreenWindow().getGraphics();
draw(g);
g.dispose();
try{
Thread.sleep(20);
}catch(Exception ex){}
}
}
public void draw(Graphics g)
{
g.drawImage(bg, 0,0, null);
g.drawImage(a.getImage(),0,0,null); }
//Create Load Pictures
public void LoadPics()
{
bg = new ImageIcon("C:\\Gamepics\\BackgroundImage.jpg").getImage();
pic = new ImageIcon("C:\\Gamepics\\SmileyIcon3.png").getImage();
nLoaded = true;
repaint();
}
public void paint(Graphics g){
if(g instanceof Graphics2D){
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
if(nLoaded)
{
g.drawImage(bg, 0, 0, null);
g.drawImage(pic, 300,300, null);
}
}
}
Im not understanding what I did wrong ive overlooked everything the best I can. Im just practicing a simple animation and I keep getting 3 null pointer exceptions.
Ive researched the best I can and apparently NullPointerExceptions in java have to do with trying to get the size of null arrays? The compiler hasn't marked any of my lists as problems so im a little confused. Any help would be greatly appreciated. All of the errors are commented out. There are three of them and they are in the Images class
Errors:
Exception in thread "main" java.lang.NullPointerException
at Images.MovieLoop(Images.java:45)
at Images.run(Images.java:26)
at Images.main(Images.java:8)
Animation Class
import java.awt.Image;
import java.util.*;
public class Animation {
private ArrayList scenes;
private int sceneindex;
private long movietime;
private long totaltime;
//CONSTRUCTOR
public Animation(){
scenes = new ArrayList();
totaltime =0;
start();
}
//Add scene to array list and sets time for each scene
//For example. If you have 3 scenes you would add t to total time three times. So if you had
//3 Scenes, one for 1s, one for 2s, one for 3s. Total time would be 6s. and you would call addscene 3 times
public synchronized void addScene(Image i, long t)
{
totaltime+=t;
scenes.add(new OneScene(i, totaltime));
}
//start animation from beggininign inignngingingnig
public synchronized void start(){
movietime = 0;
sceneindex = 0;
}
//change scenes
//movie time is the sum of all the time passed from last update
//If you have more than one scene. timepassed gets added to movietime.
//if movietime is greater than or equal to total time(ie. animation is complete) restart the animation
public synchronized void update(long timepassed)
{
if(scenes.size() > 1){
movietime += timepassed;
if(movietime >= totaltime)
{
movietime = 0;
sceneindex = 0;
}
while(movietime > getScene(sceneindex).endTime)
{
sceneindex++;
}
}
}
public synchronized Image getImage(){
if(scenes.size() == 0){
return null;}
else{
return getScene(sceneindex).pic;
}
}
//Getscene
private OneScene getScene(int x){
return (OneScene)scenes.get(x);
}
//Scenes are gonna be
private class OneScene{
Image pic;
long endTime;
public OneScene(Image pic, long endTime)
{
this.pic = pic;
this.endTime = endTime;
}
}
}
I included the animation class because the compiler is highlighting these three method calls as the source of the problem
a.update(timepassed);
MovieLoop();
I.run(dm);

Please Note: This is a really long comment
Let's start with Graphics g = s.getFullScreenWindow().getGraphics(); - getGraphics is NEVER a good idea, this can return null.
You should NEVER try and update any UI component from any thread other the EDT and you should NEVER draw directly to it in this manner, instead, you should be using paintComponent.
You should NEVER dispose of any Graphics context that you did not create yourself, this will prevent other components from been painted.
You should avoid overriding paint, especially of a top level container, if for no other reason, it's not double buffered (the top level container), and you will also be painting over any other child components.
Check out Performing Custom Painting for more details.
You should try using ImageIO instead of ImageIcon. ImageIO will throw exceptions if it can't read the file, where as ImageIcon simply fails silently, no very helpful.

Related

Problems with Graphics in MVC structure

I testing to implement graphics into MVC structure but Im a bit stuck. Here is what I got so far. For now I just want to get the red ball to bounce back and forth. And use the button start to start the thread and button stop to stop the thread that runs the GameLoop in the controller.
But I think Im mixing this up a bit. Would very much appreciate some feedback!
Heres what I got so far:
GameModell
suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
GameView
Here Im putting the labels on a JFrame. I also want to display the buttons start and stop to controll the thread but I guess they are hidden by the JPanel in TheGraphics class
GameController
Starts and stops the thread with ActionListeners. Contains the GameLoop
TheGraphics
Paints the ball and controll the direction
I guess I got a lot of thing that are all wrong but this is the best I can do at the moment. Would very much apreciate some help!
Thanks!
MAIN:
public class MVCgame {
public static void main(String[] args) {
GameModel gm = new GameModel();
GameView gv = new GameView();
GameController gc = new GameController(gm, gv);
}
}
MODEL:
public class GameModel {
private int multi = 1;
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
public int multi() {
return multi;
}
}
VIEW:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameView extends JFrame {
private JPanel jp = new JPanel();
private JButton start = new JButton("Start");
private JButton stop = new JButton("Stop");
TheGraphics gr = new TheGraphics();
public GameView() {
add(jp);
add(gr);
jp.add(start);
jp.add(stop);
setSize(250, 250);
setVisible(true);
}
public void addListener(ActionListener theListener) {
start.addActionListener(theListener);
stop.addActionListener(theListener);
}
public JButton getStart() {
return start;
}
public JButton getStop() {
return stop;
}
// GUESS I SHOULD PUT THIS IN THE VIEW???
public void paintEllipse(Graphics theG) {
Graphics2D g2d = (Graphics2D) theG;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(0, 0, 10, 10);
}
}
CONTROLLER:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GameController implements Runnable {
GameView gv;
GameModel gm;
private Thread thread;
private boolean running = false;
public GameController(GameModel gm, GameView gv) {
this.gv = gv;
this.gm = gm;
gv.addListener(theListener);
start();
}
ActionListener theListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == gv.getStart()) {
start();
System.out.println("PLAY = ");
} else if (e.getSource() == gv.getStop()) {
stop();
System.out.println("STOP = ");
}
}
};
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
thread.interrupt();
running = false;
}
// GameLoop
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 10) {
// tick();
delta--;
// repainting the graphics
gv.gr.drawer();
gm.setMulti(gv.gr.drawer());
System.out.println("gv.gr.drawer() = " + gv.gr.drawer() + " gm.multi() " + gm.multi());
// I want to use this value in the model to change the direction
}
if (running) {
}
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
}
}
THE GRAPHICS:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class TheGraphics extends JPanel {
private int locX = 40;
public TheGraphics() {
}
public int drawer() {
locX++;
repaint();
return locX;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(locX, 30, 10, 10);
}
}
GameModell suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
Really bad idea. You should always check position and direction (sign(speed)). Otherwise, your object might get stuck out of bounds always changing direction without moving from place forever.
Apart from this, using the MVC concept is overkill in my eyes and shouldn't be used in such a small project nor in a game. In a game, you should more or less put all three together. Of course you can, but the advantages and disadvantages of the MVC concept don't fit the needs of a game in many ways (except for the GUI, perhaps).
Your main loop might look something like this (you kind of did this already, but why is the tick() commented out in your code?):
while (running) {
update(); // Update all game objects
paint(); // Paint them all
}
Each game object will have its own update() and paint() implementation. You absolutely need to separate the logic of update and paint, even if they are in the same class. So this one:
public int drawer() {
locX++;
repaint();
return locX;
}
is an absolute no-go.
Edit: (Referring your update answer)
You are using the method location() for different purposes. According to the Java name convention, you should rename it getLocation() and setLocation() depending on the use to clarify the code.
(Even if this is not really MVC anymore, I'd let GameFrame implement ActionListener instead of specifying it as variable of GameController.)
One thing you should really change is this one:
private int locX = 0;
public void location(int loc) {
this.locX = (int) loc;
}
Basically, you are duplicating the location value every frame and create unused redundant data. Another problem is, that this might work fine for only one variable, but what if you add more than the position to your model later on? Instead TheGraphics has to render on an instance of the data model, not its values. As long you are using one GameModel
private GameModel model; // set value once at initialisation
and rendering its values in paintComponent will work fine, but if you want to add more than one GameModel (handling GameModel more like a GameObjectModel), you will need to pass it as parameter in the paint method.
public void update() {
repaint();
}
Remove it and try getting around without. A method called from one place forwarding to a different method is a bad idea most of the time, especially if it obfuscates the functionality with a different name.
gv.gr.update();
gv.gr.location(gm.location());
You are first repainting your image and then setting the location? Basically, your game runs one frame behind all the time. Swap that order.
gv.gr.location(gm.location());
gv.gr.repaint();
Will be fine (I already said about location()).

Rendering a rectangle with multiple classes

I am making a simple game project and I am having a problem when trying to create a rectangle that moves across the screen.
Here is the main class:
`public class Main extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
private JFrame frame;
boolean running = false;
Graphics g;
static int HEIGHT = 500;
static int WIDTH = HEIGHT * 16 / 9;
SoundHandler sh = new SoundHandler();
//Game state manager
private GameStateManager gsm;
public Main()
{
//window
frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setSize(WIDTH, HEIGHT);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
init();
}
void init()
{
gsm = new GameStateManager();
sh.playMusic("Undertale.wav", 1);
}
public synchronized void start(){
running = true;
new Thread(this).start();
}
public synchronized void stop(){
running = false;
}
//game loop
public void run()
{
//init time loop variables
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
double lastFpsTime = 0;
int fps = 0;
while(running)
{
//work out how long its been since last update
//will be used to calculate how entities should
//move this loop
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
//update frame counter
lastFpsTime += updateLength;
fps++;
//update FPS counter
if(lastFpsTime >= 1000000000)
{
System.out.println("FPS " + fps);
lastFpsTime = 0;
fps = 0;
}
//game updates
update(delta);
//draw
draw(g);
try{
Thread.sleep((lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000 );
}catch(InterruptedException e){
System.out.println("Error in sleep");
}
}
}
private void update(double delta)
{
//updates game state code
gsm.update(delta);
}
public void draw(Graphics g)
{
gsm.draw(g);
}`
here is the class I want to draw the rectangle with
package me.mangodragon.gamestate;
import java.awt.Graphics;
public class MainState extends GameState{
int x;
public MainState(GameStateManager gsm){
this.gsm = gsm;
}
public void init() {
}
public void update(double delta) {
x += 2 * delta;
}
public void draw(Graphics g) {
g.drawRect(x, 0, 50, 50);
g.dispose();
}
public void keyPressed(int k) {
}
public void keyReleased(int k) {
}
}
I keep getting this error:
Exception in thread "Thread-4" java.lang.NullPointerException
at me.mangodragon.gamestate.MainState.draw(MainState.java:22)
at me.mangodragon.gamestate.GameStateManager.draw(GameStateManager.java:37)
at me.mangodragon.main.Main.draw(Main.java:118)
at me.mangodragon.main.Main.run(Main.java:100)
at java.lang.Thread.run(Unknown Source)
I tried to fix it, but I could not locate the problem.
Thanks!
You never assign anything to g (Graphics). Now, before you run off and try and figure out how you might do that, I'd highly, highly recommend you get rid of this variable, it's going to cause you too many issues.
Normally, when the system wants your component to painted, it calls your paint method and passes you the Graphics context which it wants you to paint to. This approach is known as passive painting, as the paint requests come at random times, which isn't really what you want. Another issue is java.awt.Canvas isn't double buffered, which will cause flickering to occur as your component is updated.
You might want to take a look at Painting in AWT and Swing and Performing Custom Painting for more details
You could use a JPanel instead, which is double buffered, but the main reason for using java.awt.Canvas is so you can make use the BufferStrategy API. This not only provides double buffering, but also provides you with a means by which you can take direct control over the painting process (or active painting).
See BufferStrategy and BufferStrategy and BufferCapabilities for more details
You defined g as such:
Graphics g;
But never gave it a value.
This is not how you should be drawing shapes, anyways. Instead, override the paint method (inherited from Canvas) in class Main:
#Override
public void paint(Graphics2D g) {
//Drawing code goes in here. This runs whenever the Canvas is rendered.
}
Then, when you want to update it, such as in your while loop, run
this.repaint(); //note that this doesn't take arguments
If you want to use the draw(Graphics g) method in the other class, call it in paint().
public void paint(Graphics2D g) {
gsm.draw(g);
}
The problem is that you have not defined g, so it is null. And for the most part, you are never supposed to create a new Graphics object, but instead get it from somewhere.
Since you are inheriting a Canvas, this can be very easily done.
First, you should change your draw method to be like this.
private void draw() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
// Draw your game here, using the g declared above
g.dispose();
bs.show();
}
The first few lines create something called a BufferStrategy which you can read more about here but it essentially lets Java render the next couple frames ahead of schedule so that you don't see any flickering.
From the BufferStrategy, you can get the Graphics object to draw on.
And, finally, you have to dispose of the Graphics object, and then show the Buffer so that everything you did shows on the screen.

Java Physics loop (repaint on interval) is choppy unless mouse is moving

I have a simple physics loop that does a calculation for a time interval, waits for the interval to pass, and then renders the results on the screen. It's very simple code (even though the timing is probably wrong, but that's exactly what I'm trying to learn about) and works well when I am moving the mouse around the screen.
package physicssim;
import java.awt.Graphics;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PhysicsSim extends JFrame {
private static class PhysicsObject {
public PhysicsObject(double x, double y, double v_x, double v_y) {
this.x = x;
this.y = y;
this.v_x = v_x;
this.v_y = v_y;
}
public double x;
public double y;
public double v_x;
public double v_y;
}
PhysicsObject particle;
boolean running = true;
DrawPane drawPane;
public PhysicsSim() {
particle = new PhysicsObject(10,10, .1, .2);
drawPane = new DrawPane(particle);
this.setSize(800,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(drawPane);
this.setVisible(true);
}
private static class DrawPane extends JPanel {
PhysicsObject p;
public DrawPane(PhysicsObject p) {
this.p = p;
}
#Override
public void paint(Graphics g) {
super.paint(g); //To change body of generated methods, choose Tools | Templates.
g.fillOval((int)p.x, (int) p.y, 10, 10);
}
}
public void start() {
int FPS = 60;
long TIME_BETWEEN_FRAMES_NS = 1000000000/FPS;
// Initial draw
drawPane.repaint();
long lastDrawTime = System.nanoTime();
while(running) {
// Update physics
particle.x+=particle.v_x*(TIME_BETWEEN_FRAMES_NS*.0000001);
particle.y+=particle.v_y*(TIME_BETWEEN_FRAMES_NS*.0000001);
// While there is time until the next draw wait
while(TIME_BETWEEN_FRAMES_NS > (System.nanoTime()-lastDrawTime)) {
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
Logger.getLogger(PhysicsSim.class.getName()).log(Level.SEVERE, null, ex);
}
}
drawPane.repaint();
long currentTime = System.nanoTime();
System.out.println(currentTime - lastDrawTime);
lastDrawTime = currentTime;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
PhysicsSim sim = new PhysicsSim();
sim.start();
}
}
The last bit about printing the time difference was just a sanity check to make sure that it was in fact calling around the requested interval. The results are fairly consistent so I don't see why there should be any choppiness.
As I mentioned above, this code works great if I a moving the mouse around the screen, everything is smooth.
If I am not moving the mouse it becomes very choppy until I start moving the mouse over the application.
I assume this is something simple, but I hope that you guys can help me. Thank you.
Alright, it looks like my problem was I was drawing directly to g in paint(). After replacing with the following everything worked correctly.
#Override
public void paint(Graphics g) {
BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR);
img.getGraphics().fillOval((int) p.x, (int) p.y, 10, 10);
g.drawImage(img, 0, 0, null);
}
I was considering deleting this code snippet because it's rough and shameful, but maybe it will help someone else. Happy coding.

Main.GamePanel is not abstract and does not override abstract method keyReleased(java.awt.event.KeyEvent) in java.awt.event.KeyListener

I was doing a tutorial online because I wanted to make a 2d side scroller, and I got this exact error. I have googled it but came up with nothing. I tried looking for a typo and it looks clean, its not giving me an error anywere else in the code. I do not know where to start. If you could explaing to me what the error is and how i fix it then that would be amazing.
package Main;
import GameState.GameStateManager;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
public class GamePanel extends JPanel implements Runnable, KeyListener{
public static final int WIDTH = 320;
public static final int HIGHT = 240;
public static final int SCALE = 2;
//game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000/FPS;
//image
private BufferedImage image;
private Graphics2D g;
//game state manager
private GameStateManager gsm;
public GamePanel(){
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify(){
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(WIDTH, HIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
gsm = new GameStateManager();
}
public void run(){
init();
long start, elapsed, wait;
//game loop
while(running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
try
{
Thread.sleep(wait);
}
catch(Exception e)
{
e.printStackTrace();
}//end of try catch
}
}
private void update()
{
gsm.update();
}
private void draw()
{
gsm.draw(g);
}
private void drawToScreen()
{
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void KeyPressed(KeyEvent key)
{
gsm.keyPressed(key.getKeyCode());
}
public void KeyReleased(KeyEvent key)
{
gsm.keyReleased(key.getKeyCode());
}
}
The compiler error message tells you exactly what's wrong: your class implements the KeyListener interface but does not implement all the necessary methods of the interface. Solution: be sure to implement all the necessary methods as per the KeyListener API. Also be sure to use the #Override annotation to make sure that your overrides are correct.
Having said that, I'm going to recommend that you not use KeyListeners for most key board input with Swing applications, that it is a low-level listener and should be avoided in favor of higher level constructs such as key bindings. Also, Swing GUI's should avoid use of update(...) method overrides as that is more of an AWT construct.
Your KeyReleased(KeyEvent key) method must start with small letter 'k' like keyReleased(KeyEvent key). Java is case sensitive.
You may also required to override other methods of KeyListener interface.
Also add #Override annotation (as suggested by #Hovercraft Full Of Eels) to the method when you want to override a super method. That way IDE's will give you hint's while coding.

AnimatedGIFField from blackberry knowledge base, don't know how to manipulate the gif

I am using this code, if anybody is familiar with it, its from the blackberry knowledge base. Anyway, I was wondering how to manipulate GIF's using this class. I can get the gif on the screen, but it keeps repeating and will not disappear. Any help is greatly appreciated!
public class AnimatedGIFField extends BitmapField
{
private GIFEncodedImage _image; //The image to draw.
private int _currentFrame; //The current frame in
the animation sequence.
private int _width; //The width of the image
(background frame).
private int _height; //The height of the image
(background frame).
private AnimatorThread _animatorThread;
public AnimatedGIFField(GIFEncodedImage image)
{
this(image, 0);
}
public AnimatedGIFField(GIFEncodedImage image, long style)
{
//Call super to setup the field with the specified style.
//The image is passed in as well for the field to
//configure its required size.
super(image.getBitmap(), style);
//Store the image and it's dimensions.
_image = image;
_width = image.getWidth();
_height = image.getHeight();
//Start the animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
}
protected void paint(Graphics graphics)
{
//Call super.paint. This will draw the first background
//frame and handle any required focus drawing.
super.paint(graphics);
//Don't redraw the background if this is the first frame.
if (_currentFrame != 0)
{
//Draw the animation frame.
graphics.drawImage(_image.getFrameLeft(_currentFrame), _image.getFrameTop(_currentFrame),
_image.getFrameWidth(_currentFrame), _image.getFrameHeight(_currentFrame), _image, _currentFrame, 0, 0);
}
}
//Stop the animation thread when the screen the field is on is
//popped off of the display stack.
protected void onUndisplay()
{
_animatorThread.stop();
super.onUndisplay();
}
//A thread to handle the animation.
private class AnimatorThread extends Thread
{
private AnimatedGIFField _theField;
private boolean _keepGoing = true;
private int _totalFrames; //The total number of
frames in the image.
private int _loopCount; //The number of times the
animation has looped (completed).
private int _totalLoops; //The number of times the animation should loop (set in the image).
public AnimatorThread(AnimatedGIFField theField)
{
_theField = theField;
_totalFrames = _image.getFrameCount();
_totalLoops = _image.getIterations();
}
public synchronized void stop()
{
_keepGoing = false;
}
public void run()
{
while(_keepGoing)
{
//Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(new Runnable()
{
public void run()
{
_theField.invalidate();
}
});
try
{
//Sleep for the current frame delay before
//the next frame is drawn.
sleep(_image.getFrameDelay(_currentFrame) * 10);
}
catch (InterruptedException iex)
{} //Couldn't sleep.
//Increment the frame.
++_currentFrame;
if (_currentFrame == _totalFrames)
{
//Reset back to frame 0 if we have reached the end.
_currentFrame = 0;
++_loopCount;
//Check if the animation should continue.
if (_loopCount == _totalLoops)
{
_keepGoing = false;
}
}
}
}
}
}
Don't call super.paint(graphics), rather draw everything you need to draw by yourself. re-write your paint(Graphics graphics) method like below:
private boolean isPaint = true;
protected void paint(Graphics graphics) {
if(!isPaint) return;
// super.paint(graphics);
if (_currentFrame == _image.getFrameCount()-1) {
graphics.setGlobalAlpha(0);
isPaint = false;
}
graphics.drawImage(
_image.getFrameLeft(_currentFrame),
_image.getFrameTop(_currentFrame),
_image.getFrameWidth(_currentFrame),
_image.getFrameHeight(_currentFrame), _image,
_currentFrame, 0, 0
);
}

Categories