package com.example.qyh.joe.customview; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.TargetApi; import android.os.Build; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; import java.util.ArrayList; import java.util.List; public class MovingViewAnimator { //movement type vars public static final int HORIZONTAL_MOVE = 1; public static final int VERTICAL_MOVE = 2; public static final int DIAGONAL_MOVE = 3; public static final int AUTO_MOVE = 0; public static final int NONE_MOVE = -1; //Animators and view vars private AnimatorSet mAnimatorSet; private Animator.AnimatorListener animatorListener; private View mView; //helper vars private boolean isRunning; private int currentLoop; private boolean infiniteRepetition = true; private ArrayList<Float> pathDistances; //user vars private int loopCount = -1; private int movementType; private float offsetWidth, offsetHeight; private int mSpeed = 50; private long mDelay = 0; private Interpolator mInterpolator; private Animator.AnimatorListener repeatAnimatorListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(final Animator animation) { //super.onAnimationEnd(animation); //View always in UI threat! mView.post((new Runnable() { public void run() { if (isRunning) { if (infiniteRepetition) { mAnimatorSet.start(); if (animatorListener != null) animatorListener.onAnimationRepeat(animation); } else { currentLoop--; if (currentLoop > 0) { mAnimatorSet.start(); if (animatorListener != null) animatorListener.onAnimationRepeat(animation); } } } } })); } }; public MovingViewAnimator(View imgView) { mView = imgView; isRunning = false; mAnimatorSet = new AnimatorSet(); pathDistances = new ArrayList<>(); mInterpolator = new AccelerateDecelerateInterpolator(); } public MovingViewAnimator(View imgView, int type, float width, float height) { this(imgView); updateValues(type, width, height); } private void init() { setUpAnimator(); updateListener(); setUpValues(); } /** * Sets the animation travel types. */ private void setUpAnimator() { AnimatorSet animatorSet = new AnimatorSet(); pathDistances.clear(); switch (movementType) { case HORIZONTAL_MOVE: animatorSet.playSequentially(createHorizontalAnimator(0, offsetWidth), createHorizontalAnimator(offsetWidth, 0)); break; case VERTICAL_MOVE: animatorSet.playSequentially(createVerticalAnimator(0, offsetHeight), createVerticalAnimator(offsetHeight, 0)); break; case DIAGONAL_MOVE: animatorSet.playSequentially(createDiagonalAnimator(0, offsetWidth, 0, offsetHeight), createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0)); break; case AUTO_MOVE: animatorSet.playSequentially( createVerticalAnimator(0, offsetHeight), createDiagonalAnimator(0, offsetWidth, offsetHeight, 0), createHorizontalAnimator(offsetWidth, 0), createDiagonalAnimator(0, offsetWidth, 0, offsetHeight), createHorizontalAnimator(offsetWidth, 0), createVerticalAnimator(offsetHeight, 0)); } if (mAnimatorSet != null) { mAnimatorSet.removeAllListeners(); stop(); } mAnimatorSet = animatorSet; } private void setUpValues() { addListener(animatorListener); setSpeed(mSpeed); setStartDelay(mDelay); setRepetition(loopCount); setInterpolator(mInterpolator); } /** * <code>AnimatorSet</code> doesn't have a reverse mode... * Tricky but works. */ private void updateListener() { mAnimatorSet.addListener(repeatAnimatorListener); } /** * Update animation base vars. * * @param type new movement type. * @param w new width offset. * @param h new height offset. */ public void updateValues(int type, float w, float h) { this.movementType = type; this.offsetWidth = w; this.offsetHeight = h; init(); } public void setMovementType(int type) { updateValues(type, offsetWidth, offsetHeight); } public void setOffsets(float w, float h) { updateValues(movementType, w, h); } public void start() { if (movementType != NONE_MOVE) { isRunning = true; if (!infiniteRepetition) currentLoop = loopCount; mAnimatorSet.start(); } } public void cancel() { if(isRunning) { mAnimatorSet.removeListener(repeatAnimatorListener); mAnimatorSet.cancel(); } } @TargetApi(19) public void pause() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; if(mAnimatorSet.isStarted()) mAnimatorSet.pause(); } @TargetApi(19) public void resume() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; if(mAnimatorSet.isPaused()) mAnimatorSet.resume(); } public void stop() { isRunning = false; mAnimatorSet.removeListener(repeatAnimatorListener); mAnimatorSet.end(); mView.clearAnimation(); } public void setRepetition(int repetition) { if (repetition < 0) infiniteRepetition = true; else { loopCount = repetition; currentLoop = loopCount; infiniteRepetition = false; } } public Builder addCustomMovement() { return new Builder(); } public void clearCustomMovement() { init(); start(); } public int getMovementType() { return movementType; } public int getRemainingRepetitions() { return (infiniteRepetition) ? -1 : currentLoop; } public void setInterpolator(Interpolator interpolator) { mInterpolator = interpolator; mAnimatorSet.setInterpolator(interpolator); } public void setStartDelay(long time) { mDelay = time; mAnimatorSet.setStartDelay(time); } /** * For each animation child sets their duration using length/speed operation. * * @param speed new speed. */ public void setSpeed(int speed) { mSpeed = speed; List<Animator> listAnimator = mAnimatorSet.getChildAnimations(); for (int i = 0; i < listAnimator.size(); i++) { Animator a = listAnimator.get(i); a.setDuration(parseSpeed(pathDistances.get(i))); } } public void addListener(Animator.AnimatorListener listener) { clearListener(); if (listener != null) { animatorListener = listener; mAnimatorSet.addListener(animatorListener); } } public void clearListener() { if (animatorListener != null) { mAnimatorSet.removeListener(animatorListener); animatorListener = null; } } private long parseSpeed(float distance) { return (long) ((distance / (float) mSpeed) * 1000f); } private ObjectAnimator createHorizontalAnimator(float startValue, float endValue) { pathDistances.add(Math.abs(startValue - endValue)); return createObjectAnimation("scrollX", startValue, endValue); } private ObjectAnimator createVerticalAnimator(float startValue, float endValue) { pathDistances.add(Math.abs(startValue - endValue)); return createObjectAnimation("scrollY", startValue, endValue); } private ObjectAnimator createDiagonalAnimator(float startW, float endW, float startH, float endH) { float diagonal = Pythagoras(Math.abs(startW - endW), Math.abs(startH - endH)); pathDistances.add(diagonal); PropertyValuesHolder pvhX = createPropertyValuesHolder("scrollX", startW, endW); PropertyValuesHolder pvhY = createPropertyValuesHolder("scrollY", startH, endH); return ObjectAnimator.ofPropertyValuesHolder(mView, pvhX, pvhY); } private ObjectAnimator createObjectAnimation(String prop, float startValue, float endValue) { return ObjectAnimator.ofInt(mView, prop, (int) startValue, (int) endValue); } private PropertyValuesHolder createPropertyValuesHolder(String prop, float startValue, float endValue) { return PropertyValuesHolder.ofInt(prop, (int) startValue, (int) endValue); } private static float Pythagoras(float a, float b) { return (float) Math.sqrt((a * a) + (b * b)); } /** * Class for create custom AUTO travel type regardless movementType var. */ public class Builder { private ArrayList<Animator> mList; private Builder() { mList = new ArrayList<>(); pathDistances.clear(); } public Builder addHorizontalMoveToRight() { mList.add(createHorizontalAnimator(0, offsetWidth)); return this; } public Builder addHorizontalMoveToLeft() { mList.add(createHorizontalAnimator(offsetWidth, 0)); return this; } public Builder addVerticalMoveToDown() { mList.add(createVerticalAnimator(0, offsetHeight)); return this; } public Builder addVerticalMoveToUp() { mList.add(createVerticalAnimator(offsetHeight, 0)); return this; } public Builder addDiagonalMoveToDownRight() { mList.add(createDiagonalAnimator(0, offsetWidth, 0, offsetHeight)); return this; } public Builder addDiagonalMoveToDownLeft() { mList.add(createDiagonalAnimator(offsetWidth, 0, 0, offsetHeight)); return this; } public Builder addDiagonalMoveToUpRight() { mList.add(createDiagonalAnimator(0, offsetWidth, offsetHeight, 0)); return this; } public Builder addDiagonalMoveToUpLeft() { mList.add(createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0)); return this; } public void start() { mAnimatorSet.removeAllListeners(); stop(); mAnimatorSet = new AnimatorSet(); mAnimatorSet.playSequentially(mList); updateListener(); setUpValues(); MovingViewAnimator.this.start(); } } }