Android Open Source - AniDroid Ani Sequence






From Project

Back to project page AniDroid.

License

The source code is released under:

GNU Lesser General Public License

If you think the Android project AniDroid listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
AniDroid (an Android animation library)/*www.  j ava 2  s  .co  m*/
Copyright (c) 2013 by Maik Vlcek

https://github.com/mediavrog/AniDroid

Based on:

Ani (a processing animation library)
Copyright (c) 2010 Benedikt Gro?

http://www.looksgood.de/libraries/Ani

Standing on the shoulders of giants:
Jack Doyle - TweenLite AS3 Library (http://blog.greensock.com/tweenlite/)
Robert Penner - Equations (http://robertpenner.com/easing/)
Andreas Schlegel - ControlP5 (http://www.sojamo.de/libraries/);
Ekene Ijeoma - Tween Processin Library (http://www.ekeneijeoma.com/processing/tween/)

AniCore, Ani and AniSequence includes many ideas and code of the nice people above!
Thanks a lot for sharing your code with the rest of the world!

This library is free software; you can redistribute it and/or modify it under the terms 
of the GNU Lesser General Public License as published by the Free Software Foundation; 
either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this 
library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 
Boston, MA 02110, USA
*/
package net.mediavrog.ani;

import android.util.Log;

import java.util.ArrayList;
import java.util.Collections;

/**
 * The Class AniSequence creates sequences out of instances of Ani.
 * Each step is either one single Ani or multiple Anis at the same time (in parallel).
 * Please note: AniSequence expects that you add only Anis which do have playMode/repeatMode set to BACKWARD, PLAYMODE_YOYO, COUNT and FOREVER!
 */
public class AniSequence implements Animation {
  private final static String TAG = AniSequence.class.getSimpleName();

  private ArrayList<Step> steps = new ArrayList<Step>();
  private ArrayList<Ani> addParallelAnisCollector = new ArrayList<Ani>();
  private boolean addParallel = false;
  private boolean isPlaying = false;
  private boolean isEnded = false;
  private int currentStep;
  private float durationTotal; // sum of all durations for each step
  private float time;

  private AnimationStateChangeListener listener;

  // -- Step (class to encapsulate multiple Ani instances) --
  private class Step {
    public ArrayList<Ani> anis = new ArrayList<Ani>();
    public float duration;
    public float startTime;
    public float endTime;

    public Step(Ani _ani) {
      anis.add(_ani);
      init();
    }

    public Step(Ani[] _anis) {
      Collections.addAll(anis, _anis);
      init();
    }

    private void init() {
      duration = 0;
      for (Ani animation : anis) {
        animation.setBegin();
        animation.seek(1.0f);
        // get the longest durationTotal of all anis in this step
        duration = Math.max(animation.getDurationTotal(), duration);
      }
      //Log.d(durationTotal+" "+stepLength);
    }

    public void pre() {
      for (Ani animation : anis) {
        animation.calculate();
      }
    }

    public boolean isFinished() {
      boolean isFinished = true;
      for(Ani animation : anis) {
        isFinished &= animation.isEnded();
      }
      return isFinished;
    }

    public boolean isPaused() {
      boolean isPaused = true;
      for(Ani animation : anis) {
        isPaused &= animation.isPaused();
      }
      return isPaused;
    }

    public void start() {
      for(Ani animation : anis) animation.start();
    }

    public void seekAll(Float _value) {
      for(Ani animation : anis) {
        animation.seek(_value);
      }
    }

    public void seek(Float _value) {
      float seekTime = _value * duration;
      for(Ani animation : anis) {
        float aniSeekValue = AniUtil.map(seekTime, 0.0f, animation.getDurationTotal(), 0.0f, 1.0f);
        animation.seek(aniSeekValue);
      }
    }

    public float getTime() {
      float currentTime = 0.0f;
      for(Ani animation : anis) {
        float seekValue = animation.getSeek() * animation.getDurationTotal();
        currentTime = Math.max(seekValue, currentTime);
      }
      return currentTime;
    }

    public void play() {
      for(Ani animation : anis) animation.resume();
    }

    public void pause() {
      for(Ani animation : anis) animation.pause();
    }
  }
  // -- end Step --

  /**
   * Instantiates a new ani sequence.
   */
  public AniSequence() {
    this(null);
  }

  public AniSequence(AnimationStateChangeListener listener) {
    this.listener = listener;
  }

  private void dispatchOnStart() {
    if (listener != null) {
      listener.onStart(this);
    }
  }

  private void dispatchOnEnd() {
    if (listener != null) {
      listener.onEnd(this);
    }
  }

  private void dispatchOnUpdate() {
    if (listener != null) {
      listener.onUpdate(this);
    }
  }

  private void dispatchOnResume() {
    if (listener != null) {
      listener.onResume(this);
    }
  }

  private void dispatchOnPause() {
    if (listener != null) {
      listener.onPause(this);
    }
  }

  /**
   * Call to calculate the current value
   */
  public void calculate() {
    if (isPlaying) {
      update();
    }
  }

  private void update() {
    if (steps.size() > 0) {
      Step tmpStep = steps.get(currentStep);

      // dispatch the calculations for all Ani objects in this step
      tmpStep.pre();

      // is current step finished? if so, start next step
      if (tmpStep.isFinished() && currentStep < steps.size() - 1) {
        currentStep++;
        Step nextStep = steps.get(currentStep);
        nextStep.start();
        tmpStep = nextStep;
      } else if (currentStep == steps.size() - 1) {
        isEnded = tmpStep.isFinished();
        if (isEnded) dispatchOnEnd();
      }

      time = tmpStep.startTime + tmpStep.getTime();
      dispatchOnUpdate();
    }
  }

  /**
   * Seek the sequence to any position: start = 0.0 end = 1.0
   *
   * @param theValue seek value: 0.0 - 1.0
   */
  public void seek(float theValue) {
    isEnded = false;
    // clamp between 0 and 1
    theValue = AniUtil.constrain(theValue, 0.0f, 1.0f);
    time = theValue * durationTotal;
    // find step corresponding to seek value
    for (int i = steps.size() - 1; i >= 0; i--) {
      Step tmpStep = steps.get(i);
      if (time >= tmpStep.startTime && time < tmpStep.endTime) {
        currentStep = i;
        break;
      }
    }

    // bring all variables back to their starting points 
    // and seek current step
    for (int i = steps.size() - 1; i >= currentStep; i--) {
      Step tmpStep = steps.get(i);
      if (i == currentStep) {
        float stepSeekTime = time - tmpStep.startTime;
        float stepSeekValue = AniUtil.map(stepSeekTime, 0.0f, tmpStep.duration, 0.0f, 1.0f);
        tmpStep.seek(stepSeekValue);
        break;
      } else {
        tmpStep.seekAll(0.0f);
      }
    }
  }

  /**
   * Adds a single Ani to the sequence
   *
   * @param theAni
   */
  public void add(Ani theAni) {
    if (addParallel) {
      addParallelAnisCollector.add(theAni);
    } else {
      Step tmpStep = new Step(theAni);
      steps.add(tmpStep);
    }
  }

  /**
   * Adds multiple Anis to the sequence
   *
   * @param theAnis
   */
  public void add(Ani[] theAnis) {
    // beginParallel() was not called before
    if (!addParallel) {
      beginStep();
      for (int i = 0; i < theAnis.length; i++) {
        add(theAnis[i]);
      }
      endStep();
    }
    // beginParallel() was called before, so add insertParallelAnisCollector
    else {
      for (int i = 0; i < theAnis.length; i++) {
        add(theAnis[i]);
      }
    }
  }

  /**
   * Begin a new step, everything after until endStep() is called is treated as a single step
   */
  public void beginStep() {
    addParallelAnisCollector = new ArrayList<Ani>();
    addParallel = true;
  }

  /**
   * End the step
   */
  public void endStep() {
    Ani[] tmpAnis = new Ani[addParallelAnisCollector.size()];
    for (int i = 0; i < tmpAnis.length; i++) {
      tmpAnis[i] = addParallelAnisCollector.get(i);
    }
    Step tmpStep = new Step(tmpAnis);
    steps.add(tmpStep);
    addParallel = false;
  }

  /**
   * Start the first step of the sequence
   */
  public void start() {
    isPlaying = true;
    isEnded = false;
    reconstruct();
    // start the first step
    Step tmpStep = steps.get(currentStep);
    tmpStep.start();
    dispatchOnStart();
  }

  /**
   * Resume the sequence from the current position in time (adjustable with seek)
   */
  public void resume() {
    Step tmpStep = steps.get(currentStep);
    tmpStep.play();
    isPlaying = true;
    isEnded = false;
    dispatchOnResume();
  }

  /**
   * Pause the sequence at the current position in time
   */
  public void pause() {
    Step tmpStep = steps.get(currentStep);
    tmpStep.pause();
    isPlaying = false;
    dispatchOnPause();
  }

  public boolean isPaused() {
    return steps.get(currentStep) != null && steps.get(currentStep).isPaused();
  }

  /**
   * Begin sequence
   */
  public void beginSequence() {
    // disable autostart feature of Ani
    Ani.noAutostart();
  }

  /**
   * End sequence
   */
  public void endSequence() {
    // enable autostart feature of Ani
    Ani.autostart();
    reconstruct();
  }

  // reconstruct all variables back to their origin values (before the sequence was created)
  private void reconstruct() {
    currentStep = 0;

    // calc global durationTotal of all steps
    // set start- and end times to the steps
    durationTotal = 0;
    for (int i = 0; i < steps.size(); i++) {
      Step tmpStep = steps.get(i);
      tmpStep.pause(); // just in case this is a re-start of the whole sequence
      tmpStep.startTime = durationTotal;
      tmpStep.endTime = durationTotal + tmpStep.duration;
      durationTotal += tmpStep.duration;
      //Log.d(tmpStep.startTime+" "+tmpStep.endTime+"  "+tmpStep.duration);
    }
    // bring all variables back to their starting points 
    for (int i = steps.size() - 1; i >= 0; i--) {
      Step tmpStep = steps.get(i);
      tmpStep.seekAll(0.0f);
    }
  }

  /**
   * Checks if sequence is playing
   *
   * @return true, if sequence is playing
   */
  public boolean isPlaying() {
    return isPlaying;
  }

  /**
   * Checks if the sequence is ended.
   *
   * @return true, if the sequence is ended
   */
  public boolean isEnded() {
    return isEnded;
  }

  /**
   * Gets the current seek value (start = 0.0 end = 1.0)
   *
   * @return theValue seek value
   */
  public float getSeek() {
    return AniUtil.constrain(time / durationTotal, 0.0f, 1.0f);
  }

  /**
   * Gets the duration of the sequence
   *
   * @return the duration
   */
  public float getDuration() {
    return durationTotal;
  }

  /**
   * Gets the current step
   *
   * @return the step number
   */
  public int getStepNumber() {
    return currentStep + 1;
  }

  /**
   * Gets the count step
   *
   * @return the step count
   */
  public int getStepCount() {
    return steps.size();
  }

  /**
   * Gets the current position in time of the sequence
   *
   * @return the position
   */
  public float getTime() {
    return time;
  }

  @Override
  public Object getTarget() {
    Step curStep = steps.get(currentStep);
    if (curStep.anis != null && !curStep.anis.isEmpty()) {
      if (curStep.anis.size() > 1) {
        Log.d(TAG, "Warning. There are " + curStep.anis.size() + " different animations in parallel. Returning the target of the animation with index 0.");
      }
      return curStep.anis.get(0).getTarget();
    } else {
      return null;
    }
  }
}




Java Source Code List

net.mediavrog.ani.AniConstants.java
net.mediavrog.ani.AniCore.java
net.mediavrog.ani.AniSequence.java
net.mediavrog.ani.AniUtil.java
net.mediavrog.ani.Ani.java
net.mediavrog.ani.AnimationStateChangeListener.java
net.mediavrog.ani.Animation.java
net.mediavrog.ani.SimpleAnimationStateChangeListener.java
net.mediavrog.ani.easing.Back.java
net.mediavrog.ani.easing.Bounce.java
net.mediavrog.ani.easing.Circ.java
net.mediavrog.ani.easing.Cubic.java
net.mediavrog.ani.easing.CustomEasing.java
net.mediavrog.ani.easing.Easing.java
net.mediavrog.ani.easing.Elastic.java
net.mediavrog.ani.easing.Expo.java
net.mediavrog.ani.easing.Linear.java
net.mediavrog.ani.easing.Quad.java
net.mediavrog.ani.easing.Quart.java
net.mediavrog.ani.easing.Quint.java
net.mediavrog.ani.easing.Sine.java