arrowdownloadbutton.gif
arrowDownloadButton.startAnimating();
arrowDownloadButton.setProgress(50F);
public class ArrowDownloadButton extends View {
private static final int BLUE_ONE = Color.rgb(46, 164, 242);
private static final int WHILE = Color.rgb(255, 255, 255);
private static final float RADIUS = 180;
private static final int TRI_POINT_NUMBER = 17;
private static final float MAX_WAVE_HEIGHT = 10;
private static final float MIN_WAVE_HEIGHT = 5;
private static final int PROGRESS = 100;
private static final int ANGLE = 360;
private static final float TEXT_Y = 67.5f;
private static final float OFFSET = 10;
private static final float SMALL_RADIUS = 5;
private static final float TEXT_SIZE = 40;
private static final float ARC_WIDTH = 20;
private static final float ARROW_WIDTH = 10;
private static final float TRI_WIDTH = 10;
private static final float LOADING_WIDTH = 10;
private static final float STEP = 2;
private static final float ELASTICITY_STEP = 10;
private static final float ROPE_STEP_X = 30;
private static final float ROPE_STEP_Y = 32;
private static final float ROPE_HEAD_STEP_Y = 17;
private static final float JUMP_STEP = 45;
private static final float DOWN_STEP = 7.5f;
private static final float TRI_STEP = 16.875f;
private static final float TIME_STEP = 20;
private static final float HOOK_STEP_Y = 15;
private static final float HOOK_COUNT = 4;
private static final float LITTLE_STEP = 8;
private static final int DURATION = 20;
private static final int COMPLETE_DURATION = 20;
/**
* start instance
**/
private static final String INSTANCE_STATE = "instance_state";
/**
*
*/
private static final String X_I = "x";
private static final String Y_I = "y";
private static final String RADIUS_I = "radius";
private static final String MAX_WAVE_HEIGHT_I = "max_wave_height";
private static final String MIN_WAVE_HEIGHT_I = "min_wave_height";
private static final String TEXT_Y_I = "text_y";
private static final String STEP_I = "step";
private static final String ELASTICITY_STEP_I = "elasticity_step";
private static final String ROPE_STEP_X_I = "rope_step_x";
private static final String ROPE_STEP_Y_I = "rope_step_y";
private static final String ROPE_HEAD_STEP_Y_I = "rope_head_step_y";
private static final String JUMP_STEP_I = "jump_step";
private static final String DOWN_STEP_I = "down_step";
private static final String TRI_STEP_I = "tri_step";
private static final String HOOK_STEP_Y_I = "hook_step";
private static final String LITTLE_STEP_I = "little_step";
private static final String SMALL_RADIUS_I = "small_radius";
private static final String TEXT_SIZE_I = "text_size";
private static final String ARC_WIDTH_I = "arc_width";
private static final String ARROW_WIDTH_I = "arrow_width";
private static final String TRI_WIDTH_I = "tri_width";
private static final String LOADING_WIDTH_I = "loading_width";
private static final String IS_FIRST_I = "is_first";
private static final String IS_ANIMATING_I = "is_animating";
private static final String BEZIER_I = "bezier";
private static final String IS_LOADING_I = "is_loading";
private static final String IS_COMPLETED_I = "is_completed";
private static final String IS_END_I = "is_end";
private static final String COUNT_I = "count";
private static final String LENGTH_I = "length";
private static final String CURRENT_TIME_I = "current_time";
private static final String WAVE_HEIGHT_I = "wave_height";
private static final String PROGRESS_I = "progress";
private static final String HOOK_COUNT_I = "hook_count";
private static final String LENGTH_X_I = "length_x";
private static final String LENGTH_Y_I = "length_y";
private float x = 550;
private float y = 550;
private float radius = RADIUS;
private float maxWaveHeight = MAX_WAVE_HEIGHT;
private float minWaveHeight = MIN_WAVE_HEIGHT;
private float textY = TEXT_Y;
private float step = STEP;
private float elasticityStep = ELASTICITY_STEP;
private float ropeStepX = ROPE_STEP_X;
private float ropeStepY = ROPE_STEP_Y;
private float ropeHeadStepY = ROPE_HEAD_STEP_Y;
private float jumpStep = JUMP_STEP;
private float downStep = DOWN_STEP;
private float triStep = TRI_STEP;
private float hookStepY = HOOK_STEP_Y;
private float littleStep = LITTLE_STEP;
private float smallRadius = SMALL_RADIUS;
private float textSize = TEXT_SIZE;
private float arcWidth = ARC_WIDTH;
private float arrowWidth = ARROW_WIDTH;
private float triWidth = TRI_WIDTH;
private float loadingWidth = LOADING_WIDTH;
private Paint arrowPaint;
private Paint arcPaint;
private Paint smallPaint;
private Paint triPaint;
private Paint loadingPaint;
private Paint textPaint;
private Path arrowPath;
private Path triPath;
private Path textPath;
private RectF oval;
private Point a;
private Point b;
private Point c;
private Point d;
private Point e;
private Point jumpPoint;
private List<Point> triPoints = new ArrayList<>();
private boolean isFirst = true;
private boolean isAnimating = false;
private boolean bezier = false;
private boolean isLoading = false;
private boolean isCompleted = false;
private boolean isEnd = false;
private int count = 0;
private float length;
private int currentTime = 0;
private float waveHeight = MIN_WAVE_HEIGHT;
private float progress = 0;
private int hookCount = 0;
float lengthX = 3 * radius / 4;
float lengthY = 3 * radius / 4;
public float getProgress() {
return progress;
}
public void setProgress(float progress) {
if (progress > 100) {
this.progress = 100;
} else {
this.progress = progress;
}
if (progress == 100) {
isLoading = false;
isCompleted = true;
}
}
public ArrowDownloadButton(Context context) {
this(context, null);
}
public ArrowDownloadButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ArrowDownloadButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
}
private int measure(int measureSpec, boolean isWidth) {
int result;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
result += padding;
if (mode == MeasureSpec.AT_MOST) {
if (isWidth) {
result = Math.max(result, size);
} else {
result = Math.min(result, size);
}
}
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
if (isFirst) {
init();
isFirst = false;
}
canvas.drawCircle(x, y, radius, arcPaint);
drawArrow(canvas);
if (isAnimating) {
animating();
}
if (isLoading) {
loading(canvas);
}
if (isCompleted) {
afterCompleted(canvas);
}
}
private void init() {
float temp = getHeight() > getWidth() ? getWidth() / 2 : getHeight() / 2;
radius = temp - temp * OFFSET / RADIUS - temp * ELASTICITY_STEP / RADIUS - 6;
x = getPaddingLeft() + getWidth() / 2;
y = getPaddingTop() + getHeight() / 2;
maxWaveHeight = convert(MAX_WAVE_HEIGHT);
minWaveHeight = convert(MIN_WAVE_HEIGHT);
textY = convert(TEXT_Y);
step = convert(STEP);
elasticityStep = convert(ELASTICITY_STEP);
ropeStepX = convert(ROPE_STEP_X);
ropeStepY = convert(ROPE_STEP_Y);
ropeHeadStepY = convert(ROPE_HEAD_STEP_Y);
jumpStep = convert(JUMP_STEP);
downStep = convert(DOWN_STEP);
triStep = convert(TRI_STEP);
hookStepY = convert(HOOK_STEP_Y);
littleStep = convert(LITTLE_STEP);
smallRadius = convert(SMALL_RADIUS);
textSize = convert(TEXT_SIZE);
arcWidth = convert(ARC_WIDTH);
arrowWidth = convert(ARROW_WIDTH);
triWidth = convert(TRI_WIDTH);
loadingWidth = convert(LOADING_WIDTH);
lengthX = 3 * radius / 4;
lengthY = 3 * radius / 4;
arrowPath = new Path();
triPath = new Path();
textPath = new Path();
oval = new RectF();
oval.left = x - radius;
oval.top = y - radius;
oval.right = x + radius;
oval.bottom = y + radius;
length = radius / 2;
initializePaints();
initializePoints();
}
/**
* start animating before loading
*/
public void startAnimating() {
isAnimating = true;
invalidate();
}
/**
* reset to initial state
*/
public void reset() {
isAnimating = false;
isLoading = false;
bezier = false;
isCompleted = false;
isEnd = false;
length = radius / 2;
count = 0;
hookCount = 0;
jumpPoint.x = -1;
progress = 0;
lengthX = 3 * radius / 4;
lengthY = 3 * radius / 4;
a.y = y + length;
b.y = y - length;
e.y = y + length;
c.x = x - length / 2;
c.y = y + length / 2;
d.x = x + length / 2;
d.y = y + length / 2;
invalidate();
}
/**
* animating
*/
public void animating() {
if (count < 19) {
length = length * 3 / 4;
a.y = y + length;
b.y = y - length;
if (((count + 1) % 3) == 0 && count < 9) {
e.y = e.y + step;
c.y = c.y + step;
d.y = d.y + step;
}
if (count > 8 && count < 12) {
jumpPoint.x = x;
jumpPoint.y = y - jumpStep * (count - 8);
c.x = c.x - ropeStepX;
c.y = c.y - ropeHeadStepY;
d.x = d.x + ropeStepX;
d.y = d.y - ropeHeadStepY;
e.y = e.y - ropeStepY;
}
if (count > 11) {
bezier = true;
if (count == 12) {
jumpPoint.y = jumpPoint.y - jumpStep * 2;
} else {
jumpPoint.y = jumpPoint.y + downStep;
if (count < 16) {
int time1 = 15 - count;
e.y = y + time1 * elasticityStep;
}
}
}
count++;
postInvalidateDelayed(DURATION);
} else {
isAnimating = false;
bezier = false;
if (progress != 100) {
isLoading = true;
} else {
isLoading = false;
isCompleted = true;
}
}
}
/**
* under loading
*
* @param canvas Target Canvas
*/
private void loading(Canvas canvas) {
Point currentPoint = triPoints.get(0);
Point nextPoint;
for (int i = 0; i < TRI_POINT_NUMBER; i++) {
Point p = triPoints.get(i);
p.x = (x - 3 * radius / 4) + triStep * i;
p.y = y + calculateTri(TIME_STEP * i, currentTime);
}
for (int i = 1; i < TRI_POINT_NUMBER; i++) {
nextPoint = triPoints.get(i);
triPath.reset();
triPath.moveTo(currentPoint.x, currentPoint.y);
triPath.lineTo(nextPoint.x, nextPoint.y);
canvas.drawCircle(nextPoint.x, nextPoint.y, smallRadius, smallPaint);
canvas.drawPath(triPath, triPaint);
currentPoint = nextPoint;
}
textPath.moveTo(x - textSize, y + textY);
textPath.lineTo(x + textSize, y + textY);
canvas.drawTextOnPath((int) progress + "%", textPath, 0, 0, textPaint);
currentTime = (int) (currentTime + TIME_STEP);
float sweepAngle = (progress / PROGRESS * ANGLE);
canvas.drawArc(oval, 270, 0 - sweepAngle, false, loadingPaint);
postInvalidateDelayed(DURATION);
}
/**
* the method do such things:
* 1.draw arrow.
* 2.when animate was completed, let the small ball jump.
*
* @param canvas Target Canvas
*/
protected void drawArrow(Canvas canvas) {
if (jumpPoint.x != -1) {
canvas.drawCircle(jumpPoint.x, jumpPoint.y, smallRadius, smallPaint);
}
if (bezier) {
arrowPath.reset();
arrowPath.moveTo(c.x, c.y);
arrowPath.quadTo(e.x, e.y, d.x, d.y);
canvas.drawPath(arrowPath, arrowPaint);
} else if (isLoading) {
} else if (isCompleted) {
} else if (isEnd) {
canvas.drawCircle(x, y, radius, loadingPaint);
drawArrowOrHook(canvas);
} else {
arrowPath.reset();
arrowPath.moveTo(a.x, a.y);
arrowPath.lineTo(b.x, b.y);
canvas.drawPath(arrowPath, arrowPaint);
canvas.drawCircle(a.x, a.y, smallRadius, smallPaint);
canvas.drawCircle(b.x, b.y, smallRadius, smallPaint);
drawArrowOrHook(canvas);
}
}
/**
* draw arrow or hook
*
* @param canvas Target Canvas
*/
private void drawArrowOrHook(Canvas canvas) {
arrowPath.reset();
arrowPath.moveTo(e.x, e.y);
arrowPath.lineTo(c.x, c.y);
canvas.drawPath(arrowPath, arrowPaint);
arrowPath.reset();
arrowPath.moveTo(e.x, e.y);
arrowPath.lineTo(d.x, d.y);
canvas.drawPath(arrowPath, arrowPaint);
canvas.drawCircle(c.x, c.y, smallRadius, smallPaint);
canvas.drawCircle(d.x, d.y, smallRadius, smallPaint);
canvas.drawCircle(e.x, e.y, smallRadius, smallPaint);
}
/**
* the animate after loading
*
* @param canvas Target Canvas
*/
private void afterCompleted(Canvas canvas) {
canvas.drawCircle(x, y, radius, loadingPaint);
if (hookCount == HOOK_COUNT - 1) {
e.y = e.y + littleStep;
c.x = c.x - littleStep;
d.x = d.x + littleStep;
d.y = d.y - littleStep;
isCompleted = false;
isEnd = true;
} else {
e.x = x;
e.y = y + hookStepY * (hookCount + 1);
lengthX = lengthX * 3 / 4;
c.x = x - lengthX * 3 / 4;
c.y = y;
d.x = x + lengthY - radius / (float) 8 * (hookCount + 1);
d.y = y - hookStepY * (hookCount + 1);
hookCount++;
}
drawArrowOrHook(canvas);
postInvalidateDelayed(COMPLETE_DURATION);
}
protected void initializePaints() {
arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setStyle(Paint.Style.STROKE);
arcPaint.setStrokeWidth(arcWidth);
arcPaint.setColor(BLUE_ONE);
arrowPaint = new Paint();
arrowPaint.setAntiAlias(true);
arrowPaint.setStyle(Paint.Style.STROKE);
arrowPaint.setStrokeWidth(arrowWidth);
arrowPaint.setColor(WHILE);
smallPaint = new Paint();
smallPaint.setAntiAlias(true);
smallPaint.setStyle(Paint.Style.FILL);
smallPaint.setColor(WHILE);
triPaint = new Paint();
triPaint.setAntiAlias(true);
triPaint.setStyle(Paint.Style.STROKE);
triPaint.setStrokeWidth(triWidth);
triPaint.setColor(WHILE);
loadingPaint = new Paint();
loadingPaint.setAntiAlias(true);
loadingPaint.setStyle(Paint.Style.STROKE);
loadingPaint.setStrokeWidth(loadingWidth);
loadingPaint.setColor(WHILE);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setStrokeWidth(1);
textPaint.setColor(WHILE);
textPaint.setTextSize(textSize);
}
protected void initializePoints() {
a = new Point(x, y + radius / 2);
b = new Point(x, y - radius / 2);
c = new Point(x - radius / 4, y + radius / 4);
d = new Point(x + radius / 4, y + radius / 4);
e = new Point(x, y + radius / 2);
jumpPoint = new Point();
for (int i = 0; i < TRI_POINT_NUMBER; i++) {
Point point = new Point();
point.x = (x - 3 * radius / 4) + triStep * i;
point.y = y + calculateTri(TIME_STEP * i, 0);
triPoints.add(point);
}
}
/**
* calculate the wave
*
* @param originalTime original time
* @param currentTime current time
* @return wave
*/
private float calculateTri(float originalTime, float currentTime) {
if (progress < PROGRESS / 3) {
waveHeight = MIN_WAVE_HEIGHT;
} else if (progress < PROGRESS * 2 / 3) {
waveHeight = maxWaveHeight;
} else {
waveHeight = minWaveHeight;
}
return (float) (waveHeight * Math.sin((Math.PI / 80) * (originalTime + currentTime)));
}
private float convert(float original) {
return radius * original / RADIUS;
}
@Override
protected Parcelable onSaveInstanceState() {
final Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
bundle.putFloat(X_I, x);
bundle.putFloat(Y_I, y);
bundle.putFloat(RADIUS_I, radius);
bundle.putFloat(MAX_WAVE_HEIGHT_I, maxWaveHeight);
bundle.putFloat(MIN_WAVE_HEIGHT_I, minWaveHeight);
bundle.putFloat(TEXT_Y_I, textY);
bundle.putFloat(STEP_I, step);
bundle.putFloat(ELASTICITY_STEP_I, elasticityStep);
bundle.putFloat(ROPE_STEP_X_I, ropeStepX);
bundle.putFloat(ROPE_STEP_Y_I, ropeStepY);
bundle.putFloat(ROPE_HEAD_STEP_Y_I, ropeHeadStepY);
bundle.putFloat(JUMP_STEP_I, jumpStep);
bundle.putFloat(DOWN_STEP_I, downStep);
bundle.putFloat(TRI_STEP_I, triStep);
bundle.putFloat(HOOK_STEP_Y_I, hookStepY);
bundle.putFloat(LITTLE_STEP_I, littleStep);
bundle.putFloat(SMALL_RADIUS_I, smallRadius);
bundle.putFloat(TEXT_SIZE_I, textSize);
bundle.putFloat(ARC_WIDTH_I, arcWidth);
bundle.putFloat(ARROW_WIDTH_I, arrowWidth);
bundle.putFloat(TRI_WIDTH_I, triWidth);
bundle.putFloat(LOADING_WIDTH_I, loadingWidth);
bundle.putBoolean(IS_FIRST_I, isFirst);
bundle.putBoolean(IS_ANIMATING_I, isAnimating);
bundle.putBoolean(BEZIER_I, bezier);
bundle.putBoolean(IS_LOADING_I, isLoading);
bundle.putBoolean(IS_COMPLETED_I, isCompleted);
bundle.putBoolean(IS_END_I, isEnd);
bundle.putInt(COUNT_I, count);
bundle.putFloat(LENGTH_I, length);
bundle.putInt(CURRENT_TIME_I, currentTime);
bundle.putFloat(WAVE_HEIGHT_I, waveHeight);
bundle.putFloat(PROGRESS_I, progress);
bundle.putInt(HOOK_COUNT_I, hookCount);
bundle.putFloat(LENGTH_X_I, lengthX);
bundle.putFloat(LENGTH_Y_I, lengthY);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle bundle = (Bundle) state;
x = bundle.getFloat(X_I);
y = bundle.getFloat(Y_I);
radius = bundle.getFloat(RADIUS_I);
maxWaveHeight = bundle.getFloat(MAX_WAVE_HEIGHT_I);
minWaveHeight = bundle.getFloat(MIN_WAVE_HEIGHT_I);
textY = bundle.getFloat(TEXT_Y_I);
step = bundle.getFloat(STEP_I);
elasticityStep = bundle.getFloat(ELASTICITY_STEP_I);
ropeStepX = bundle.getFloat(ROPE_STEP_X_I);
ropeStepY = bundle.getFloat(ROPE_STEP_Y_I);
ropeHeadStepY = bundle.getFloat(ROPE_HEAD_STEP_Y_I);
jumpStep = bundle.getFloat(JUMP_STEP_I);
downStep = bundle.getFloat(DOWN_STEP_I);
triStep = bundle.getFloat(TRI_STEP_I);
hookStepY = bundle.getFloat(HOOK_STEP_Y_I);
littleStep = bundle.getFloat(LITTLE_STEP_I);
smallRadius = bundle.getFloat(SMALL_RADIUS_I);
textSize = bundle.getFloat(TEXT_SIZE_I);
arcWidth = bundle.getFloat(ARC_WIDTH_I);
arrowWidth = bundle.getFloat(ARROW_WIDTH_I);
triWidth = bundle.getFloat(TRI_WIDTH_I);
loadingWidth = bundle.getFloat(LOADING_WIDTH_I);
isFirst = bundle.getBoolean(IS_FIRST_I);
isAnimating = bundle.getBoolean(IS_ANIMATING_I);
bezier = bundle.getBoolean(BEZIER_I);
isLoading = bundle.getBoolean(IS_LOADING_I);
isCompleted = bundle.getBoolean(IS_COMPLETED_I);
isEnd = bundle.getBoolean(IS_END_I);
count = bundle.getInt(COUNT_I);
length = bundle.getFloat(LENGTH_I);
currentTime = bundle.getInt(CURRENT_TIME_I);
waveHeight = bundle.getFloat(WAVE_HEIGHT_I);
progress = bundle.getFloat(PROGRESS_I);
hookCount = bundle.getInt(HOOK_COUNT_I);
lengthX = bundle.getFloat(LENGTH_X_I);
lengthY = bundle.getFloat(LENGTH_Y_I);
}
super.onRestoreInstanceState(state);
}
static class Point {
public float x;
public float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public Point() {
x = -1;
y = -1;
}
}
}
网友评论