0

I need to run a complex custom animation in an Android app. For this, I wrote a function that needs to be called repeatedly and uses the current time-stamp to calculate positions, colors, shadows, etc. of View elements on the screen.

There seem to be a whole bunch of different approaches I could use to have this function called:

In my current implementation I'm just calling my animation-function from a separate thread via runOnUIThread. While it works, it doesn't seem like a good idea as it might flood the event queue with messages faster than they can be handled or are needed given the screen refresh...

I posted a similar question for iOS a couple hours back and @IanMacDonald had an amazing answer for me that allows my function to be called once before every screen refresh and it makes for awesomely smooth animations. Is there something similar that I can do in Android, i.e. have it call my function every time the screen is about to be refreshed?

If possible, I would like to use a method that is as backward-compatible as possible, preferably API 7 or below...

Community
  • 1
  • 1
Markus A.
  • 12,349
  • 8
  • 52
  • 116

1 Answers1

1

I did some more reading and it seems like the current preferred approach would be to use the Choreographer's postFrameCallback method. Unfortunately Choreographer was only added in API 16, which is quite a bit too restrictive for my use-case.

Here's a way to do it via "recursive" View.postDelayed calls (i.e. the Runnable will reissue the View.postDelayed for itself) with the delay calculated from Display.getRefreshRate:

import android.app.Activity;
import android.view.Display;
import android.view.View;

public abstract class DisplayRefreshTimer implements Runnable {

    public DisplayRefreshTimer(final View view) {
        Activity activity = (Activity) view.getContext();
        Display display = activity.getWindowManager().getDefaultDisplay();
        final int delay = (int) (1000 / display.getRefreshRate());
        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                view.postDelayed(this, delay);
                DisplayRefreshTimer.this.run();
            }
        }, delay);
    }
}

To use this, just sub-class it, override run() and instantiate passing it your current view:

new DisplayRefreshTimer(currentView) {
    @Override
    public void run() {
        // Do your magic here...
    }
};

If you're targeting API 17+, you can use View.getDisplay instead of going through the activity, but then you might as well look into Choreographer instead as mentioned above.

Please don't let this preliminary answer stop you from posting other ideas. I wouldn't be surprised if there is a better solution still hiding out there somewhere. Maybe in the OpenGL framework? At first glance I couldn't find anything there but I'll keep looking, too.

Markus A.
  • 12,349
  • 8
  • 52
  • 116