package com.blundell.quicksand.demo.amazeanimation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.blundell.quicksand.Quicksand;
import java.util.concurrent.TimeUnit;
/**
* original author SiYao of https://github.com/2359media/EasyAndroidAnimations
* <p/>
* It doesn't matter what this animation is or how it works. This is just an example of a complex animation.
* See the {@link #monitor(ImageView[])}} method for how you integrate with Quicksand.
*/
public class ExplodeAnimation {
public static final String KEY_ANIMATION_SET = "NewKey";
private static final int MATRIX_3X3 = 33;
private static final long DURATION_LONG = TimeUnit.SECONDS.toMillis(2);
private final View view;
private int xParts;
private int yParts;
private ViewGroup parentView;
private TimeInterpolator interpolator;
private long duration;
private AnimationListener listener;
public interface AnimationListener {
/**
* This method is called when the animation ends.
*
* @param animation The Animation object.
*/
void onAnimationEnd(ExplodeAnimation animation);
}
/**
* This animation creates a bitmap of the view, divides them into
* customizable number of X and Y parts and translates the parts away from
* the center of the view to mimic an explosion. The number of parts can
* vary from 1x2 to 3x3. The view is set to invisible and added back for
* reuse.
*
* @param view The view to be animated.
*/
ExplodeAnimation(View view) {
this.view = view;
setExplodeMatrix(MATRIX_3X3);
interpolator = new AccelerateDecelerateInterpolator();
duration = DURATION_LONG;
listener = null;
}
public void animate() {
final LinearLayout explodeLayout = new LinearLayout(view.getContext());
LinearLayout[] layouts = new LinearLayout[yParts];
parentView = (ViewGroup) view.getParent();
explodeLayout.setLayoutParams(view.getLayoutParams());
explodeLayout.setOrientation(LinearLayout.VERTICAL);
explodeLayout.setClipChildren(false);
view.setDrawingCacheEnabled(true);
Bitmap viewBmp = view.getDrawingCache(true);
int totalParts = xParts * yParts, bmpWidth = viewBmp.getWidth()
/ xParts, bmpHeight = viewBmp.getHeight() / yParts, widthCount = 0, heightCount = 0, middleXPart = (xParts - 1) / 2;
int[] translation;
ImageView[] imageViews = new ImageView[totalParts];
for (int i = 0; i < totalParts; i++) {
int translateX = 0, translateY = 0;
if (i % xParts == 0) {
if (i != 0) {
heightCount++;
}
widthCount = 0;
layouts[heightCount] = new LinearLayout(view.getContext());
layouts[heightCount].setClipChildren(false);
translation = sideTranslation(
heightCount, bmpWidth, bmpHeight,
yParts);
translateX = translation[0];
translateY = translation[1];
} else if (i % xParts == xParts - 1) {
translation = sideTranslation(
heightCount, -bmpWidth,
bmpHeight, yParts);
translateX = translation[0];
translateY = translation[1];
} else {
if (widthCount == middleXPart) {
if (heightCount == 0) {
translateX = 0;
if (yParts != 1) {
translateY = -bmpHeight;
}
} else if (heightCount == yParts - 1) {
translateX = 0;
translateY = bmpHeight;
}
}
}
if (xParts == 1) {
translation = sideTranslation(
heightCount, 0, bmpHeight,
yParts);
translateX = translation[0];
translateY = translation[1];
}
imageViews[i] = new ImageView(view.getContext());
imageViews[i]
.setImageBitmap(
Bitmap.createBitmap(
viewBmp, bmpWidth
* widthCount, bmpHeight * heightCount, bmpWidth,
bmpHeight));
imageViews[i].animate().translationXBy(translateX)
.translationYBy(translateY).alpha(0)
.setInterpolator(interpolator).setDuration(duration);
layouts[heightCount].addView(imageViews[i]);
widthCount++;
}
monitor(imageViews);
for (int i = 0; i < yParts; i++) {
explodeLayout.addView(layouts[i]);
}
final int positionView = parentView.indexOfChild(view);
parentView.removeView(view);
parentView.addView(explodeLayout, positionView);
ViewGroup rootView = (ViewGroup) explodeLayout.getRootView();
while (!parentView.equals(rootView)) {
parentView.setClipChildren(false);
parentView = (ViewGroup) parentView.getParent();
}
rootView.setClipChildren(false);
imageViews[0].animate().setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
parentView = (ViewGroup) explodeLayout.getParent();
view.setLayoutParams(explodeLayout.getLayoutParams());
view.setVisibility(View.INVISIBLE);
parentView.removeView(explodeLayout);
parentView.addView(view, positionView);
if (getListener() != null) {
getListener().onAnimationEnd(ExplodeAnimation.this);
}
}
});
}
/**
* This is the line of interest below. It doesn't matter how you create your animation
* as long as those views which want to be animated are added to Quicksand as a set.
* <p/>
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvvvvvvvvvvvv
* vvvvvvvvvv
* vvvv
* vv
* v
*/
private void monitor(ImageView[] imageViews) {
Quicksand.trap(KEY_ANIMATION_SET, imageViews);
}
private int[] sideTranslation(int heightCount, int bmpWidth, int bmpHeight, int yParts) {
int[] translation = new int[2];
int middleYPart = (yParts - 1) / 2;
if (heightCount == 0) {
translation[0] = -bmpWidth;
translation[1] = -bmpHeight;
} else if (heightCount == yParts - 1) {
translation[0] = -bmpWidth;
translation[1] = bmpHeight;
}
if (yParts % 2 != 0) {
if (heightCount == middleYPart) {
translation[0] = -bmpWidth;
translation[1] = 0;
}
}
return translation;
}
/**
* @param matrix The matrix that determines the number of X and Y parts to set.
* @return This object, allowing calls to methods in this class to be
* chained.
*/
private ExplodeAnimation setExplodeMatrix(int matrix) {
xParts = matrix / 10;
yParts = matrix % 10;
return this;
}
private AnimationListener getListener() {
return listener;
}
/**
* @param listener The listener to set for the end of the animation.
* @return This object, allowing calls to methods in this class to be
* chained.
*/
public ExplodeAnimation setListener(AnimationListener listener) {
this.listener = listener;
return this;
}
}