Search code examples
androidanimationframe-rate

android:fixed frame rate animation


I use GLSurfaceView.RENDERMODE_WHEN_DIRTY mode to update my glSurfaceView.

To draw smooth animations I need some controller which will update surface view at a constant frame-rate.I know I can achieve this with Looper class, but I believe there have to be native way of doing this.Ideally I need something like:

 Animation anim=new Animation(..);
 anim.setListener(this);
 anim.start();
 ...
private void onNextFrame(float pos) {
 //do my animation according to position value
}
private void onAnimFinished() {
//animation is finished
}

UPD: Solved, see my answer.


Solution

  • Solved with the following Class:

    package tween_test;
    import android.os.SystemClock;
    
    public class Tween extends Thread {
          public interface OnTweenUpdate {
              public void onNextFrame(Tween tween,float position);
              public void onTweenFinish(Tween tween);
          }
          public enum Easing {
              REGULAR;
          }
          public enum Types {
              LINEAR(1000, false,Easing.REGULAR);
              private final long duration;
              private final boolean looped;
              private final Easing easing;
              Types(long duration, boolean looped,Easing easing) {
                  this.duration = duration;
                  this.looped = looped;
                  this.easing=easing;
              }
              public Easing easing() {
                  return this.easing;
              }
              public long duration() {
                  return this.duration;
              }
              public boolean looped() {
            return looped;
        }
    }
          private final int FPS=60;
          private final int FRAME_DELTA=1000/FPS;
          private long lastFrameTimestamp;
          private OnTweenUpdate listener;
          private Types type;
          private long startTS;
          public Tween(Types type) {
              super();
              this.type=type;
          }
          public void setListener(OnTweenUpdate listener) {
              this.listener=listener;
          }
          @Override
          public void start() {
              lastFrameTimestamp=startTS=SystemClock.elapsedRealtime();
              super.start();
          }
          @Override
          public void run() {
              while (!isInterrupted()) {
                  long cts= SystemClock.elapsedRealtime();
                  if (cts-lastFrameTimestamp>=FRAME_DELTA) {
                      lastFrameTimestamp=cts;
                      if (listener!=null)
                          listener.onNextFrame(this,ease((float)(cts-startTS)/type.duration()));
                  }
                  if(cts>=startTS+type.duration()) {
                      boolean looped=type.looped();
                      if (!looped) {
                          if (listener != null)
                              listener.onTweenFinish(this);
                          this.interrupt();
                      } else {
                          lastFrameTimestamp=startTS=cts;
                      }
                  }
              }
          }
          public void fforward() {
              if (listener!=null)
                  listener.onTweenFinish(this);
              this.interrupt();
          }
          private float ease(float pos) {
              switch (type.easing()) {
                  case REGULAR:
                      return  pos;
              }
              return 0f;
          }
    }