package com.marshalchen.common.uimodule.activityanimation;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
public class AnimatedRectLayout extends ViewGroup {
private static final String TAG = "AnimatedRectLayout";
public static final int ANIMATION_RANDOM = 1;
public static final int ANIMATION_WAVE_TL = 2;
public static final int ANIMATION_WAVE_TR = 4;
public static final int ANIMATION_WAVE_BR = 8;
public static final int ANIMATION_WAVE_BL = 16;
public static final int RECT_COUNT_IN_WIDTH = 10;
static final boolean IS_JBMR2 = Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2;
private static SparseArray<IndexesBuilder> sIndexesBuilder = new SparseArray<IndexesBuilder>();
private int mAnimationType;
private int mRectCountInHeight;
private int mRectCountInWidth;
private Rect[][] mRects;
private int[][] mRectIndexes;
private float mProgress;
private Bitmap mFullBitmap;
public AnimatedRectLayout(Context context) {
super(context);
}
public AnimatedRectLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AnimatedRectLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setAnimationType(int animationType) {
mAnimationType = animationType;
}
public float getProgress() {
return mProgress;
}
public void setProgress(float progress) {
mProgress = progress;
invalidate();
}
@Override
protected boolean addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout) {
throwCustomException(getChildCount());
boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
return returnValue;
}
@Override
public void addView(View child, int index, LayoutParams params) {
throwCustomException(getChildCount());
super.addView(child, index, params);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
View child = getChildAt(0);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View child = getChildAt(0);
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
updateGrid();
}
private void throwCustomException (int numOfChildViews) {
if (numOfChildViews == 1) {
throw new IllegalArgumentException("only one child please");
}
}
private void updateGrid() {
prepareGrid();
invalidate();
}
private void prepareGrid() {
if(isInEditMode()) {
return;
}
int w = getMeasuredWidth();
int h = getMeasuredHeight();
int rectWidth = w/ RECT_COUNT_IN_WIDTH;
int rectHeight = rectWidth;
mRectCountInWidth = w/rectWidth;
mRectCountInHeight = h/rectHeight;
int delta = w%rectWidth;
if(delta>0) {
rectWidth += delta/mRectCountInWidth;
}
delta = h%rectWidth;
if(delta>0) {
rectHeight += delta/mRectCountInHeight;
}
mRects = new Rect[mRectCountInWidth][mRectCountInHeight];
mRectIndexes = new int[mRectCountInWidth * mRectCountInHeight][2];
for (int x = 0; x < mRectCountInWidth; x++) {
for (int y = 0; y < mRectCountInHeight; y++) {
int left = x * rectWidth;
int top = y * rectHeight;
int right = left + rectWidth;
int bottom = top + rectHeight;
if (x + 1 >= mRectCountInWidth) {
right = w;
}
if (y + 1 >= mRectCountInHeight) {
bottom = h;
}
mRects[x][y] = new Rect(left, top, right, bottom);
}
}
sIndexesBuilder.get(mAnimationType).build(mRectIndexes, mRectCountInWidth, mRectCountInHeight);
if (IS_JBMR2) {
mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mFullBitmap);
getChildAt(0).draw(canvas);
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
if(isInEditMode() || mProgress >= 1f) {
super.dispatchDraw(canvas);
return;
}
int threshold = (int) (mRectCountInWidth * mRectCountInHeight * mProgress);
for(int i=0 ; i<mRectIndexes.length ; i++) {
if(i >= threshold) {
return;
}
canvas.save();
int[] index = mRectIndexes[i];
Rect rect = mRects[index[0]][index[1]];
if(IS_JBMR2) {
canvas.drawBitmap(mFullBitmap, rect, rect, null);
} else {
canvas.clipRect(rect);
super.dispatchDraw(canvas);
}
canvas.restore();
}
}
private interface IndexesBuilder {
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight);
}
private static final IndexesBuilder sRandomIndexesBuilder = new IndexesBuilder() {
@Override
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight) {
int index = 0;
for (int x = 0; x < rectCountInWidth; x++) {
for (int y = 0; y < rectCountInHeight; y++) {
indexes[index][0] = x;
indexes[index][1] = y;
index++;
}
}
return shuffle(indexes);
}
};
private static final IndexesBuilder sBottomLeftWaveIndexesBuilder = new IndexesBuilder() {
@Override
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight) {
int index = 0;
for (int k = 0; k < rectCountInHeight + rectCountInWidth; ++k) {
int z = k < rectCountInHeight ? 0 : k - rectCountInHeight + 1;
for (int i = z; i <= k - z; ++i) {
int j = (rectCountInHeight -1)-(k-i);
if(i < rectCountInWidth && j < rectCountInHeight) {
indexes[index][0] = i;
indexes[index][1] = j;
index++;
}
}
}
return indexes;
}
};
private static final IndexesBuilder sTopRightWaveIndexesBuilder = new IndexesBuilder() {
@Override
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight) {
return reverse(sBottomLeftWaveIndexesBuilder.build(indexes, rectCountInWidth, rectCountInHeight));
}
};
private static final IndexesBuilder sTopLeftWaveIndexesBuilder = new IndexesBuilder() {
@Override
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight) {
int index = 0;
for(int k = 0, max = rectCountInHeight + rectCountInWidth; k < max ; k++) {
for(int j = 0 ; j <= k ; j++ ) {
int i = k - j;
if(i < rectCountInWidth && j < rectCountInHeight) {
indexes[index][0] = i;
indexes[index][1] = j;
index++;
}
}
}
return indexes;
}
};
private static final IndexesBuilder sBottomRightWaveIndexesBuilder = new IndexesBuilder() {
@Override
public int[][] build(int[][] indexes, int rectCountInWidth, int rectCountInHeight) {
return reverse(sTopLeftWaveIndexesBuilder.build(indexes, rectCountInWidth, rectCountInHeight));
}
};
static {
sIndexesBuilder.put(ANIMATION_RANDOM, sRandomIndexesBuilder);
sIndexesBuilder.put(ANIMATION_WAVE_TL, sTopLeftWaveIndexesBuilder);
sIndexesBuilder.put(ANIMATION_WAVE_TR, sTopRightWaveIndexesBuilder);
sIndexesBuilder.put(ANIMATION_WAVE_BR, sBottomRightWaveIndexesBuilder);
sIndexesBuilder.put(ANIMATION_WAVE_BL, sBottomLeftWaveIndexesBuilder);
}
private static int[][] shuffle(int[][] array) {
int n = array.length;
for (int i = 0; i < n; i++) {
int r = i + (int) (Math.random() * (n-i));
int[] tmp = array[i];
array[i] = array[r];
array[r] = tmp;
}
return array;
}
private static int[][] reverse(int[][] array) {
int i = 0;
int j = array.length - 1;
int[] tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array;
}
}