Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ba11dcd72cce27b22087a872ef2de5cebb88cdf5 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
| @@ -0,0 +1,256 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.chrome.browser.widget; |
| + |
| +import android.animation.ValueAnimator; |
| +import android.animation.ValueAnimator.AnimatorUpdateListener; |
| +import android.content.Context; |
| +import android.content.res.Resources; |
| +import android.content.res.TypedArray; |
| +import android.graphics.Canvas; |
| +import android.graphics.Paint; |
| +import android.support.v4.view.ViewCompat; |
| +import android.util.AttributeSet; |
| +import android.view.View; |
| + |
| +import org.chromium.base.ApiCompatibilityUtils; |
| +import org.chromium.chrome.R; |
| + |
| +/** |
| + * Material-styled horizontal progress bar |
| + * |
| + * This class is to be used in place of the support library's progress bar, which does not support |
| + * indeterminate progress bar styling on JB or KK. |
| + * |
| + * ------------------------------------------------------------------------------------------------- |
| + * DESIGN SPEC |
| + * |
| + * https://material.io/guidelines/components/progress-activity.html |
| + * |
| + * Secondary progress is represented by a second bar that is drawn on top of the primary progress, |
| + * and is completely optional. |
| + * |
| + * ------------------------------------------------------------------------------------------------- |
| + * DEFINING THE CONTROL STYLING IN AN XML LAYOUT |
| + * |
| + * Add the "chrome" namespace as an attribute to the main tag of the layout file: |
| + * xmlns:chrome="http://schemas.android.com/apk/res-auto |
| + * |
| + * These attributes control styling of the bar: |
| + * chrome:colorBackground Background color of a determinate progress bar. |
| + * chrome:colorProgress Represents progress along a determinate progress bar. |
| + * Also used as the background color for indeterminate progress. |
| + * chrome:colorSecondaryProgress Represents secondary progress on top of the regular progress. |
| + * chrome:colorThrobber Color of the pulses fired along the indeterminate progress bar. |
| + */ |
| +public class MaterialProgressBar extends View implements AnimatorUpdateListener { |
| + private static final long INDETERMINATE_ANIMATION_DURATION_MS = 2500; |
| + |
| + private final ValueAnimator mIndeterminateAnimator = ValueAnimator.ofFloat(0.0f, 3.0f); |
| + private final Paint mBackgroundPaint = new Paint(); |
| + private final Paint mProgressPaint = new Paint(); |
| + private final Paint mSecondaryProgressPaint = new Paint(); |
| + private final Paint mThrobberPaint = new Paint(); |
| + |
| + private boolean mIsIndeterminate; |
| + private int mProgress; |
| + private int mSecondaryProgress; |
| + |
| + public MaterialProgressBar(Context context) { |
| + super(context); |
| + initialize(context, null, 0); |
| + } |
| + |
| + public MaterialProgressBar(Context context, AttributeSet attrs) { |
| + super(context, attrs); |
| + initialize(context, attrs, 0); |
| + } |
| + |
| + public MaterialProgressBar(Context context, AttributeSet attrs, int defStyle) { |
| + super(context, attrs, defStyle); |
| + initialize(context, attrs, defStyle); |
| + } |
| + |
| + /** Sets the background color, corresponding to "chrome:colorBackground". */ |
| + @Override |
| + public void setBackgroundColor(int color) { |
| + mBackgroundPaint.setColor(color); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** Sets the progress color, corresponding to "chrome:colorProgress". */ |
| + public void setProgressColor(int color) { |
| + mProgressPaint.setColor(color); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** Sets the secondary color, corresponding to "chrome:colorSecondaryProgress". */ |
| + public void setSecondaryProgressColor(int color) { |
| + mSecondaryProgressPaint.setColor(color); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** Sets the indeterminate throbber color, corresponding to "chrome:colorThrobber". */ |
| + public void setThrobberColor(int color) { |
| + mThrobberPaint.setColor(color); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** |
| + * Sets the progress value being displayed. |
| + * @param progress Progress value, ranging from 0 to 100. Will be clamped into the range. |
| + */ |
| + public void setProgress(int progress) { |
| + mProgress = Math.max(0, Math.min(100, progress)); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** |
| + * Sets the secondary progress value being displayed. |
| + * @param progress Progress value, ranging from 0 to 100. Will be clamped into the range. |
| + */ |
| + public void setSecondaryProgress(int progress) { |
| + mSecondaryProgress = Math.max(0, Math.min(100, progress)); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + /** Sets whether the progress bar is indeterminate or not. */ |
| + public void setIndeterminate(boolean indeterminate) { |
| + if (mIsIndeterminate == indeterminate) return; |
| + mIsIndeterminate = indeterminate; |
| + |
| + if (mIsIndeterminate) startIndeterminateAnimation(); |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + @Override |
| + public void setVisibility(int visibility) { |
| + super.setVisibility(visibility); |
| + if (visibility == View.VISIBLE) { |
| + startIndeterminateAnimation(); |
| + } else { |
| + stopIndeterminateAnimation(); |
| + } |
| + } |
| + |
| + @Override |
| + public void onAnimationUpdate(ValueAnimator animation) { |
| + postInvalidateOnAnimation(); |
| + } |
| + |
| + @Override |
| + public void onDraw(Canvas canvas) { |
| + if (mIsIndeterminate) { |
| + drawIndeterminateBar(canvas); |
| + } else { |
| + drawDeterminateBar(canvas); |
|
Ian Wen
2016/12/20 23:17:58
Not sure if we need to support determinate bar in
gone
2016/12/21 00:54:23
We could, but I'm going for an all encompassing so
|
| + } |
| + } |
| + |
| + @Override |
| + public void onAttachedToWindow() { |
| + super.onAttachedToWindow(); |
| + startIndeterminateAnimation(); |
| + } |
| + |
| + @Override |
| + public void onDetachedFromWindow() { |
| + super.onDetachedFromWindow(); |
| + stopIndeterminateAnimation(); |
| + } |
| + |
| + private void initialize(Context context, AttributeSet attrs, int defStyle) { |
| + Resources resources = context.getResources(); |
| + int backgroundColor = |
| + ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_background); |
| + int progressColor = |
| + ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_foreground); |
| + int secondaryProgressColor = |
| + ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_secondary); |
| + int throbberColor = |
| + ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_default_throbber); |
| + |
| + if (attrs != null) { |
| + TypedArray a = context.obtainStyledAttributes( |
| + attrs, R.styleable.MaterialProgressBar, defStyle, 0); |
| + backgroundColor = |
| + a.getColor(R.styleable.MaterialProgressBar_colorBackground, backgroundColor); |
| + progressColor = |
| + a.getColor(R.styleable.MaterialProgressBar_colorProgress, progressColor); |
| + secondaryProgressColor = a.getColor( |
| + R.styleable.MaterialProgressBar_colorSecondaryProgress, secondaryProgressColor); |
| + throbberColor = |
| + a.getColor(R.styleable.MaterialProgressBar_colorThrobber, throbberColor); |
| + a.recycle(); |
| + } |
| + |
| + setBackgroundColor(backgroundColor); |
| + setProgressColor(progressColor); |
| + setSecondaryProgressColor(secondaryProgressColor); |
| + setThrobberColor(throbberColor); |
| + |
| + mIndeterminateAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| + mIndeterminateAnimator.setDuration(INDETERMINATE_ANIMATION_DURATION_MS); |
| + mIndeterminateAnimator.addUpdateListener(this); |
| + } |
| + |
| + private void startIndeterminateAnimation() { |
| + if (!mIsIndeterminate || mIndeterminateAnimator.isRunning()) return; |
| + if (!ViewCompat.isAttachedToWindow(this) || getVisibility() != View.VISIBLE) return; |
| + mIndeterminateAnimator.start(); |
| + } |
| + |
| + private void stopIndeterminateAnimation() { |
| + if (!mIndeterminateAnimator.isRunning()) return; |
| + mIndeterminateAnimator.cancel(); |
| + } |
| + |
| + private void drawIndeterminateBar(Canvas canvas) { |
| + int width = canvas.getWidth(); |
| + drawRect(canvas, mProgressPaint, 0, width); |
| + |
| + // The first pulse fires off at the beginning of the animation. |
| + float value = (Float) mIndeterminateAnimator.getAnimatedValue(); |
| + float left = width * (float) (Math.pow(value, 1.5f) - 0.5f); |
| + float right = width * value; |
| + drawRect(canvas, mThrobberPaint, left, right); |
| + |
| + // The second pulse fires off at some point after the first pulse has been fired. |
| + final float secondPulseStart = 1.1f; |
| + final float secondPulseLength = 1.0f; |
| + if (value >= secondPulseStart) { |
| + float percentage = (value - secondPulseStart) / secondPulseLength; |
| + left = width * (float) (Math.pow(percentage, 3.0f) - 0.1f); |
| + right = width * percentage; |
| + drawRect(canvas, mThrobberPaint, left, right); |
| + } |
| + } |
| + |
| + private void drawDeterminateBar(Canvas canvas) { |
| + int width = canvas.getWidth(); |
| + drawRect(canvas, mBackgroundPaint, 0, width); |
| + |
| + if (mProgress > 0) { |
| + float percentage = mProgress / 100.0f; |
| + drawRect(canvas, mProgressPaint, 0, width * percentage); |
| + } |
| + |
| + if (mSecondaryProgress > 0) { |
| + float percentage = mSecondaryProgress / 100.0f; |
| + drawRect(canvas, mSecondaryProgressPaint, 0, width * percentage); |
| + } |
| + } |
| + |
| + private void drawRect(Canvas canvas, Paint paint, float start, float end) { |
| + if (ViewCompat.getLayoutDirection(this) == View.LAYOUT_DIRECTION_RTL) { |
| + int width = canvas.getWidth(); |
| + float rtlStart = width - end; |
| + float rtlEnd = width - start; |
| + canvas.drawRect(rtlStart, 0, rtlEnd, canvas.getHeight(), paint); |
| + } else { |
| + canvas.drawRect(start, 0, end, canvas.getHeight(), paint); |
| + } |
| + } |
| +} |