com.tr4android.support.extension.drawable.IndeterminateProgressDrawable.java Source code

Java tutorial

Introduction

Here is the source code for com.tr4android.support.extension.drawable.IndeterminateProgressDrawable.java

Source

/*
 * Copyright (C) 2015 Thomas Robert Altstidl
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tr4android.support.extension.drawable;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.v4.view.animation.PathInterpolatorCompat;
import android.view.animation.Interpolator;

import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.animation.AnimationUtils;
import com.tr4android.support.extension.animation.ValueAnimatorCompat;
import com.tr4android.support.extension.utils.ThemeUtils;

public class IndeterminateProgressDrawable extends Drawable implements Animatable {
    private static final String TAG = "Indeterminate Progress";

    // Interpolator used for path start
    private static final Interpolator PATH_START_INTERPOLATOR;
    static {
        Path mPathStart = new Path();
        mPathStart.lineTo(0.5f, 0);
        mPathStart.cubicTo(0.7f, 0, 0.6f, 1, 1, 1);
        PATH_START_INTERPOLATOR = PathInterpolatorCompat.create(mPathStart);
    }

    // Interpolator used for path end
    private static final Interpolator PATH_END_INTERPOLATOR;
    static {
        Path mPathEnd = new Path();
        mPathEnd.cubicTo(0.2f, 0, 0.1f, 1, 0.5f, 1);
        mPathEnd.lineTo(1, 1);
        PATH_END_INTERPOLATOR = PathInterpolatorCompat.create(mPathEnd);
    }

    // Paint
    private Paint mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private float mArcStrokeWidth = -1f; // auto
    private float mArcPadding = -1f; // auto
    private RectF mArcRect = new RectF();

    // Values that determine the current drawing state
    private int mRotationCount = 0;
    private float mRotation;
    private float mOffset;
    private float mStart;
    private float mEnd;

    // Animator
    private ValueAnimatorCompat mAnimator;

    public IndeterminateProgressDrawable(Context context, @ColorInt int color, float stroke, float padding) {
        mArcStrokeWidth = stroke;
        mArcPadding = padding;

        // The paint used to draw the spinning wheel
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
        mArcPaint.setStrokeJoin(Paint.Join.MITER);
        mArcPaint.setColor(color);

        // The animator used to animate the spinning wheel (works back to API 7)
        mAnimator = AnimationUtils.createAnimator();
        mAnimator.setFloatValues(0f, 0.25f);
        mAnimator.setDuration(1333);
        mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
        mAnimator.setRepeatCount(ValueAnimatorCompat.INFINITE);
        mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimatorCompat animator) {
                mOffset = animator.getAnimatedFloatValue();
                float fraction = animator.getAnimatedFraction();
                mStart = PATH_START_INTERPOLATOR.getInterpolation(fraction) * 0.75f;
                mEnd = PATH_END_INTERPOLATOR.getInterpolation(fraction) * 0.75f;
                mRotation = (mRotationCount * 144) + mOffset * 576;
                invalidateSelf();
            }
        });
        mAnimator.setListener(new ValueAnimatorCompat.AnimatorListener() {
            @Override
            public void onAnimationStart(ValueAnimatorCompat animator) {
            }

            @Override
            public void onAnimationEnd(ValueAnimatorCompat animator) {
            }

            @Override
            public void onAnimationCancel(ValueAnimatorCompat animator) {
            }

            @Override
            public void onAnimationRepeat(ValueAnimatorCompat animator) {
                // Update rotation count (rotation does 1 cycle for every 5 animator cycles)
                mRotationCount = (mRotationCount + 1) % 5;
            }
        });
    }

    @Override
    public void draw(Canvas canvas) {
        int saveCount = canvas.save();
        canvas.rotate(mRotation, mArcRect.centerX(), mArcRect.centerY());

        float startAngle = -90 + 360 * (mOffset + mStart);
        float sweepAngle = 360 * (mEnd - mStart);
        canvas.drawArc(mArcRect, startAngle, sweepAngle, false, mArcPaint);

        canvas.restoreToCount(saveCount);
    }

    @Override
    public void setBounds(Rect bounds) {
        calculateArcMetrics(bounds);
        super.setBounds(bounds);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        calculateArcMetrics(new Rect(left, top, right, bottom));
        super.setBounds(left, top, right, bottom);
    }

    @Override
    public void setAlpha(int i) {
        mArcPaint.setAlpha(i);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mArcPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void start() {
        mAnimator.start();
    }

    @Override
    public void stop() {
        mAnimator.cancel();
    }

    @Override
    public boolean isRunning() {
        return mAnimator.isRunning();
    }

    /**
     * Helper that calculates the bounds and the stroke width of the progress arc
     *
     * @param bounds the bounds of the drawable
     */
    private void calculateArcMetrics(Rect bounds) {
        float size = Math.min(bounds.height(), bounds.width());
        float yOffset = (bounds.height() - size) / 2f;
        float xOffset = (bounds.width() - size) / 2f;

        float strokeWidth;
        float padding;
        if (mArcStrokeWidth == -1f && mArcPadding == -1f) {
            // auto calculate bounds
            strokeWidth = 4f / 48f * size;
            padding = 5f / 48f * size;
        } else if (mArcStrokeWidth == -1f) {
            // auto calculate stroke width
            strokeWidth = 4f / 48f * size;
            padding = mArcPadding + strokeWidth / 2;
        } else if (mArcPadding == -1f) {
            // auto calculate padding
            strokeWidth = mArcStrokeWidth;
            padding = 3f / 48f * size + strokeWidth / 2;
        } else {
            strokeWidth = mArcStrokeWidth;
            padding = mArcPadding + strokeWidth / 2;
        }
        mArcPaint.setStrokeWidth(strokeWidth);
        mArcRect.set(bounds.left + padding + xOffset, bounds.top + padding + yOffset,
                bounds.right - padding - xOffset, bounds.bottom - padding - yOffset);
    }

    public static class Builder {
        private Context mContext;
        private int mColor;
        private float mPadding;
        private float mStrokeWidth;

        public Builder(Context context) {
            mContext = context;
            // Default values
            mColor = ThemeUtils.getThemeAttrColor(mContext, R.attr.colorAccent);
            mPadding = -1f; // auto calculates
            mStrokeWidth = -1f; //auto calculates
        }

        public Builder setColor(@ColorInt int color) {
            mColor = color;
            return this;
        }

        public Builder setPadding(float padding) {
            mPadding = padding;
            return this;
        }

        public Builder setStrokeWidth(float strokeWidth) {
            mStrokeWidth = strokeWidth;
            return this;
        }

        public IndeterminateProgressDrawable build() {
            return new IndeterminateProgressDrawable(mContext, mColor, mStrokeWidth, mPadding);
        }
    }
}