0

To clarify, I'm interested in doing a simple animation that changes from one bitmap to another after a few seconds, and then repeats. For example, if I drew a frowny face, how could I set it to be removed after a few seconds, and replaced it with a smiley face?

Here's some example code



public class MySurface extends SurfaceView implements Runnable {

SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = true;

Bitmap frowny;
Bitmap smiley;

public MySurface(Context context) {
    super(context);
    init(context);

}

public MySurface(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

public MySurface(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context);
}

private void init(Context context) {
    // do stuff that was in your original constructor...
    ourHolder = getHolder();
    ourThread = new Thread(this);
    ourThread.start();

    frowny = BitmapFactory.decodeResource(getResources(),
            R.drawable.frowny);
    smiley = BitmapFactory
            .decodeResource(getResources(), R.drawable.smiley);


}

public void pause() {
    isRunning = false;
    while (true) {
        try {
            ourThread.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        break;
    }
    ourThread = null;
}

public void resume() {
    isRunning = true;

}

@SuppressLint("WrongCall")
public void run() {
    // TODO Auto-generated method stub
    while (isRunning) {
        if (!ourHolder.getSurface().isValid()) {
            continue;
        }

        Canvas c = ourHolder.lockCanvas();
        onDraw(c);
        ourHolder.unlockCanvasAndPost(c);

    }

}

@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);

    canvas.drawBitmap(frowny, (getWidth/2), (getHeight/2), null);

    //wait a period/remove frowny

    canvas.drawBitmap(smiley, (getWidth/2), (getHeight/2), null);



    postInvalidate();

  }

}
user3720864
  • 5
  • 1
  • 6
  • Using Animator should be easier in this case. You can cross fade both bitmap. And [ViewFlipper](http://developer.android.com/reference/android/widget/ViewFlipper.html) can also works. You can find ViewFlipper example online. – Wenhui Jul 28 '14 at 23:44
  • I see you've asked 8 questions but haven't marked a single one as answered even though 5 have received responses. You're obviously new to SO, so read http://stackoverflow.com/help/whats-reputation. – Mark Cramer Jul 29 '14 at 23:15
  • 1
    @MarkCramer I didn't even realize that, thank you – user3720864 Jul 30 '14 at 00:34
  • @user3720864 No problem. If you ask a question and someone gives the right answer, you need to mark it with the green check mark. – Mark Cramer Jul 30 '14 at 02:25

3 Answers3

0

I would suggest using AsyncTask. In the doInBackground method, set a timer and then in the onPostExecute method, do the animation.

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.System;
import android.view.View;

public class AsyncTaskActivity extends Activity implements OnClickListener {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  myBitmapTask myTask = new myBitmapTask();
  myTask.execute();
}

private class myBitmapTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPreExecute() {}

    @Override
    protected String doInBackground(Void... params) {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void... Params) {
        // do bitmap animation
    }

    @Override
    protected void onProgressUpdate(Void... values) {}
}
}

Note: this was not tested and was typed on my phone. Sorry for any typos.

user1282637
  • 1,827
  • 5
  • 27
  • 56
  • To do so, would the process be: create a new class that extends Asynctasks, leave onPreExecute blank, then in doInBackground I have it wait (would I use wait()?), leave onProgressUpdateBlank, and then in onPostExecute do canvas.drawbitmap? – user3720864 Jul 29 '14 at 00:08
  • This might work, but I don't think it's the best way to go. AsyncTask() is great if you're looking to process something in parallel or in the background, but it's not necessarily designed for for a timer since you can't control when it'll run. Using a 'Handler' enables you to send a message at a pre-determined time interval, which is what you want for this. – Mark Cramer Jul 29 '14 at 00:50
0

I think a Handler is the way to go. Use a subclass to create a RefreshHander like this:

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        updateImage(); // change your frown to a smiley
        view.invalidate(); // trigger onDraw()
    }

    public void sleep() {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), DELAY, 0)); // how long to wait in ms
    }
}

Create a redrawHandler with private RefreshHandler redrawHandler = new RefreshHandler(); and then kick off the RefreshHander with redrawHandler.sleep();. After the DELAY, handleMessage() will be run.

Now in onDraw(), rather than draw the frown and then the smiley, just have it draw an image which is initially set to the frown (Bitmap image; image = frown;).

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(image, (getWidth/2), (getHeight/2), null);
}

Use updateImage() to change that image to a smiley so that when the view is invalidated the next onDraw() will draw the smiley.

private void updateImage() {
    image = smiley;
}
Mark Cramer
  • 2,614
  • 5
  • 33
  • 57
  • One more thing, don't call onDraw() directly. Invalidate the view and that will call onDraw() for you. – Mark Cramer Jul 29 '14 at 00:39
  • I think I'm close, but don't fully understand this. It gives me an error from view.invalidate();. Is this because I'm using a SurfaceView instead of a view or did I not use it right? [Here is what I came up with](http://pastebin.com/pdSmWB1T) – user3720864 Jul 29 '14 at 01:38
  • SurfaceView inherits all methods from android.view.View: http://developer.android.com/reference/android/view/SurfaceView.html. The problem is that you just put 'view.invalidate();'. The "view" is a pointer to the view that you wish to invalidate. Since your RefreshHandler is actually in the view you wish to invalidate, go with just 'invalidate();' or 'this.invalidate();'. Also, replace 'onDraw(c);' with with just 'invalidate();' or 'this.invalidate();'. – Mark Cramer Jul 29 '14 at 04:39
  • I tried switching to just invalidate(); and that removed the error, but it still seems to crash ([logcat](http://pastebin.com/0qrg9zDZ)). On a side-note, if I remove onDraw(c), when would my bitmaps actually be drawn, as that is when I run my onDraw method? – user3720864 Jul 29 '14 at 06:06
  • The problem is "Only the original thread that created a view hierarchy can touch its views". You can only update the UI from the main thread: http://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi. Two things: 1) What's the purpose of your runnable? I think you can just remove it altogether. 2) onDraw() is called when you invalidate the view. You don't call onDraw() directly, as you are doing. If you remove the runnable you'll obviously fix this, too. – Mark Cramer Jul 29 '14 at 06:38
  • I've removed runnable, so now it doesn't crash, but now the SurfaceView just appears black – user3720864 Jul 29 '14 at 06:51
  • You're making me work hard for this, and I'm getting an "avoid extended discussion" warning from SO. I don't have your latest code, but you need to do two things: 1) Invalidate the view. In the last version this was only done in handleMessage() of the RefreshHandler. You might want to 'kick off' the first one by adding invalidate(); to init(). 2) You need to start the RefreshHanler with redrawHandler.sleep();. Put that in init() as well and then 5000 ms later handleMessage() will run. If you want the RefreshHander to continue looping, add redrawHandler.sleep(); to handleMessage(). – Mark Cramer Jul 29 '14 at 14:54
0

I like to use the asyncTask method because It might be overkill for your application but you can use the same code for other more processing intense tasks. What I usually do is something like this in my activity.

public void callAsynchronousClass() {
    final Handler handler = new Handler();
    Timer timer = new Timer();
    TimerTask doAsynchronousTask = new TimerTask() {
        @Override
        public void run() {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (processing) {
                        try {
                            AsynchronousClassICreated myAsyncTask = new
                                    AsynchronousClassICreated();
                        myAsyncTask.execute();                            
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }
};
timer.schedule(doAsynchronousTask, 0, timeUntilUpdate);

This way you can still do as much processing as you want and then have the timer call the task repeatedly as many times as you want. Then you can use the on post execute method in the asyncTask like so.

public class AsynchronousssClassICreated extends AsyncTask<Void, Void, theDrawingToDo>{
    @Override
    protected void doInBackground(Void... params) {
    }

    @Override
    protected void onPostExecute(SomeClassToReturn theDrawingToDo) {
          //here is where you can do some drawing
    }

Anyway that’s how I like to do things like this. Like I said this might be overkill for what your looking for but it works for all sorts of cases where you might need a little more processing done as well.

mkrinblk
  • 896
  • 9
  • 21