Android Open Source - AniDroid Ani Core






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)//from   ww w . j  ava 2  s  .c o  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 net.mediavrog.ani.easing.Easing;

import java.lang.reflect.Field;

/**
 * The Class AniCore encapsulates the core features for Ani and AniSequence, it's not recommended to use this class standalone!.
 */
public class AniCore implements AniConstants, Animation {
  private final static String TAG = AniCore.class.getSimpleName();

  private String id;
  private String targetName;
  private String fieldName;

  private boolean isRegistered = false;
  private Object targetObject;
  private Field targetField;
  private Class<?> targetObjectFieldType;

  private float position;
  private float begin;
  private float change;
  private float end;

  private float beginTime;
  private float time;
  private float durationEasing;
  private float durationDelay;
  // durationEasing + durationDelay = durationTotal
  private float durationTotal;
  private String timeMode;

  // stopwatch timer for pause and resume
  private float pauseTime = 0;

  private Easing easing;

  private AnimationStateChangeListener listener;

  private String playMode = PLAYMODE_FORWARD;
  private String playDirection = PLAYMODE_FORWARD;
  private int repeatNumber; // on purpose, set in start()
  private int repeatCount = 1;

  private boolean isRepeating = false;
  private boolean isDelaying = false;
  private boolean isPlaying = false;
  private boolean isEnded = false;
  private boolean isPaused = false;

  private long milliSinceStart;

  /**
   * Instantiates a new ani core.
   *
   * @param theAutostart             the autostart
   * @param theTargetObject          the target object
   * @param theDurationEasing        the duration easing
   * @param theDurationDelay         the duration delay
   * @param theTargetObjectFieldName the target object field name
   * @param theEnd                   the end
   * @param theEasing                the easing
   * @param theTimeMode              the time mode
   */
  public AniCore(String theAutostart, Object theTargetObject,
           float theDurationEasing, float theDurationDelay,
           String theTargetObjectFieldName, float theEnd, Easing theEasing,
           String theTimeMode, AnimationStateChangeListener listener) {

    targetObject = theTargetObject;
    // generate unique id
    targetName = targetObject.toString();
    fieldName = theTargetObjectFieldName;
    id = targetName + "_" + fieldName;

    end = theEnd;
    durationEasing = theDurationEasing;
    durationDelay = theDurationDelay;
    durationTotal = durationEasing + durationDelay;

    timeMode = theTimeMode;
    setEasing(theEasing);

    setOnAnimationStateChangeListener(listener);

    // set begin value to field value
    boolean setBeginSuccess = setBegin();
    if (setBeginSuccess && theAutostart == AUTOSTART) {
      start();
    }
  }

  /**
   * Sets the begin of the animation to the current value of the target.
   *
   * @return true, if successful
   */
  public boolean setBegin() {
    // -- check and find fields --
    boolean foundType = false;
    boolean foundField = false;
    Class<?> targetClass = targetObject.getClass();

    // check field inheritance
    while (targetClass != null) {
      for (int i = 0; i < targetClass.getDeclaredFields().length; i++) {
        if (targetClass.getDeclaredFields()[i].getName().equals(fieldName)) {
          // get field type
          targetObjectFieldType = targetClass.getDeclaredFields()[i].getType();
          foundField = true;
          break;
        }
      }
      if (foundField) break;
      else targetClass = targetClass.getSuperclass();
    }

    //Log.d(ANI_DEBUG_PREFIX, " Found field? " + foundField);

    // when running in applet mode. setAccessible(true) is not working for fields!
    // AccessControlException is thrown. therefore, make fields in your code public.
    if (foundField) {
      try {
        // get field
        targetField = targetClass.getDeclaredField(fieldName);
        // make accessible
        try {
          targetField.setAccessible(true);
        } catch (java.security.AccessControlException e) {
          printSecurityWarning(e);
        }
        // get field value + check type
        try {
          if (targetObjectFieldType == Float.TYPE) {
            begin = targetField.getFloat(targetObject);
            foundType = true;
          } else if (targetObjectFieldType == Integer.TYPE) {
            begin = new Float(targetField.getInt(targetObject));
            foundType = true;
          } else {
            String text = ANI_DEBUG_PREFIX + "Wrong Type @ AniCore "
                + targetName + " ("
                + targetField.getType().getName() + ")"
                + " is not float or int";
            throw new ClassCastException(text);
          }
        } catch (Exception ex) {
          printSecurityWarning(ex);
        }
      } catch (NoSuchFieldException e) {
        Log.d(ANI_DEBUG_PREFIX, " Error @ AniCore. " + e);
      }
    }

    if (foundType && foundField) {
      change = end - begin;
      return true;
    } else {
      return false;
    }
  }

  /**
   * Sets the begin of the animation to a new value.
   *
   * @param theBegin the new begin
   */
  public void setBegin(float theBegin) {
    begin = theBegin;
    change = end - begin;
  }

  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 dispatchOnDelayEnd() {
    if (listener != null) {
      listener.onDelayEnd(this);
    }
  }

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

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

  private void printSecurityWarning(Exception theE) {
    // AccessControlException required for applets.
    Log.d(ANI_DEBUG_PREFIX, " Error @ AniCore. ");
    theE.printStackTrace();
  }

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

  /**
   * Start the animation.
   */
  public void start() {
    // register pre()
    if (!isRegistered) {
      isRegistered = true;
      repeatNumber = 1;
      dispatchOnStart();
    }
    milliSinceStart = System.currentTimeMillis();
    seek(0.0f);
    isPlaying = true;
    isPaused = false;
    isEnded = false;
  }

  /**
   * End the animation.
   */
  public void end() {
    isDelaying = false;
    seek(1.0f);
    isPlaying = false;
    isPaused = false;
    isEnded = true;
    // unregister pre()
    if (isRegistered) {
      isRegistered = false;
    }
    dispatchOnEnd();
  }

  private void update() {
    setTime(getTime());

    // delay or easing?
    if (time < durationDelay) {
      isDelaying = true;
      position = begin;
    } else {
      if (isDelaying) {
        setBegin();
        position = begin;
        dispatchOnDelayEnd();
      }
      isDelaying = false;

      if (time >= durationTotal) {
        if (isRepeating) {
          if (repeatCount == 1 || repeatNumber <= repeatCount - 1 || repeatCount == -1) {
            if (playMode == PLAYMODE_YOYO) reverse();
            start();
            repeatNumber++;
          } else {
            isRepeating = false;
          }
        } else {
          end();
        }

      } else {
        updatePosition();
      }

      updateTargetObjectField();
      dispatchOnUpdate();
    }
    //Log.d("isEasing: "+isEasing+" isDelaying: "+isDelaying+" time: "+time+" position: "+position);
  }

  private void updatePosition() {
    try {
      //Log.d(ANI_DEBUG_PREFIX, "updatePosition! Time:" + time + " / " +"DurationDelay:" + durationDelay + " / " +"Begin:" + begin + " / " +"Change: " + change + " / " +"durationEasing:" + durationEasing);
      position = easing.calcEasing(time - durationDelay, begin, change, durationEasing);
    } catch (Exception e) {
      Log.d(ANI_DEBUG_PREFIX, " Error @ AniCore -> updatePosition(). " + e);
    }
  }

  private void updateTargetObjectField() {
    try {
      if (targetObjectFieldType == Float.TYPE) {
        targetField.setFloat(targetObject, position);
      } else if (targetObjectFieldType == Integer.TYPE) {
        targetField.setInt(targetObject, mapPositionToInt(position));
        //Log.d(TAG, "Position is " + position + " and gets mapped to INT " + targetField.getInt(targetObject));
      }
    } catch (Exception e) {
      Log.d(ANI_DEBUG_PREFIX, " Error @ AniCore -> updateTargetObjectField(). " + e);
    }
  }

  // TODO: needs refactoring for YOYO effect
  private int mapPositionToInt(float position) {
    float rangePerInt = Math.abs(change / (change + 1));
    float currentRangeFactor = (position - begin) / rangePerInt;
    float positionToCheck = begin + currentRangeFactor;
    return (int)(change < 0 ? Math.ceil(positionToCheck) : Math.floor(positionToCheck));
  }

  public float getTime() {
    if (timeMode != TIMEMODE_SECONDS) throw new RuntimeException("Timemodes other than SECONDS not implemented");
    //return (timeMode == SECONDS) ? ((System.currentTimeMillis() - beginTime) / 1000) : )papplet.frameCount - beginTime));
    return (millis() - beginTime) / 1000;
  }

  private void setTime(float theTime) {
    if (timeMode != TIMEMODE_SECONDS) throw new RuntimeException("Timemodes other than SECONDS not implemented");

    time = theTime;
    //beginTime = (timeMode == SECONDS) ? (System.currentTimeMillis() - time * 1000) : (papplet.frameCount - time));
    beginTime = millis() - time * 1000;
  }

  private long millis() {
    return System.currentTimeMillis() - milliSinceStart;
  }

  /**
   * Pause the animation at the current position in time.
   */
  public void pause() {
    isPlaying = false;
    isPaused = true;
    pauseTime = getTime();
    dispatchOnPause();
  }

  /**
   * Resume the animation from the current position in time (adjustable with seek).
   */
  public void resume() {
    if (!isRegistered) {
      isRegistered = true;
    }
    if (!isPlaying && !isEnded) {
      isPlaying = true;
      isPaused = false;
      // remember the pause time, seek to last time
      seek(pauseTime / durationTotal);
      dispatchOnResume();
    }
  }

  /**
   * Seek the Animation to any position: start = 0.0 end = 1.0
   *
   * @param theValue seek value
   */
  public void seek(float theValue) {
    // clamp between 0 and 1
    theValue = AniUtil.constrain(theValue, 0.0f, 1.0f);
    setTime(theValue * durationTotal);
    pauseTime = time; // overwrite old pause time
    isEnded = false;
    // only use easing function to calc position if time > durationDelay
    if (time < durationDelay) {
      //setBegin();
      position = begin;
    } else {
      updatePosition();
    }
    updateTargetObjectField();
  }

  /**
   * 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);
  }

  /**
   * Swap begin end of the animation.
   */
  public void reverse() {
    float beginTemp = begin;
    float endTemp = end;

    begin = endTemp;
    end = beginTemp;
    change = end - begin;

    if (playDirection == PLAYMODE_FORWARD) {
      playDirection = PLAYMODE_BACKWARD;
    } else if (playDirection == PLAYMODE_BACKWARD) {
      playDirection = PLAYMODE_FORWARD;
    }
  }

  /**
   * Gets the time mode.
   *
   * @return the time mode
   */
  public String getTimeMode() {
    return timeMode;
  }

  /**
   * Sets the time mode: SECONDS or FRAMES.
   *
   * @param theTimeMode the new time mode
   */
  public void setTimeMode(String theTimeMode) {
    timeMode = theTimeMode;
  }

  /**
   * Gets the easing.
   *
   * @return the easing name
   */
  public Easing getEasing() {
    return easing;
  }

  /**
   * Sets the easing: LINEAR, QUAD_IN, QUAD_OUT, QUAD_IN_OUT, CUBIC_IN, CUBIC_IN_OUT, CUBIC_OUT, QUART_IN, QUART_OUT, QUART_IN_OUT, QUINT_IN, QUINT_OUT, QUINT_IN_OUT, SINE_IN, SINE_OUT, SINE_IN_OUT, CIRC_IN, CIRC_OUT, CIRC_IN_OUT, EXPO_IN, EXPO_OUT, EXPO_IN_OUT, BACK_IN, BACK_OUT, BACK_IN_OUT, BOUNCE_IN, BOUNCE_OUT, BOUNCE_IN_OUT, ELASTIC_IN, ELASTIC_OUT, ELASTIC_IN_OUT or use a Custom Easing.
   *
   * @param theEasing
   */
  public void setEasing(Easing theEasing) {
    easing = theEasing;
  }

  /**
   * Gets the target object.
   *
   * @return the target object
   */
  public Object getTarget() {
    return targetObject;
  }

  /**
   * Gets the play mode.
   *
   * @return the play mode
   */
  public String getPlayMode() {
    return playMode;
  }

  /**
   * Sets the play mode: FORWARD, BACKWARD, PLAYMODE_YOYO.
   *
   * @param thePlayMode the new play mode
   */
  public void setPlayMode(String thePlayMode) {
    String oldPlayDirection = playDirection;

    if (thePlayMode == PLAYMODE_FORWARD) {
      if (oldPlayDirection == PLAYMODE_BACKWARD) reverse();
      playMode = playDirection = PLAYMODE_FORWARD;
    } else if (thePlayMode == PLAYMODE_BACKWARD) {
      if (oldPlayDirection == PLAYMODE_FORWARD) reverse();
      playMode = playDirection = PLAYMODE_BACKWARD;
    } else if (thePlayMode == PLAYMODE_YOYO) {
      playMode = PLAYMODE_YOYO;
    }
  }


  /**
   * Repeat the animation forever.
   */
  public void repeat() {
    isRepeating = true;
    repeatCount = -1;
  }

  /**
   * Stop any repeating.
   */
  public void noRepeat() {
    isRepeating = false;
    repeatCount = 1;
  }

  /**
   * Sets the repeat count.
   *
   * @param theRepeatCount the new repeat count
   */
  public void repeat(int theRepeatCount) {
    if (theRepeatCount > 1) {
      isRepeating = true;
      repeatCount = theRepeatCount;
    } else {
      isRepeating = false;
      repeatCount = 1;
    }
  }

  /**
   * Gets the repeat count.
   *
   * @return the repeat count
   */
  public int getRepeatCount() {
    return repeatCount;
  }

  /**
   * Gets the current repeat number.
   *
   * @return the repeat count
   */
  public int getRepeatNumber() {
    return repeatNumber;
  }

  /**
   * Gets the direction.
   *
   * @return the direction
   */
  public String getDirection() {
    return playDirection;
  }

  /**
   * Gets the position.
   *
   * @return the position
   */
  public float getPosition() {
    return position;
  }

  /**
   * Gets the begin.
   *
   * @return the begin
   */
  public float getBegin() {
    return begin;
  }

  /**
   * Sets the end.
   *
   * @param theEnd the new end
   */
  public void setEnd(float theEnd) {
    end = theEnd;
    change = end - begin;
  }

  /**
   * Gets the end.
   *
   * @return the end
   */
  public float getEnd() {
    return end;
  }

  /**
   * Gets the duration: duration easing + duration delay.
   *
   * @return the duration total
   */
  public float getDurationTotal() {
    return durationTotal;
  }

  /**
   * Gets the duration of delay.
   *
   * @return the delay
   */
  public float getDelay() {
    return durationDelay;
  }

  /**
   * Sets the delay duration.
   *
   * @param theDurationDelay the new delay duration
   */
  public void setDelay(float theDurationDelay) {
    durationDelay = theDurationDelay;
    durationTotal = durationEasing + durationDelay;
  }

  /**
   * Gets the duration.
   *
   * @return the duration
   */
  public float getDuration() {
    return durationEasing;
  }

  /**
   * Sets the duration.
   *
   * @param theDurationEasing the new duration
   */
  public void setDuration(float theDurationEasing) {
    durationEasing = theDurationEasing;
    durationTotal = durationEasing + durationDelay;
  }

  /**
   * Gets the id.
   *
   * @return the id
   */
  public String getId() {
    return id;
  }

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

  /**
   * Checks if the animation is repeating.
   *
   * @return true, if the animation is repeating
   */
  public boolean isRepeating() {
    return isRepeating;
  }

  /**
   * Checks if the animation is delaying.
   *
   * @return true, if the animation is delaying
   */
  public boolean isDelaying() {
    return isDelaying;
  }

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

  /**
   * Checks if the animation is paused.
   *
   * @return true, if the animation is paused
   */
  public boolean isPaused() {
    return isPaused;
  }

  public void setOnAnimationStateChangeListener(AnimationStateChangeListener listener) {
    this.listener = listener;
  }

}




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