package com.cleveroad.androidmanimation; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.os.Build; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import java.util.Random; /** * Android M Loading animation view. */ public class LoadingAnimationView extends View { private static final int LAYERS_COUNT = 4; private final Layer[] layers = new Layer[LAYERS_COUNT]; private YellowRectangle yellowRectangle; private final RectF bounds = new RectF(); private ValueAnimator valueAnimator; private Random random; private int bgColor; public LoadingAnimationView(Context context) { this(context, null); } public LoadingAnimationView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public LoadingAnimationView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public LoadingAnimationView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs); } void fromBuilder(@NonNull AnimationDialogFragment.Builder builder) { initValues( builder.getFirstColor(getContext()), builder.getSecondColor(getContext()), builder.getThirdColor(getContext()), builder.getFourthColor(getContext()), builder.getBackgroundColor(), builder.getSpeedCoefficient() ); } private void init(Context context, AttributeSet attrs) { int googleBlue = ColorUtil.getColor(context, R.color.lav_google_blue); int googleYellow = ColorUtil.getColor(context, R.color.lav_google_yellow); int googleRed = ColorUtil.getColor(context, R.color.lav_google_red); int googleGreen = ColorUtil.getColor(context, R.color.lav_google_green); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LoadingAnimationView); int firstColor; int secondColor; int thirdColor; int fourthColor; int bgColor; float speedCoefficient; try { firstColor = typedArray.getColor(R.styleable.LoadingAnimationView_lav_firstColor, googleRed); secondColor = typedArray.getColor(R.styleable.LoadingAnimationView_lav_secondColor, googleGreen); thirdColor = typedArray.getColor(R.styleable.LoadingAnimationView_lav_thirdColor, googleBlue); fourthColor = typedArray.getColor(R.styleable.LoadingAnimationView_lav_fourthColor, googleYellow); bgColor = typedArray.getColor(R.styleable.LoadingAnimationView_lav_backgroundColor, Color.BLACK); speedCoefficient = typedArray.getFloat(R.styleable.LoadingAnimationView_lav_speedCoefficient, Constants.DEFAULT_SPEED_COEFFICIENT); } finally { typedArray.recycle(); } initValues(firstColor, secondColor, thirdColor, fourthColor, bgColor, speedCoefficient); } private void initValues(int red, int green, int blue, int yellow, int bgColor, float speedCoefficient) { Paint bluePaint = new Paint(); Paint yellowPaint = new Paint(); Paint redPaint = new Paint(); Paint greenPaint = new Paint(); redPaint.setColor(red); redPaint.setAntiAlias(true); greenPaint.setColor(green); greenPaint.setAntiAlias(true); bluePaint.setColor(blue); bluePaint.setAntiAlias(true); yellowPaint.setColor(yellow); yellowPaint.setAntiAlias(true); Paint bgPaint = new Paint(); bgPaint.setColor(bgColor); bgPaint.setAntiAlias(true); this.bgColor = bgColor; layers[0] = new FirstLayer(bluePaint, greenPaint, yellowPaint, bgPaint); layers[1] = new SecondLayer(redPaint, yellowPaint, bgPaint); layers[2] = new ThirdLayer(redPaint, greenPaint, bgPaint); layers[3] = new FourthLayer(redPaint, greenPaint, bluePaint, yellowPaint, bgPaint); yellowRectangle = new YellowRectangle(yellowPaint); Constants.SPEED_COEFFICIENT = speedCoefficient; valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setDuration((long) (Constants.TOTAL_DURATION * Constants.SPEED_COEFFICIENT)); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { updateAnimation(getWidth(), animation.getAnimatedFraction()); invalidate(); } }); } private void updateAnimation(float width, float dt) { float left = getPaddingLeft(); float right = getPaddingRight(); float maxWidth = 1f * (width - (left + right)) / LAYERS_COUNT; float spacing = maxWidth * 0.1f; float size = maxWidth - spacing; float halfSize = size / 2f; for (int i = 0; i< LAYERS_COUNT; i++) { float l = left + i * (size + spacing); float t = getHeight() / 2f - halfSize; float r = l + size; float b = t + size; bounds.set(l, t, r, b); layers[i].update(bounds, dt); if (i == 1) { yellowRectangle.setFirstValues(bounds.centerX(), bounds.centerY()); } else if (i == 2) { yellowRectangle.setSecondValues(bounds.centerX(), bounds.centerY()); } else if (i == 3) { yellowRectangle.setThirdValues(bounds.centerX(), bounds.centerY()); yellowRectangle.updateRadius(bounds.height()); } } yellowRectangle.update(bounds, dt); } @Override public void onDraw(Canvas canvas) { if (isInEditMode()) { if (random == null) { random = new Random(); } updateAnimation(canvas.getWidth(), random.nextFloat()); } canvas.drawColor(bgColor); for (int i = 0; i< LAYERS_COUNT; i++) { layers[i].draw(canvas); } yellowRectangle.draw(canvas); } /** * Start animation. */ public void startAnimation() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && valueAnimator.isPaused()) { valueAnimator.resume(); return; } if (valueAnimator.isRunning()) return; valueAnimator.start(); } /** * Pause animation. It behaves like {@link #stopAnimation()} on API < 19. */ public void pauseAnimation() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { stopAnimation(); } else if (!valueAnimator.isPaused()) { valueAnimator.pause(); } } /** * Stop animation. */ public void stopAnimation() { if (!valueAnimator.isRunning()) { return; } valueAnimator.cancel(); resetAll(); invalidate(); } private void resetAll() { for (Layer layer : layers) { layer.reset(); } yellowRectangle.reset(); } /** * Check current state of animation. * @return true if animation is running, false otherwise */ public boolean isRunning() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && valueAnimator.isPaused()) { return false; } return valueAnimator.isRunning(); } }