I would like to develop a simple Live Wallpaper with a sequence of images. The only animation required is for each image to fade in and fade out.
All the tutorials I have found online for LWP demonstrate how to use the Draw canvas for fancy animations and drawing. This is not necessary for my app, I only want to iterate through a collection of images.
Being a novice programmer, I need some help learning how to loop through an array of images and how to display them as a wallpaper.
Can anyone share some code or point me towards a good tutorial for this?
UPDATE
The LWP loads on my device but the wallpaper does not change. It is stuck on image3, ironman
Here is the code I have so far. I assume I am doing something wrong in draw()
public class Wallpaper extends WallpaperService {
public void onCreate() {
super.onCreate();
}
public void onDestroy() {
super.onDestroy();
}
public Engine onCreateEngine() {
return new CercleEngine();
}
class CercleEngine extends Engine {
private final Handler handler = new Handler();
private final Runnable drawRunner = new Runnable() {
#Override
public void run() {
draw();
}
};
private boolean visible = true;
public Bitmap image1, image2, image3;
CercleEngine() {
image1 = BitmapFactory.decodeResource(getResources(),
R.drawable.batman);
image2 = BitmapFactory.decodeResource(getResources(),
R.drawable.captainamerica);
image3 = BitmapFactory.decodeResource(getResources(),
R.drawable.ironman);
}
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawRunner);
} else {
handler.removeCallbacks(drawRunner);
}
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(drawRunner);
}
public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
float yStep, int xPixels, int yPixels) {
draw();
}
void draw() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
c.drawBitmap(image1, 0, 0, null);
c.drawBitmap(image2, 0, 0, null);
c.drawBitmap(image3, 0, 0, null);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
handler.removeCallbacks(drawRunner);
if (visible)
{
handler.postDelayed(drawRunner, 1000); // delay 1 sec
}
}
}
There is no easy way around the looping through an array of images. It would have to be done manually.
One approach that you could adopt is to keep your images in the /res/drawable
and then use an int[] array to store the resid's of the images and then loop through it.
A well explained tutorial on Live Wallpapers may be found here:
http://www.vogella.com/articles/AndroidLiveWallpaper/article.html
Good Luck
Related
I'm making a simple 2D game in LibGDX. This is my first project with it.
On my phone everything works fine, nothing bad (6.0.1), but on other phones (no matter what version) it doesn't work. It's clear that the render method doesn't work, but I don't know why. I've tried to add super.render(); but it made it red, so it's not working or I've made some mistake.
This is a menu screen, what comes first. It works fine, than I play and die, than I call it again and it crashes.
public class MenuScreen implements Screen {
private DualisAutosapp game;
private OrthographicCamera camera;
...
public MenuScreen(DualisAutosapp game)
{
super();
Gdx.app.log("Menu","Started");
this.game = game;
camera = new OrthographicCamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.app.log("Menu","Camera");
logoTexture = new Texture(Gdx.files.internal("background.png"));
logoTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
logo = new Sprite(logoTexture);
Gdx.app.log("Menu","Background");
startTexture = new Texture(Gdx.files.internal("start.png"));
startTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
start = new Sprite(startTexture);
Gdx.app.log("Menu","Start");
carChooseTexture = new Texture(Gdx.files.internal(prefs.getString("playercar", "player/mazda2.png")));
carChooseTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
carChoose = new Sprite(carChooseTexture);
Gdx.app.log("Menu","Car");
font = new BitmapFont(Gdx.files.internal("font/joystix_monospace.fnt"), Gdx.files.internal("font/joystix_monospace.png"), false);
batch = new SpriteBatch();
Gdx.app.log("Menu","batch");
calculateSpriteLocation(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
Gdx.app.log("Menu","calculate");
}
#Override
public void show() {
}
#Override
public void resize(int width, int height) {
camera = new OrthographicCamera();
camera.setToOrtho(false,Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
calculateSpriteLocation(width,height);
}
#Override
public void render (float delta) {
Gdx.app.log("Menu","render");
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
...
}
#Override
public void resume() {
Gdx.app.log("Menu","Rizjúm");
carChooseTexture = new Texture(Gdx.files.internal(prefs.getString("playercar", "player/mazda2.png")));
carChooseTexture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
carChoose = new Sprite(carChooseTexture);
}
#Override
public void hide() {
}
private void calculateSpriteLocation(int width, int height)
{
logo.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
logo.setPosition(width/2-logo.getWidth()/2, height/2-logo.getHeight()/2);
start.setSize(Gdx.graphics.getWidth()/3.6f, Gdx.graphics.getHeight()/6.4f);
start.setPosition(width/2-start.getWidth()/2, height/2-start.getHeight()/2);
carChoose.setSize(Gdx.graphics.getWidth()/3.6f, Gdx.graphics.getHeight()/6.4f);
carChoose.setPosition(width/2-carChoose.getWidth()/2, height/10-carChoose.getHeight()/2);
}
}
Everything is ok until the log of the render where it crashes.
EDIT
Game Class
public class DualisAutosapp extends Game {
#Override
public void create () {
showMenuScreen();
}
public void switchScreen(Screen newScreen){
Screen previousScreen = getScreen();
setScreen( newScreen );
if (previousScreen != null)
{
previousScreen.dispose();
}
}
public void showMenuScreen()
{
switchScreen(new MenuScreen(this));
}
public void showGameScreen()
{
switchScreen(new GameScreen(this));
}
public void showCarChangeScreen() {
switchScreen( new CarChange(this));
}
}
First of all I'll recommend you to follow Life cycle method of Game/Screen of Libgdx API. If you not override lifecycle method of Game then it call Screen corresponding method.
You're initialize your objects whenever you want but it's not a good way. It's better to initialize all your screen object inside show method of Screen Interface.
Create object's of all screens inside Game class at once and only use that object in setScreen(screen) method.
#Override
public void show() {
..//create objects
}
Use dispose() method and destroy objects that you created in show().
Method resize() can be used to update viewport of camera with device size.
Use pause() and resume() according to your requirement like music pause but not for object initialization.
Well,i think you should not pass Game class to screen constructors every time, and you could try something like this for screen management.
import com.badlogic.gdx.Screen;
public enum MyScreens {
GAME_SCREEN {
public Screen getScreenInstance() {
return new GameScreen();
}
},
MAIN_MENU {
public Screen getScreenInstance() {
return new MainMenu();
}
},
SPLASH_SCREEN {
public Screen getScreenInstance() {
return new SplashScreen();
}
};
public abstract Screen getScreenInstance();
}
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.utils.IntMap;
public class ScreenManager {
private static ScreenManager instance;
private Game game;
private IntMap<Screen> screens;
private ScreenManager() {
screens = new IntMap<Screen>();
}
public static ScreenManager getInstance() {
if (instance == null) {
instance = new ScreenManager();
}
return instance;
}
public void initialize(Game game) {
this.game = game;
}
public void show(MyScreens screen) {
if (game == null) {
return;
}
if (!screens.containsKey(screen.ordinal())) {
screens.put(screen.ordinal(), screen.getScreenInstance());
}
game.setScreen(screens.get(screen.ordinal()));
}
public void dispose(MyScreens screen) {
if (!screens.containsKey(screen.ordinal())) {
return;
}
screens.remove(screen.ordinal()).dispose();
}
public void dispose() {
for (Screen screen : screens.values()) {
screen.dispose();
}
screens.clear();
instance = null;
}
}
Can anyone shed some light on this behaivor of the viewport or ortographic camera? I expect a rectangular viewport to be drawn but what I get instead is everything squeezed up in a square frame. I uploaded a picture to show you what's going on. The center of the green circle is being drawn on to the point(100,100) so clearly , it looks too disproportioned. The size of the red square is supposed to be 400x400 but judging by the way it looks, it isn't . The aspect ratio is completely off. I don't know what's causing this issue. Ignore the little mario in the center. that's being rendered by another camera soewhere else.
Does any one know what could be causing it?
/**
* Created by Omer on 11/24/2015.
*/
public class Play extends GameStateBase {
public static final int CAMERA_TYPE_SCREEN = 0;
public static final int CAMERA_TYPE_MAP = 1;
private Viewport SCREEN_VIEW;
private Viewport MAP_VIEW;
private OrthographicCamera SCREEN_CAM;
private OrthographicCamera MAP_CAM;
AssaultTrooper assaultTrooper;
GamePad GAME_PAD;
public Play(GameStateManager _gameStateManager) {
super(_gameStateManager);
SCREEN_CAM = new OrthographicCamera();
SCREEN_VIEW = new FitViewport(GameGraphics.VIRTUAL_WIDTH,GameGraphics.VIRTUAL_HEIGHT,SCREEN_CAM);
SCREEN_VIEW.apply();
SCREEN_CAM.translate(GameGraphics.VIRTUAL_WIDTH/2,GameGraphics.VIRTUAL_HEIGHT/2);
SCREEN_CAM.update();
MAP_CAM = new OrthographicCamera();
MAP_VIEW = new FitViewport(GameGraphics.VIRTUAL_HEIGHT,GameGraphics.VIRTUAL_HEIGHT,MAP_CAM);
MAP_VIEW.apply();
MAP_CAM.translate(GameGraphics.VIRTUAL_HEIGHT/2,GameGraphics.VIRTUAL_HEIGHT/2);
MAP_CAM.update();
RENDER_STATE.addCamera(CAMERA_TYPE_SCREEN,SCREEN_CAM);
RENDER_STATE.addCamera(CAMERA_TYPE_MAP,MAP_CAM);
GAME_PAD = new GamePad();
RenderStateManager.changeGameRenderState(RENDER_STATE);
GAME_PAD.setCameraType(CAMERA_TYPE_SCREEN);
GAME_PAD.addToRenderState();
assaultTrooper = new AssaultTrooper(GameSettings.TEAM_BLUE,new Location(200,200));
assaultTrooper.setCameraType(CAMERA_TYPE_MAP);
assaultTrooper.addToRenderState();
}
#Override
public void render(SpriteBatch _spriteBatch) {
try {
RenderStateManager.RENDERING_STATE.render(_spriteBatch);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void handleInput() {
}
#Override
public void update(float deltaTime) {
}
#Override
public void dispose() {
}
#Override
public void reSize(int _width, int _height) {
SCREEN_VIEW.update(_width,_height);
MAP_VIEW.update(_width,_height);
}
}
private ConcurrentHashMap<Long,Location> locations;
private ConcurrentHashMap<Long,Sprite> sprites;
private ConcurrentHashMap<Integer,OrthographicCamera> cameras;
private ConcurrentHashMap<Integer,List<Long>> cameraMapping;
public void render(SpriteBatch _spriteBatch) throws InterruptedException {
synchronized (this) {
for (int key : cameras.keySet()) {
_spriteBatch.setProjectionMatrix(cameras.get(key).combined);
_spriteBatch.begin();
if (cameraMapping.get(key) == null) {
_spriteBatch.end();
continue;
}
for (long MAPPER : cameraMapping.get(key)) {
sprites.get(MAPPER).draw(_spriteBatch);
}
_spriteBatch.end();
}
}
}
I have this code where there is a Backgroung under an Hand, both a Bitmap images. Now, i have to control with an onClick method if the click touches the hand or not. For this, i wrote here and some guys tell me to use a method called "bitmap.getPixel(int x, int y)" that gives the color of pixel touched. For control if the click doesn't touch the hand, i write "bitmap.getPixel(x, y) != Color.TRASPARENT", but doesn't work perfectly. Under this i write the code of SurfaceView.
P.S. The bitmap "Hand" is a picture of a central Hand with parts trasparent around.
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback{
private long missileStartTime;
private ThreadLv1 thread;
private OggettiLv1 hand;
private int conto=0;
private Sfondo sfondo;
MediaPlayer ouch;
public GamePanel(Context context){
super(context);
getHolder().addCallback(this);
thread = new ThreadLv1(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){}
#Override
public void surfaceDestroyed(SurfaceHolder holder){
boolean retry = true;
while(retry)
{
try{thread.setRunning(false);
thread.join();
}catch(InterruptedException e){e.printStackTrace();}
retry = false;
}
}
#Override
public void surfaceCreated(SurfaceHolder holder){
hand = new OggettiLv1(BitmapFactory.decodeResource(getResources(), R.drawable.mano5));
sfondo = new Sfondo(BitmapFactory.decodeResource(getResources(), R.drawable.sfondo));
ouch= MediaPlayer.create(getContext(), R.raw.ouch);
knifesong.start();
thread.setRunning(true);
thread.start();
}
public void update() {
}
#Override
public void draw(Canvas canvas)
{
if(canvas!=null) {
final int savedState = canvas.save();
background.draw(canvas);
hand.draw(canvas);
drawText(canvas);
canvas.restoreToCount(savedState);
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
int c = hand.sprite.getPixel((int) event.getX(), (int) event.getY());
if(c!=Color.TRANSPARENT){
conto++;
}
return true;
}
return false;
}
}
Someone know the problem and the solution in this code? Thanks in advance.
I think this might be the problem:
int c = hand.sprite.getPixel((int) event.getX(), (int) event.getY());
I think you are getting the value of a pixel at the x,y location on the sprite, you should get the value of the pixel on canvas.
EDIT:
Since your canvas is static, I suggest doing the following:
add a Bitmap to your class
private Bitmap canvasState;
In the draw function, initialize canvasState before anything else
canvasState = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.RGB_565));
canvas.setBitmap(canvasState);
after that, every change that you make to the canvas will be made to the canvasState, and you can use that in your event handler like so:
int c = canvasState.getPixel((int) event.getX(), (int) event.getY());
I'm not sure if the code I wrote will compile (I wrote them from the top of my head) but I think you can work out compile errors yourself.
extremely slow frame rates when using the openCV Java method in android for detecting circular shaped objects in images
Imgproc.HoughCircles(mGray, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 50);
when i remove this method it runs fast, but after adding this method inside of this callback
public Mat onCameraFrame(final CvCameraViewFrame inputFrame) {
the frame rate slows to 1 to 2 frames per second, I don't understand why it gets so slow, i tried putting this method in a separate thread and it would not help, the only thing that worked is to use a counter and and an if statement to run the method every 10 frames.
in the OpenCV examples there is a sample project called face detection and it has both a native C++ and Java camera versions and they both are vary fast, how is it possible that when I use similar code I get this slow constipated action from OpenCV?
is there something i am doing wrong here? In the face detection project from openCV examples they take every frame and they don't launch a separate thread. how do I fix this problem and make my code run fast like the sample projects in OpenCV?
in a different project I am also having the same problem of slow frame rate, in this practice project where I am not using openCV, it is just the android Camera class only, in that I am taking the image from the onPreviewFrame(byte[] data, Camera camera) method and doing some light processing like converting the YUV format from the byte array into a bitmap and putting that into another view on the same screen as the camera view, and the result is vary slow frame rate.
EDIT: In some additional experimentation I added the Imgproc.HoughCircles() method to the OpenCV face Detection sample project. putting this method inside the onCameraFrame method of the java detector.
the result is the same as in my project, it became vary slow. so the HoughCircles method probably takes more processing power than the face detection method CascadeClassifier.detectMultiScale(), however that does not explain the fact I watched other circle detection projects on youTube and in their videos the frame rate is not slowed down. that is why I think there is something wrong with what I am doing.
here is a sample of the code I am using
public class CircleActivity extends Activity implements CvCameraViewListener2 {
Mat mRgba;
Mat mGray;
File mCascadeFile;
CascadeClassifier mJavaDetector;
CameraBridgeViewBase mOpenCvCameraView;
LinearLayout linearLayoutOne;
ImageView imageViewOne;
int counter = 0;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
#Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i("OPENCV", "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
if (!OpenCVLoader.initDebug()) {
// Handle initialization error
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coffee);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.fd_activity_surface_view);
mOpenCvCameraView.setCvCameraViewListener(this);
}
#Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
#Override
public void onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
}
public void onDestroy() {
super.onDestroy();
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
mGray = new Mat();
mRgba = new Mat();
}
public void onCameraViewStopped() {
mGray.release();
mRgba.release();
}
public Mat onCameraFrame(final CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
if(counter == 9) {
MatOfRect circles = new MatOfRect();
Imgproc.HoughCircles(mGray, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 50);
// returns number of circular objects found
Log.e("circle check", "circles.cols() " + circles.cols());
}
counterAdder();
return mRgba;
} // end oncamera frame
public void counterAdder() {
if (counter > 10) {
counter = 0;
}
counter++;
}
}
Reducing resolution of camera frames might help
mOpenCvCameraView.setMaxFrameSize(640, 480);
From my brief experience, the running time for HoughCircles greatly depends on the image. A textured image with a lot of potential circles takes much longer than an image with a uniform background. Hope this helps.
I' ve faced this problem either.
I' ve tried to decrease the camera resolution with mOpenCvCameraView.setMaxFrameSize(1280, 720);
However it is still slow. I' ve been trying to work parallel with Threads, but it is still 3.5FPS.
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
//System.gc();
carrierMat = inputFrame.gray();
Thread thread = new Thread(new MultThread(carrierMat, this));
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return carrierMat;
}
My MultThread class is just like this
public class MultThread implements Runnable {
private Mat source;
private Context context;
public MultThread(Mat source, Context context) {
this.source = source;
this.context = context;
}
#Override
public void run() {
//output = General.Threshold(source);
int x = General.MSERP(source);
Log.i("MtMTxtDtc:Main","x: " + x);
if (x > 10){
((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(500);
}
}
}
You have to perform the Hough Circle transform in the background not in the main activity!
Otherwise your app response will be too slow and it may be killed by the operating system due to Application Not Responding (ANR) error.
You need to add this class to your main activity and you are good to go.
private class HoughCircleTransformTask
extends AsyncTask<Mat, Void, Integer> {
#Override
protected Boolean doInBackground(Mat mGray) {
MatOfRect circles = new MatOfRect();
Imgproc.HoughCircles(mGray, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 50);
// returns number of circular objects found
// then display it from onPostExecute()
return circles.cols();
}
#Override
protected void onPostExecute(Integer circlesCols){
// This is only logging
// You can display it in a TextView as well in the main activity
Log.e("circle check", "circles.cols() " + circles.cols());
}
}
And just call it from onCameraFrame with one line of code only
public Mat onCameraFrame(final CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
if(counter == 9) {
// call AsyncTask
new HoughCircleTransformTask().execute(mGray);
}
counterAdder();
return mRgba;
} // end oncamera frame
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
);
}