fr.francetv.zoom.share.loader.ZoomLoaderView.java Source code

Java tutorial

Introduction

Here is the source code for fr.francetv.zoom.share.loader.ZoomLoaderView.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 France Tlvisions
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package fr.francetv.zoom.share.loader;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;

public class ZoomLoaderView extends View {

    private static final String LOG_TAG = ZoomLoaderView.class.getSimpleName();

    private static final long ANIMATION_IN_DURATION_MS = 1800L;
    private static final long ANIMATION_PAUSE_MS = 250L;
    private static final long ANIMATION_OUT_DURATION_MS = 1800L;

    private static final int VIEW_WIDTH_DP = 63;
    private static final int VIEW_HEIGHT_DP = 36;
    private static final int LEFT_START_DEGRE = -45;
    private static final int RIGHT_START_DEGRE = 180 + LEFT_START_DEGRE;

    private DelayHandler mDelayHandler;

    private int mViewWidthPx = 0;
    private int mViewHeightPx = 0;

    private int mRadiusPx = 0;

    private Bitmap mMaskBitmap;
    private Paint mMaskPaint;
    private Paint mBackgroundPaint;

    private Paint mAnimPaint;
    private RectF mLeftCircleArc;
    private RectF mRightCircleArc;

    private ValueAnimator mAnimationIn;
    private ValueAnimator mAnimationOut;

    private boolean mIsRunning;

    public ZoomLoaderView(Context context) {
        super(context);
        init();
    }

    public ZoomLoaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ZoomLoaderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void start() {
        Log.v(LOG_TAG, "start: ");
        if (mIsRunning) {
            return;
        }
        stopRunningAnimation();
        mIsRunning = true;
        mAnimationIn.start();
    }

    public void stop() {
        Log.v(LOG_TAG, "stop: ");
        mIsRunning = false;
        stopRunningAnimation();
    }

    public void setThemeColor(final int color) {
        mAnimPaint.setColor(color);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int sc = canvas.saveLayer(0, 0, mViewWidthPx, mViewHeightPx, null,
                Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
                        | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);

        int mAnimationInValue = (int) mAnimationIn.getAnimatedValue();
        int mAnimationOutValue = mAnimationOut.isRunning() ? (int) mAnimationOut.getAnimatedValue() : 0;

        canvas.drawPaint(mBackgroundPaint);
        canvas.drawArc(mLeftCircleArc, LEFT_START_DEGRE - mAnimationOutValue,
                -mAnimationInValue + mAnimationOutValue, true, mAnimPaint);
        canvas.drawArc(mRightCircleArc, RIGHT_START_DEGRE - mAnimationOutValue,
                -mAnimationInValue + mAnimationOutValue, true, mAnimPaint);
        canvas.drawBitmap(mMaskBitmap, 0, 0, mMaskPaint);
        canvas.restoreToCount(sc);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(MeasureSpec.makeMeasureSpec(mViewWidthPx, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(mViewHeightPx, MeasureSpec.EXACTLY));
        if (mRadiusPx == 0) {
            mRadiusPx = MeasureSpec.getSize(heightMeasureSpec);
        }
    }

    private void init() {
        mIsRunning = false;

        mViewWidthPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEW_WIDTH_DP,
                getResources().getDisplayMetrics());
        mViewHeightPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEW_HEIGHT_DP,
                getResources().getDisplayMetrics());

        mRadiusPx = mViewHeightPx / 2;

        mMaskBitmap = Bitmap.createBitmap(mViewWidthPx, mViewHeightPx, Bitmap.Config.ARGB_8888);
        new Canvas(mMaskBitmap).drawBitmap(
                BitmapFactory.decodeResource(getContext().getResources(), R.drawable.img_loader_mask), 0, 0, null);

        mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mMaskPaint.setFilterBitmap(false);
        mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundPaint.setFilterBitmap(false);
        mBackgroundPaint.setXfermode(null);
        mBackgroundPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));

        // Circles
        mAnimPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mAnimPaint.setXfermode(null);
        mAnimPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.holo_green_light));

        mLeftCircleArc = new RectF(0, 0, 2 * mRadiusPx, 2 * mRadiusPx);
        mRightCircleArc = new RectF(mViewWidthPx - 2 * mRadiusPx, 0, mViewWidthPx, 2 * mRadiusPx);

        //Anims
        mAnimationIn = ValueAnimator.ofInt(0, 361 + LEFT_START_DEGRE);
        mAnimationIn.setInterpolator(new FastOutSlowInInterpolator());
        mAnimationIn.addUpdateListener(mAnimationInUpdateListener);
        mAnimationIn.addListener(mAnimationInListener);
        mAnimationIn.setDuration(ANIMATION_IN_DURATION_MS);

        mAnimationOut = ValueAnimator.ofInt(0, 361 + LEFT_START_DEGRE);
        mAnimationOut.setInterpolator(new FastOutSlowInInterpolator());
        mAnimationOut.addUpdateListener(mAnimationOutUpdateListener);
        mAnimationOut.addListener(mAnimationOutListener);
        mAnimationOut.setDuration(ANIMATION_OUT_DURATION_MS);

        mDelayHandler = new DelayHandler(mAnimationOut);
    }

    private void stopRunningAnimation() {
        mDelayHandler.removeCallbacks(null);
        if (mAnimationIn.isRunning()) {
            mAnimationIn.cancel();
        }
        if (mAnimationOut.isRunning()) {
            mAnimationOut.cancel();
        }
    }

    private final ValueAnimator.AnimatorUpdateListener mAnimationInUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            invalidate();
        }
    };

    private final Animator.AnimatorListener mAnimationInListener = new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            if (mIsRunning) {
                mDelayHandler.delay(ANIMATION_PAUSE_MS);
            }
        }

        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationCancel(Animator animation) {
        }

        @Override
        public void onAnimationRepeat(Animator animation) {
        }
    };

    private final ValueAnimator.AnimatorUpdateListener mAnimationOutUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            invalidate();
        }
    };

    private final Animator.AnimatorListener mAnimationOutListener = new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            if (mIsRunning) {
                mAnimationIn.start();
            }
        }

        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationCancel(Animator animation) {
        }

        @Override
        public void onAnimationRepeat(Animator animation) {
        }
    };

    private static class DelayHandler extends Handler {

        private static final int MESSAGE_DELAY = 0;

        private final ValueAnimator mAnimationOut;

        public DelayHandler(final ValueAnimator animationOut) {
            super(Looper.myLooper());
            mAnimationOut = animationOut;
        }

        protected void delay(long delay) {
            removeMessages(MESSAGE_DELAY);
            sendMessageDelayed(obtainMessage(MESSAGE_DELAY), delay);
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_DELAY) {
                mAnimationOut.start();
            }
        }

    }

}