/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * 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 android.widget;

import android.animation.ObjectAnimator;
import android.annotation.InterpolatorRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.MathUtils;
import android.util.Pools.SynchronizedPool;
import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;

import com.android.internal.R;

import java.util.ArrayList;

A user interface element that indicates the progress of an operation. Progress bar supports two modes to represent progress: determinate, and indeterminate. For a visual overview of the difference between determinate and indeterminate progress modes, see Progress & activity. Display progress bars to a user in a non-interruptive way. Show the progress bar in your app's user interface or in a notification instead of within a dialog.

Indeterminate Progress

Use indeterminate mode for the progress bar when you do not know how long an operation will take. Indeterminate mode is the default for progress bar and shows a cyclic animation without a specific amount of progress indicated. The following example shows an indeterminate progress bar:

<ProgressBar
     android:id="@+id/indeterminateBar"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     />

Determinate Progress

Use determinate mode for the progress bar when you want to show that a specific quantity of progress has occurred. For example, the percent remaining of a file being retrieved, the amount records in a batch written to database, or the percent remaining of an audio file that is playing.

To indicate determinate progress, you set the style of the progress bar to Widget_ProgressBar_Horizontal.Widget_ProgressBar_Horizontal and set the amount of progress. The following example shows a determinate progress bar that is 25% complete:

<ProgressBar
     android:id="@+id/determinateBar"
     style="@android:style/Widget.ProgressBar.Horizontal"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:progress="25"/>
You can update the percentage of progress displayed by using the setProgress(int) method, or by calling incrementProgressBy(int) to increase the current progress completed by a specified amount. By default, the progress bar is full when the progress value reaches 100. You can adjust this default by setting the android:max attribute.

Other progress bar styles provided by the system include:

  • Widget.ProgressBar.Horizontal
  • Widget.ProgressBar.Small
  • Widget.ProgressBar.Large
  • Widget.ProgressBar.Inverse
  • Widget.ProgressBar.Small.Inverse
  • Widget.ProgressBar.Large.Inverse

The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary if your application uses a light colored theme (a white background).

XML attributes

See ProgressBar Attributes, View Attributes

@attrref android.R.styleable#ProgressBar_animationResolution
@attrref android.R.styleable#ProgressBar_indeterminate
@attrref android.R.styleable#ProgressBar_indeterminateBehavior
@attrref android.R.styleable#ProgressBar_indeterminateDrawable
@attrref android.R.styleable#ProgressBar_indeterminateDuration
@attrref android.R.styleable#ProgressBar_indeterminateOnly
@attrref android.R.styleable#ProgressBar_interpolator
@attrref android.R.styleable#ProgressBar_min
@attrref android.R.styleable#ProgressBar_max
@attrref android.R.styleable#ProgressBar_maxHeight
@attrref android.R.styleable#ProgressBar_maxWidth
@attrref android.R.styleable#ProgressBar_minHeight
@attrref android.R.styleable#ProgressBar_minWidth
@attrref android.R.styleable#ProgressBar_mirrorForRtl
@attrref android.R.styleable#ProgressBar_progress
@attrref android.R.styleable#ProgressBar_progressDrawable
@attrref android.R.styleable#ProgressBar_secondaryProgress
/** * <p> * A user interface element that indicates the progress of an operation. * Progress bar supports two modes to represent progress: determinate, and indeterminate. For * a visual overview of the difference between determinate and indeterminate progress modes, see * <a href="https://material.io/guidelines/components/progress-activity.html#progress-activity-types-of-indicators"> * Progress & activity</a>. * Display progress bars to a user in a non-interruptive way. * Show the progress bar in your app's user interface or in a notification * instead of within a dialog. * </p> * <h3>Indeterminate Progress</h3> * <p> * Use indeterminate mode for the progress bar when you do not know how long an * operation will take. * Indeterminate mode is the default for progress bar and shows a cyclic animation without a * specific amount of progress indicated. * The following example shows an indeterminate progress bar: * <pre> * &lt;ProgressBar * android:id="@+id/indeterminateBar" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * /&gt; * </pre> * </p> * <h3>Determinate Progress</h3> * <p> * Use determinate mode for the progress bar when you want to show that a specific quantity of * progress has occurred. * For example, the percent remaining of a file being retrieved, the amount records in * a batch written to database, or the percent remaining of an audio file that is playing. * <p> * <p> * To indicate determinate progress, you set the style of the progress bar to * {@link android.R.style#Widget_ProgressBar_Horizontal} and set the amount of progress. * The following example shows a determinate progress bar that is 25% complete: * <pre> * &lt;ProgressBar * android:id="@+id/determinateBar" * style="@android:style/Widget.ProgressBar.Horizontal" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * android:progress="25"/&gt; * </pre> * You can update the percentage of progress displayed by using the * {@link #setProgress(int)} method, or by calling * {@link #incrementProgressBy(int)} to increase the current progress completed * by a specified amount. * By default, the progress bar is full when the progress value reaches 100. * You can adjust this default by setting the * {@link android.R.styleable#ProgressBar_max android:max} attribute. * </p> * <p>Other progress bar styles provided by the system include:</p> * <ul> * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li> * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li> * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li> * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li> * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse * Widget.ProgressBar.Small.Inverse}</li> * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse * Widget.ProgressBar.Large.Inverse}</li> * </ul> * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary * if your application uses a light colored theme (a white background).</p> * * <p><strong>XML attributes</b></strong> * <p> * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, * {@link android.R.styleable#View View Attributes} * </p> * * @attr ref android.R.styleable#ProgressBar_animationResolution * @attr ref android.R.styleable#ProgressBar_indeterminate * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable * @attr ref android.R.styleable#ProgressBar_indeterminateDuration * @attr ref android.R.styleable#ProgressBar_indeterminateOnly * @attr ref android.R.styleable#ProgressBar_interpolator * @attr ref android.R.styleable#ProgressBar_min * @attr ref android.R.styleable#ProgressBar_max * @attr ref android.R.styleable#ProgressBar_maxHeight * @attr ref android.R.styleable#ProgressBar_maxWidth * @attr ref android.R.styleable#ProgressBar_minHeight * @attr ref android.R.styleable#ProgressBar_minWidth * @attr ref android.R.styleable#ProgressBar_mirrorForRtl * @attr ref android.R.styleable#ProgressBar_progress * @attr ref android.R.styleable#ProgressBar_progressDrawable * @attr ref android.R.styleable#ProgressBar_secondaryProgress */
@RemoteView public class ProgressBar extends View { private static final int MAX_LEVEL = 10000; private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
Interpolator used for smooth progress animations.
/** Interpolator used for smooth progress animations. */
private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR = new DecelerateInterpolator();
Duration of smooth progress animations.
/** Duration of smooth progress animations. */
private static final int PROGRESS_ANIM_DURATION = 80; int mMinWidth; int mMaxWidth; int mMinHeight; int mMaxHeight; private int mProgress; private int mSecondaryProgress; private int mMin; private boolean mMinInitialized; private int mMax; private boolean mMaxInitialized; private int mBehavior; private int mDuration; private boolean mIndeterminate; private boolean mOnlyIndeterminate; private Transformation mTransformation; private AlphaAnimation mAnimation; private boolean mHasAnimation; private Drawable mIndeterminateDrawable; private Drawable mProgressDrawable; private Drawable mCurrentDrawable; private ProgressTintInfo mProgressTintInfo; int mSampleWidth = 0; private boolean mNoInvalidate; private Interpolator mInterpolator; private RefreshProgressRunnable mRefreshProgressRunnable; private long mUiThreadId; private boolean mShouldStartAnimationDrawable; private boolean mInDrawing; private boolean mAttached; private boolean mRefreshIsPosted;
Value used to track progress animation, in the range [0...1].
/** Value used to track progress animation, in the range [0...1]. */
private float mVisualProgress; boolean mMirrorForRtl = false; private boolean mAggregatedIsVisible; private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>(); private AccessibilityEventSender mAccessibilityEventSender;
Create a new progress bar with range 0...100 and initial progress of 0.
Params:
  • context – the application environment
/** * Create a new progress bar with range 0...100 and initial progress of 0. * @param context the application environment */
public ProgressBar(Context context) { this(context, null); } public ProgressBar(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.progressBarStyle); } public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mUiThreadId = Thread.currentThread().getId(); initProgressBar(); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes); mNoInvalidate = true; final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); if (progressDrawable != null) { // Calling setProgressDrawable can set mMaxHeight, so make sure the // corresponding XML attribute for mMaxHeight is read after calling // this method. if (needsTileify(progressDrawable)) { setProgressDrawableTiled(progressDrawable); } else { setProgressDrawable(progressDrawable); } } mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth); mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight); mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight); mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior); final int resID = a.getResourceId( com.android.internal.R.styleable.ProgressBar_interpolator, android.R.anim.linear_interpolator); // default to linear interpolator if (resID > 0) { setInterpolator(context, resID); } setMin(a.getInt(R.styleable.ProgressBar_min, mMin)); setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); setSecondaryProgress(a.getInt( R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); final Drawable indeterminateDrawable = a.getDrawable( R.styleable.ProgressBar_indeterminateDrawable); if (indeterminateDrawable != null) { if (needsTileify(indeterminateDrawable)) { setIndeterminateDrawableTiled(indeterminateDrawable); } else { setIndeterminateDrawable(indeterminateDrawable); } } mOnlyIndeterminate = a.getBoolean( R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate); mNoInvalidate = false; setIndeterminate(mOnlyIndeterminate || a.getBoolean( R.styleable.ProgressBar_indeterminate, mIndeterminate)); mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); if (a.hasValue(R.styleable.ProgressBar_progressTintMode)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressTintMode = Drawable.parseTintMode(a.getInt( R.styleable.ProgressBar_progressTintMode, -1), null); mProgressTintInfo.mHasProgressTintMode = true; } if (a.hasValue(R.styleable.ProgressBar_progressTint)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressTintList = a.getColorStateList( R.styleable.ProgressBar_progressTint); mProgressTintInfo.mHasProgressTint = true; } if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTintMode)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt( R.styleable.ProgressBar_progressBackgroundTintMode, -1), null); mProgressTintInfo.mHasProgressBackgroundTintMode = true; } if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList( R.styleable.ProgressBar_progressBackgroundTint); mProgressTintInfo.mHasProgressBackgroundTint = true; } if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTintMode)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mSecondaryProgressTintMode = Drawable.parseTintMode( a.getInt(R.styleable.ProgressBar_secondaryProgressTintMode, -1), null); mProgressTintInfo.mHasSecondaryProgressTintMode = true; } if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList( R.styleable.ProgressBar_secondaryProgressTint); mProgressTintInfo.mHasSecondaryProgressTint = true; } if (a.hasValue(R.styleable.ProgressBar_indeterminateTintMode)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mIndeterminateTintMode = Drawable.parseTintMode(a.getInt( R.styleable.ProgressBar_indeterminateTintMode, -1), null); mProgressTintInfo.mHasIndeterminateTintMode = true; } if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mIndeterminateTintList = a.getColorStateList( R.styleable.ProgressBar_indeterminateTint); mProgressTintInfo.mHasIndeterminateTint = true; } a.recycle(); applyProgressTints(); applyIndeterminateTint(); // If not explicitly specified this view is important for accessibility. if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } }
Returns true if the target drawable needs to be tileified.
Params:
  • dr – the drawable to check
Returns:true if the target drawable needs to be tileified, false otherwise
/** * Returns {@code true} if the target drawable needs to be tileified. * * @param dr the drawable to check * @return {@code true} if the target drawable needs to be tileified, * {@code false} otherwise */
private static boolean needsTileify(Drawable dr) { if (dr instanceof LayerDrawable) { final LayerDrawable orig = (LayerDrawable) dr; final int N = orig.getNumberOfLayers(); for (int i = 0; i < N; i++) { if (needsTileify(orig.getDrawable(i))) { return true; } } return false; } if (dr instanceof StateListDrawable) { final StateListDrawable in = (StateListDrawable) dr; final int N = in.getStateCount(); for (int i = 0; i < N; i++) { if (needsTileify(in.getStateDrawable(i))) { return true; } } return false; } // If there's a bitmap that's not wrapped with a ClipDrawable or // ScaleDrawable, we'll need to wrap it and apply tiling. if (dr instanceof BitmapDrawable) { return true; } return false; }
Converts a drawable to a tiled version of itself. It will recursively traverse layer and state list drawables.
/** * Converts a drawable to a tiled version of itself. It will recursively * traverse layer and state list drawables. */
private Drawable tileify(Drawable drawable, boolean clip) { // TODO: This is a terrible idea that potentially destroys any drawable // that extends any of these classes. We *really* need to remove this. if (drawable instanceof LayerDrawable) { final LayerDrawable orig = (LayerDrawable) drawable; final int N = orig.getNumberOfLayers(); final Drawable[] outDrawables = new Drawable[N]; for (int i = 0; i < N; i++) { final int id = orig.getId(i); outDrawables[i] = tileify(orig.getDrawable(i), (id == R.id.progress || id == R.id.secondaryProgress)); } final LayerDrawable clone = new LayerDrawable(outDrawables); for (int i = 0; i < N; i++) { clone.setId(i, orig.getId(i)); clone.setLayerGravity(i, orig.getLayerGravity(i)); clone.setLayerWidth(i, orig.getLayerWidth(i)); clone.setLayerHeight(i, orig.getLayerHeight(i)); clone.setLayerInsetLeft(i, orig.getLayerInsetLeft(i)); clone.setLayerInsetRight(i, orig.getLayerInsetRight(i)); clone.setLayerInsetTop(i, orig.getLayerInsetTop(i)); clone.setLayerInsetBottom(i, orig.getLayerInsetBottom(i)); clone.setLayerInsetStart(i, orig.getLayerInsetStart(i)); clone.setLayerInsetEnd(i, orig.getLayerInsetEnd(i)); } return clone; } if (drawable instanceof StateListDrawable) { final StateListDrawable in = (StateListDrawable) drawable; final StateListDrawable out = new StateListDrawable(); final int N = in.getStateCount(); for (int i = 0; i < N; i++) { out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); } return out; } if (drawable instanceof BitmapDrawable) { final Drawable.ConstantState cs = drawable.getConstantState(); final BitmapDrawable clone = (BitmapDrawable) cs.newDrawable(getResources()); clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); if (mSampleWidth <= 0) { mSampleWidth = clone.getIntrinsicWidth(); } if (clip) { return new ClipDrawable(clone, Gravity.LEFT, ClipDrawable.HORIZONTAL); } else { return clone; } } return drawable; } Shape getDrawableShape() { final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; return new RoundRectShape(roundedCorners, null, null); }
Convert a AnimationDrawable for use as a barberpole animation. Each frame of the animation is wrapped in a ClipDrawable and given a tiling BitmapShader.
/** * Convert a AnimationDrawable for use as a barberpole animation. * Each frame of the animation is wrapped in a ClipDrawable and * given a tiling BitmapShader. */
private Drawable tileifyIndeterminate(Drawable drawable) { if (drawable instanceof AnimationDrawable) { AnimationDrawable background = (AnimationDrawable) drawable; final int N = background.getNumberOfFrames(); AnimationDrawable newBg = new AnimationDrawable(); newBg.setOneShot(background.isOneShot()); for (int i = 0; i < N; i++) { Drawable frame = tileify(background.getFrame(i), true); frame.setLevel(10000); newBg.addFrame(frame, background.getDuration(i)); } newBg.setLevel(10000); drawable = newBg; } return drawable; }

Initialize the progress bar's default values:

  • progress = 0
  • max = 100
  • animation duration = 4000 ms
  • indeterminate = false
  • behavior = repeat
/** * <p> * Initialize the progress bar's default values: * </p> * <ul> * <li>progress = 0</li> * <li>max = 100</li> * <li>animation duration = 4000 ms</li> * <li>indeterminate = false</li> * <li>behavior = repeat</li> * </ul> */
private void initProgressBar() { mMin = 0; mMax = 100; mProgress = 0; mSecondaryProgress = 0; mIndeterminate = false; mOnlyIndeterminate = false; mDuration = 4000; mBehavior = AlphaAnimation.RESTART; mMinWidth = 24; mMaxWidth = 48; mMinHeight = 24; mMaxHeight = 48; }

Indicate whether this progress bar is in indeterminate mode.

Returns:true if the progress bar is in indeterminate mode
/** * <p>Indicate whether this progress bar is in indeterminate mode.</p> * * @return true if the progress bar is in indeterminate mode */
@ViewDebug.ExportedProperty(category = "progress") public synchronized boolean isIndeterminate() { return mIndeterminate; }

Change the indeterminate mode for this progress bar. In indeterminate mode, the progress is ignored and the progress bar shows an infinite animation instead.

If this progress bar's style only supports indeterminate mode (such as the circular progress bars), then this will be ignored.
Params:
  • indeterminate – true to enable the indeterminate mode
/** * <p>Change the indeterminate mode for this progress bar. In indeterminate * mode, the progress is ignored and the progress bar shows an infinite * animation instead.</p> * * If this progress bar's style only supports indeterminate mode (such as the circular * progress bars), then this will be ignored. * * @param indeterminate true to enable the indeterminate mode */
@android.view.RemotableViewMethod public synchronized void setIndeterminate(boolean indeterminate) { if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) { mIndeterminate = indeterminate; if (indeterminate) { // swap between indeterminate and regular backgrounds swapCurrentDrawable(mIndeterminateDrawable); startAnimation(); } else { swapCurrentDrawable(mProgressDrawable); stopAnimation(); } } } private void swapCurrentDrawable(Drawable newDrawable) { final Drawable oldDrawable = mCurrentDrawable; mCurrentDrawable = newDrawable; if (oldDrawable != mCurrentDrawable) { if (oldDrawable != null) { oldDrawable.setVisible(false, false); } if (mCurrentDrawable != null) { mCurrentDrawable.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); } } }

Get the drawable used to draw the progress bar in indeterminate mode.

See Also:
Returns:a Drawable instance
/** * <p>Get the drawable used to draw the progress bar in * indeterminate mode.</p> * * @return a {@link android.graphics.drawable.Drawable} instance * * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) * @see #setIndeterminate(boolean) */
public Drawable getIndeterminateDrawable() { return mIndeterminateDrawable; }
Define the drawable used to draw the progress bar in indeterminate mode.
Params:
  • d – the new drawable
See Also:
/** * Define the drawable used to draw the progress bar in indeterminate mode. * * @param d the new drawable * @see #getIndeterminateDrawable() * @see #setIndeterminate(boolean) */
public void setIndeterminateDrawable(Drawable d) { if (mIndeterminateDrawable != d) { if (mIndeterminateDrawable != null) { mIndeterminateDrawable.setCallback(null); unscheduleDrawable(mIndeterminateDrawable); } mIndeterminateDrawable = d; if (d != null) { d.setCallback(this); d.setLayoutDirection(getLayoutDirection()); if (d.isStateful()) { d.setState(getDrawableState()); } applyIndeterminateTint(); } if (mIndeterminate) { swapCurrentDrawable(d); postInvalidate(); } } }
Applies a tint to the indeterminate drawable. Does not modify the current tint mode, which is Mode.SRC_IN by default.

Subsequent calls to setIndeterminateDrawable(Drawable) will automatically mutate the drawable and apply the specified tint and tint mode using Drawable.setTintList(ColorStateList).

Params:
  • tint – the tint to apply, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_indeterminateTint
/** * Applies a tint to the indeterminate drawable. Does not modify the * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will * automatically mutate the drawable and apply the specified tint and * tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#ProgressBar_indeterminateTint * @see #getIndeterminateTintList() * @see Drawable#setTintList(ColorStateList) */
@RemotableViewMethod public void setIndeterminateTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mIndeterminateTintList = tint; mProgressTintInfo.mHasIndeterminateTint = true; applyIndeterminateTint(); }
See Also:
Returns:the tint applied to the indeterminate drawable
@attrref android.R.styleable#ProgressBar_indeterminateTint
/** * @return the tint applied to the indeterminate drawable * @attr ref android.R.styleable#ProgressBar_indeterminateTint * @see #setIndeterminateTintList(ColorStateList) */
@Nullable public ColorStateList getIndeterminateTintList() { return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintList : null; }
Specifies the blending mode used to apply the tint specified by setIndeterminateTintList(ColorStateList) to the indeterminate drawable. The default mode is Mode.SRC_IN.
Params:
  • tintMode – the blending mode used to apply the tint, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_indeterminateTintMode
/** * Specifies the blending mode used to apply the tint specified by * {@link #setIndeterminateTintList(ColorStateList)} to the indeterminate * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode * @see #setIndeterminateTintList(ColorStateList) * @see Drawable#setTintMode(PorterDuff.Mode) */
public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mIndeterminateTintMode = tintMode; mProgressTintInfo.mHasIndeterminateTintMode = true; applyIndeterminateTint(); }
Returns the blending mode used to apply the tint to the indeterminate drawable, if specified.
See Also:
Returns:the blending mode used to apply the tint to the indeterminate drawable
@attrref android.R.styleable#ProgressBar_indeterminateTintMode
/** * Returns the blending mode used to apply the tint to the indeterminate * drawable, if specified. * * @return the blending mode used to apply the tint to the indeterminate * drawable * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode * @see #setIndeterminateTintMode(PorterDuff.Mode) */
@Nullable public PorterDuff.Mode getIndeterminateTintMode() { return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintMode : null; } private void applyIndeterminateTint() { if (mIndeterminateDrawable != null && mProgressTintInfo != null) { final ProgressTintInfo tintInfo = mProgressTintInfo; if (tintInfo.mHasIndeterminateTint || tintInfo.mHasIndeterminateTintMode) { mIndeterminateDrawable = mIndeterminateDrawable.mutate(); if (tintInfo.mHasIndeterminateTint) { mIndeterminateDrawable.setTintList(tintInfo.mIndeterminateTintList); } if (tintInfo.mHasIndeterminateTintMode) { mIndeterminateDrawable.setTintMode(tintInfo.mIndeterminateTintMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if (mIndeterminateDrawable.isStateful()) { mIndeterminateDrawable.setState(getDrawableState()); } } } }
Define the tileable drawable used to draw the progress bar in indeterminate mode.

If the drawable is a BitmapDrawable or contains BitmapDrawables, a tiled copy will be generated for display as a progress bar.

Params:
  • d – the new drawable
See Also:
/** * Define the tileable drawable used to draw the progress bar in * indeterminate mode. * <p> * If the drawable is a BitmapDrawable or contains BitmapDrawables, a * tiled copy will be generated for display as a progress bar. * * @param d the new drawable * @see #getIndeterminateDrawable() * @see #setIndeterminate(boolean) */
public void setIndeterminateDrawableTiled(Drawable d) { if (d != null) { d = tileifyIndeterminate(d); } setIndeterminateDrawable(d); }

Get the drawable used to draw the progress bar in progress mode.

See Also:
Returns:a Drawable instance
/** * <p>Get the drawable used to draw the progress bar in * progress mode.</p> * * @return a {@link android.graphics.drawable.Drawable} instance * * @see #setProgressDrawable(android.graphics.drawable.Drawable) * @see #setIndeterminate(boolean) */
public Drawable getProgressDrawable() { return mProgressDrawable; }
Define the drawable used to draw the progress bar in progress mode.
Params:
  • d – the new drawable
See Also:
/** * Define the drawable used to draw the progress bar in progress mode. * * @param d the new drawable * @see #getProgressDrawable() * @see #setIndeterminate(boolean) */
public void setProgressDrawable(Drawable d) { if (mProgressDrawable != d) { if (mProgressDrawable != null) { mProgressDrawable.setCallback(null); unscheduleDrawable(mProgressDrawable); } mProgressDrawable = d; if (d != null) { d.setCallback(this); d.setLayoutDirection(getLayoutDirection()); if (d.isStateful()) { d.setState(getDrawableState()); } // Make sure the ProgressBar is always tall enough int drawableHeight = d.getMinimumHeight(); if (mMaxHeight < drawableHeight) { mMaxHeight = drawableHeight; requestLayout(); } applyProgressTints(); } if (!mIndeterminate) { swapCurrentDrawable(d); postInvalidate(); } updateDrawableBounds(getWidth(), getHeight()); updateDrawableState(); doRefreshProgress(R.id.progress, mProgress, false, false, false); doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false, false); } }
@hide
/** * @hide */
public boolean getMirrorForRtl() { return mMirrorForRtl; }
Applies the progress tints in order of increasing specificity.
/** * Applies the progress tints in order of increasing specificity. */
private void applyProgressTints() { if (mProgressDrawable != null && mProgressTintInfo != null) { applyPrimaryProgressTint(); applyProgressBackgroundTint(); applySecondaryProgressTint(); } }
Should only be called if we've already verified that mProgressDrawable and mProgressTintInfo are non-null.
/** * Should only be called if we've already verified that mProgressDrawable * and mProgressTintInfo are non-null. */
private void applyPrimaryProgressTint() { if (mProgressTintInfo.mHasProgressTint || mProgressTintInfo.mHasProgressTintMode) { final Drawable target = getTintTarget(R.id.progress, true); if (target != null) { if (mProgressTintInfo.mHasProgressTint) { target.setTintList(mProgressTintInfo.mProgressTintList); } if (mProgressTintInfo.mHasProgressTintMode) { target.setTintMode(mProgressTintInfo.mProgressTintMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if (target.isStateful()) { target.setState(getDrawableState()); } } } }
Should only be called if we've already verified that mProgressDrawable and mProgressTintInfo are non-null.
/** * Should only be called if we've already verified that mProgressDrawable * and mProgressTintInfo are non-null. */
private void applyProgressBackgroundTint() { if (mProgressTintInfo.mHasProgressBackgroundTint || mProgressTintInfo.mHasProgressBackgroundTintMode) { final Drawable target = getTintTarget(R.id.background, false); if (target != null) { if (mProgressTintInfo.mHasProgressBackgroundTint) { target.setTintList(mProgressTintInfo.mProgressBackgroundTintList); } if (mProgressTintInfo.mHasProgressBackgroundTintMode) { target.setTintMode(mProgressTintInfo.mProgressBackgroundTintMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if (target.isStateful()) { target.setState(getDrawableState()); } } } }
Should only be called if we've already verified that mProgressDrawable and mProgressTintInfo are non-null.
/** * Should only be called if we've already verified that mProgressDrawable * and mProgressTintInfo are non-null. */
private void applySecondaryProgressTint() { if (mProgressTintInfo.mHasSecondaryProgressTint || mProgressTintInfo.mHasSecondaryProgressTintMode) { final Drawable target = getTintTarget(R.id.secondaryProgress, false); if (target != null) { if (mProgressTintInfo.mHasSecondaryProgressTint) { target.setTintList(mProgressTintInfo.mSecondaryProgressTintList); } if (mProgressTintInfo.mHasSecondaryProgressTintMode) { target.setTintMode(mProgressTintInfo.mSecondaryProgressTintMode); } // The drawable (or one of its children) may not have been // stateful before applying the tint, so let's try again. if (target.isStateful()) { target.setState(getDrawableState()); } } } }
Applies a tint to the progress indicator, if one exists, or to the entire progress drawable otherwise. Does not modify the current tint mode, which is Mode.SRC_IN by default.

The progress indicator should be specified as a layer with id progress.progress in a LayerDrawable used as the progress drawable.

Subsequent calls to setProgressDrawable(Drawable) will automatically mutate the drawable and apply the specified tint and tint mode using Drawable.setTintList(ColorStateList).

Params:
  • tint – the tint to apply, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_progressTint
/** * Applies a tint to the progress indicator, if one exists, or to the * entire progress drawable otherwise. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> * The progress indicator should be specified as a layer with * id {@link android.R.id#progress} in a {@link LayerDrawable} * used as the progress drawable. * <p> * Subsequent calls to {@link #setProgressDrawable(Drawable)} will * automatically mutate the drawable and apply the specified tint and * tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#ProgressBar_progressTint * @see #getProgressTintList() * @see Drawable#setTintList(ColorStateList) */
@RemotableViewMethod public void setProgressTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressTintList = tint; mProgressTintInfo.mHasProgressTint = true; if (mProgressDrawable != null) { applyPrimaryProgressTint(); } }
Returns the tint applied to the progress drawable, if specified.
See Also:
Returns:the tint applied to the progress drawable
@attrref android.R.styleable#ProgressBar_progressTint
/** * Returns the tint applied to the progress drawable, if specified. * * @return the tint applied to the progress drawable * @attr ref android.R.styleable#ProgressBar_progressTint * @see #setProgressTintList(ColorStateList) */
@Nullable public ColorStateList getProgressTintList() { return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintList : null; }
Specifies the blending mode used to apply the tint specified by setProgressTintList(ColorStateList)} to the progress indicator. The default mode is Mode.SRC_IN.
Params:
  • tintMode – the blending mode used to apply the tint, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_progressTintMode
/** * Specifies the blending mode used to apply the tint specified by * {@link #setProgressTintList(ColorStateList)}} to the progress * indicator. The default mode is {@link PorterDuff.Mode#SRC_IN}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#ProgressBar_progressTintMode * @see #getProgressTintMode() * @see Drawable#setTintMode(PorterDuff.Mode) */
public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressTintMode = tintMode; mProgressTintInfo.mHasProgressTintMode = true; if (mProgressDrawable != null) { applyPrimaryProgressTint(); } }
Returns the blending mode used to apply the tint to the progress drawable, if specified.
See Also:
Returns:the blending mode used to apply the tint to the progress drawable
@attrref android.R.styleable#ProgressBar_progressTintMode
/** * Returns the blending mode used to apply the tint to the progress * drawable, if specified. * * @return the blending mode used to apply the tint to the progress * drawable * @attr ref android.R.styleable#ProgressBar_progressTintMode * @see #setProgressTintMode(PorterDuff.Mode) */
@Nullable public PorterDuff.Mode getProgressTintMode() { return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintMode : null; }
Applies a tint to the progress background, if one exists. Does not modify the current tint mode, which is Mode.SRC_ATOP by default.

The progress background must be specified as a layer with id background.background in a LayerDrawable used as the progress drawable.

Subsequent calls to setProgressDrawable(Drawable) where the drawable contains a progress background will automatically mutate the drawable and apply the specified tint and tint mode using Drawable.setTintList(ColorStateList).

Params:
  • tint – the tint to apply, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_progressBackgroundTint
/** * Applies a tint to the progress background, if one exists. Does not * modify the current tint mode, which is * {@link PorterDuff.Mode#SRC_ATOP} by default. * <p> * The progress background must be specified as a layer with * id {@link android.R.id#background} in a {@link LayerDrawable} * used as the progress drawable. * <p> * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the * drawable contains a progress background will automatically mutate the * drawable and apply the specified tint and tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint * @see #getProgressBackgroundTintList() * @see Drawable#setTintList(ColorStateList) */
@RemotableViewMethod public void setProgressBackgroundTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressBackgroundTintList = tint; mProgressTintInfo.mHasProgressBackgroundTint = true; if (mProgressDrawable != null) { applyProgressBackgroundTint(); } }
Returns the tint applied to the progress background, if specified.
See Also:
Returns:the tint applied to the progress background
@attrref android.R.styleable#ProgressBar_progressBackgroundTint
/** * Returns the tint applied to the progress background, if specified. * * @return the tint applied to the progress background * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint * @see #setProgressBackgroundTintList(ColorStateList) */
@Nullable public ColorStateList getProgressBackgroundTintList() { return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintList : null; }
Specifies the blending mode used to apply the tint specified by setProgressBackgroundTintList(ColorStateList)} to the progress background. The default mode is Mode.SRC_IN.
Params:
  • tintMode – the blending mode used to apply the tint, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_progressBackgroundTintMode
/** * Specifies the blending mode used to apply the tint specified by * {@link #setProgressBackgroundTintList(ColorStateList)}} to the progress * background. The default mode is {@link PorterDuff.Mode#SRC_IN}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode * @see #setProgressBackgroundTintList(ColorStateList) * @see Drawable#setTintMode(PorterDuff.Mode) */
public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mProgressBackgroundTintMode = tintMode; mProgressTintInfo.mHasProgressBackgroundTintMode = true; if (mProgressDrawable != null) { applyProgressBackgroundTint(); } }
See Also:
Returns:the blending mode used to apply the tint to the progress background
@attrref android.R.styleable#ProgressBar_progressBackgroundTintMode
/** * @return the blending mode used to apply the tint to the progress * background * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode * @see #setProgressBackgroundTintMode(PorterDuff.Mode) */
@Nullable public PorterDuff.Mode getProgressBackgroundTintMode() { return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintMode : null; }
Applies a tint to the secondary progress indicator, if one exists. Does not modify the current tint mode, which is Mode.SRC_ATOP by default.

The secondary progress indicator must be specified as a layer with id secondaryProgress.secondaryProgress in a LayerDrawable used as the progress drawable.

Subsequent calls to setProgressDrawable(Drawable) where the drawable contains a secondary progress indicator will automatically mutate the drawable and apply the specified tint and tint mode using Drawable.setTintList(ColorStateList).

Params:
  • tint – the tint to apply, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_secondaryProgressTint
/** * Applies a tint to the secondary progress indicator, if one exists. * Does not modify the current tint mode, which is * {@link PorterDuff.Mode#SRC_ATOP} by default. * <p> * The secondary progress indicator must be specified as a layer with * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable} * used as the progress drawable. * <p> * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the * drawable contains a secondary progress indicator will automatically * mutate the drawable and apply the specified tint and tint mode using * {@link Drawable#setTintList(ColorStateList)}. * * @param tint the tint to apply, may be {@code null} to clear tint * * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint * @see #getSecondaryProgressTintList() * @see Drawable#setTintList(ColorStateList) */
public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mSecondaryProgressTintList = tint; mProgressTintInfo.mHasSecondaryProgressTint = true; if (mProgressDrawable != null) { applySecondaryProgressTint(); } }
Returns the tint applied to the secondary progress drawable, if specified.
See Also:
Returns:the tint applied to the secondary progress drawable
@attrref android.R.styleable#ProgressBar_secondaryProgressTint
/** * Returns the tint applied to the secondary progress drawable, if * specified. * * @return the tint applied to the secondary progress drawable * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint * @see #setSecondaryProgressTintList(ColorStateList) */
@Nullable public ColorStateList getSecondaryProgressTintList() { return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintList : null; }
Specifies the blending mode used to apply the tint specified by setSecondaryProgressTintList(ColorStateList)} to the secondary progress indicator. The default mode is Mode.SRC_ATOP.
Params:
  • tintMode – the blending mode used to apply the tint, may be null to clear tint
See Also:
@attrref android.R.styleable#ProgressBar_secondaryProgressTintMode
/** * Specifies the blending mode used to apply the tint specified by * {@link #setSecondaryProgressTintList(ColorStateList)}} to the secondary * progress indicator. The default mode is * {@link PorterDuff.Mode#SRC_ATOP}. * * @param tintMode the blending mode used to apply the tint, may be * {@code null} to clear tint * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode * @see #setSecondaryProgressTintList(ColorStateList) * @see Drawable#setTintMode(PorterDuff.Mode) */
public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); } mProgressTintInfo.mSecondaryProgressTintMode = tintMode; mProgressTintInfo.mHasSecondaryProgressTintMode = true; if (mProgressDrawable != null) { applySecondaryProgressTint(); } }
Returns the blending mode used to apply the tint to the secondary progress drawable, if specified.
See Also:
Returns:the blending mode used to apply the tint to the secondary progress drawable
@attrref android.R.styleable#ProgressBar_secondaryProgressTintMode
/** * Returns the blending mode used to apply the tint to the secondary * progress drawable, if specified. * * @return the blending mode used to apply the tint to the secondary * progress drawable * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode * @see #setSecondaryProgressTintMode(PorterDuff.Mode) */
@Nullable public PorterDuff.Mode getSecondaryProgressTintMode() { return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintMode : null; }
Returns the drawable to which a tint or tint mode should be applied.
Params:
  • layerId – id of the layer to modify
  • shouldFallback – whether the base drawable should be returned if the id does not exist
Returns:the drawable to modify
/** * Returns the drawable to which a tint or tint mode should be applied. * * @param layerId id of the layer to modify * @param shouldFallback whether the base drawable should be returned * if the id does not exist * @return the drawable to modify */
@Nullable private Drawable getTintTarget(int layerId, boolean shouldFallback) { Drawable layer = null; final Drawable d = mProgressDrawable; if (d != null) { mProgressDrawable = d.mutate(); if (d instanceof LayerDrawable) { layer = ((LayerDrawable) d).findDrawableByLayerId(layerId); } if (shouldFallback && layer == null) { layer = d; } } return layer; }
Define the tileable drawable used to draw the progress bar in progress mode.

If the drawable is a BitmapDrawable or contains BitmapDrawables, a tiled copy will be generated for display as a progress bar.

Params:
  • d – the new drawable
See Also:
/** * Define the tileable drawable used to draw the progress bar in * progress mode. * <p> * If the drawable is a BitmapDrawable or contains BitmapDrawables, a * tiled copy will be generated for display as a progress bar. * * @param d the new drawable * @see #getProgressDrawable() * @see #setIndeterminate(boolean) */
public void setProgressDrawableTiled(Drawable d) { if (d != null) { d = tileify(d, false); } setProgressDrawable(d); }
Returns:The drawable currently used to draw the progress bar
/** * @return The drawable currently used to draw the progress bar */
Drawable getCurrentDrawable() { return mCurrentDrawable; } @Override protected boolean verifyDrawable(@NonNull Drawable who) { return who == mProgressDrawable || who == mIndeterminateDrawable || super.verifyDrawable(who); } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState(); if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState(); }
@hide
/** * @hide */
@Override public void onResolveDrawables(int layoutDirection) { final Drawable d = mCurrentDrawable; if (d != null) { d.setLayoutDirection(layoutDirection); } if (mIndeterminateDrawable != null) { mIndeterminateDrawable.setLayoutDirection(layoutDirection); } if (mProgressDrawable != null) { mProgressDrawable.setLayoutDirection(layoutDirection); } } @Override public void postInvalidate() { if (!mNoInvalidate) { super.postInvalidate(); } } private class RefreshProgressRunnable implements Runnable { public void run() { synchronized (ProgressBar.this) { final int count = mRefreshData.size(); for (int i = 0; i < count; i++) { final RefreshData rd = mRefreshData.get(i); doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate); rd.recycle(); } mRefreshData.clear(); mRefreshIsPosted = false; } } } private static class RefreshData { private static final int POOL_MAX = 24; private static final SynchronizedPool<RefreshData> sPool = new SynchronizedPool<RefreshData>(POOL_MAX); public int id; public int progress; public boolean fromUser; public boolean animate; public static RefreshData obtain(int id, int progress, boolean fromUser, boolean animate) { RefreshData rd = sPool.acquire(); if (rd == null) { rd = new RefreshData(); } rd.id = id; rd.progress = progress; rd.fromUser = fromUser; rd.animate = animate; return rd; } public void recycle() { sPool.release(this); } } private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp, boolean animate) { int range = mMax - mMin; final float scale = range > 0 ? (progress - mMin) / (float) range : 0; final boolean isPrimary = id == R.id.progress; if (isPrimary && animate) { final ObjectAnimator animator = ObjectAnimator.ofFloat(this, VISUAL_PROGRESS, scale); animator.setAutoCancel(true); animator.setDuration(PROGRESS_ANIM_DURATION); animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR); animator.start(); } else { setVisualProgress(id, scale); } if (isPrimary && callBackToApp) { onProgressRefresh(scale, fromUser, progress); } } void onProgressRefresh(float scale, boolean fromUser, int progress) { if (AccessibilityManager.getInstance(mContext).isEnabled()) { scheduleAccessibilityEventSender(); } }
Sets the visual state of a progress indicator.
Params:
  • id – the identifier of the progress indicator
  • progress – the visual progress in the range [0...1]
/** * Sets the visual state of a progress indicator. * * @param id the identifier of the progress indicator * @param progress the visual progress in the range [0...1] */
private void setVisualProgress(int id, float progress) { mVisualProgress = progress; Drawable d = mCurrentDrawable; if (d instanceof LayerDrawable) { d = ((LayerDrawable) d).findDrawableByLayerId(id); if (d == null) { // If we can't find the requested layer, fall back to setting // the level of the entire drawable. This will break if // progress is set on multiple elements, but the theme-default // drawable will always have all layer IDs present. d = mCurrentDrawable; } } if (d != null) { final int level = (int) (progress * MAX_LEVEL); d.setLevel(level); } else { invalidate(); } onVisualProgressChanged(id, progress); }
Called when the visual state of a progress indicator changes.
Params:
  • id – the identifier of the progress indicator
  • progress – the visual progress in the range [0...1]
/** * Called when the visual state of a progress indicator changes. * * @param id the identifier of the progress indicator * @param progress the visual progress in the range [0...1] */
void onVisualProgressChanged(int id, float progress) { // Stub method. } private synchronized void refreshProgress(int id, int progress, boolean fromUser, boolean animate) { if (mUiThreadId == Thread.currentThread().getId()) { doRefreshProgress(id, progress, fromUser, true, animate); } else { if (mRefreshProgressRunnable == null) { mRefreshProgressRunnable = new RefreshProgressRunnable(); } final RefreshData rd = RefreshData.obtain(id, progress, fromUser, animate); mRefreshData.add(rd); if (mAttached && !mRefreshIsPosted) { post(mRefreshProgressRunnable); mRefreshIsPosted = true; } } }
Sets the current progress to the specified value. Does not do anything if the progress bar is in indeterminate mode.

This method will immediately update the visual position of the progress indicator. To animate the visual position to the target value, use setProgress(int, boolean)}.

Params:
See Also:
/** * Sets the current progress to the specified value. Does not do anything * if the progress bar is in indeterminate mode. * <p> * This method will immediately update the visual position of the progress * indicator. To animate the visual position to the target value, use * {@link #setProgress(int, boolean)}}. * * @param progress the new progress, between {@link #getMin()} and {@link #getMax()} * * @see #setIndeterminate(boolean) * @see #isIndeterminate() * @see #getProgress() * @see #incrementProgressBy(int) */
@android.view.RemotableViewMethod public synchronized void setProgress(int progress) { setProgressInternal(progress, false, false); }
Sets the current progress to the specified value, optionally animating the visual position between the current and target values.

Animation does not affect the result of getProgress(), which will return the target value immediately after this method is called.

Params:
  • progress – the new progress value, between getMin() and getMax()
  • animate – true to animate between the current and target values or false to not animate
/** * Sets the current progress to the specified value, optionally animating * the visual position between the current and target values. * <p> * Animation does not affect the result of {@link #getProgress()}, which * will return the target value immediately after this method is called. * * @param progress the new progress value, between {@link #getMin()} and {@link #getMax()} * @param animate {@code true} to animate between the current and target * values or {@code false} to not animate */
public void setProgress(int progress, boolean animate) { setProgressInternal(progress, false, animate); } @android.view.RemotableViewMethod synchronized boolean setProgressInternal(int progress, boolean fromUser, boolean animate) { if (mIndeterminate) { // Not applicable. return false; } progress = MathUtils.constrain(progress, mMin, mMax); if (progress == mProgress) { // No change from current. return false; } mProgress = progress; refreshProgress(R.id.progress, mProgress, fromUser, animate); return true; }

Set the current secondary progress to the specified value. Does not do anything if the progress bar is in indeterminate mode.

Params:
See Also:
/** * <p> * Set the current secondary progress to the specified value. Does not do * anything if the progress bar is in indeterminate mode. * </p> * * @param secondaryProgress the new secondary progress, between {@link #getMin()} and * {@link #getMax()} * @see #setIndeterminate(boolean) * @see #isIndeterminate() * @see #getSecondaryProgress() * @see #incrementSecondaryProgressBy(int) */
@android.view.RemotableViewMethod public synchronized void setSecondaryProgress(int secondaryProgress) { if (mIndeterminate) { return; } if (secondaryProgress < mMin) { secondaryProgress = mMin; } if (secondaryProgress > mMax) { secondaryProgress = mMax; } if (secondaryProgress != mSecondaryProgress) { mSecondaryProgress = secondaryProgress; refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false); } }

Get the progress bar's current level of progress. Return 0 when the progress bar is in indeterminate mode.

See Also:
Returns:the current progress, between getMin() and getMax()
/** * <p>Get the progress bar's current level of progress. Return 0 when the * progress bar is in indeterminate mode.</p> * * @return the current progress, between {@link #getMin()} and {@link #getMax()} * * @see #setIndeterminate(boolean) * @see #isIndeterminate() * @see #setProgress(int) * @see #setMax(int) * @see #getMax() */
@ViewDebug.ExportedProperty(category = "progress") public synchronized int getProgress() { return mIndeterminate ? 0 : mProgress; }

Get the progress bar's current level of secondary progress. Return 0 when the progress bar is in indeterminate mode.

See Also:
Returns:the current secondary progress, between getMin() and getMax()
/** * <p>Get the progress bar's current level of secondary progress. Return 0 when the * progress bar is in indeterminate mode.</p> * * @return the current secondary progress, between {@link #getMin()} and {@link #getMax()} * * @see #setIndeterminate(boolean) * @see #isIndeterminate() * @see #setSecondaryProgress(int) * @see #setMax(int) * @see #getMax() */
@ViewDebug.ExportedProperty(category = "progress") public synchronized int getSecondaryProgress() { return mIndeterminate ? 0 : mSecondaryProgress; }

Return the lower limit of this progress bar's range.

See Also:
Returns:a positive integer
/** * <p>Return the lower limit of this progress bar's range.</p> * * @return a positive integer * * @see #setMin(int) * @see #getProgress() * @see #getSecondaryProgress() */
@ViewDebug.ExportedProperty(category = "progress") public synchronized int getMin() { return mMin; }

Return the upper limit of this progress bar's range.

See Also:
Returns:a positive integer
/** * <p>Return the upper limit of this progress bar's range.</p> * * @return a positive integer * * @see #setMax(int) * @see #getProgress() * @see #getSecondaryProgress() */
@ViewDebug.ExportedProperty(category = "progress") public synchronized int getMax() { return mMax; }

Set the lower range of the progress bar to min.

Params:
  • min – the lower range of this progress bar
See Also:
/** * <p>Set the lower range of the progress bar to <tt>min</tt>.</p> * * @param min the lower range of this progress bar * * @see #getMin() * @see #setProgress(int) * @see #setSecondaryProgress(int) */
@android.view.RemotableViewMethod public synchronized void setMin(int min) { if (mMaxInitialized) { if (min > mMax) { min = mMax; } } mMinInitialized = true; if (mMaxInitialized && min != mMin) { mMin = min; postInvalidate(); if (mProgress < min) { mProgress = min; } refreshProgress(R.id.progress, mProgress, false, false); } else { mMin = min; } }

Set the upper range of the progress bar max.

Params:
  • max – the upper range of this progress bar
See Also:
/** * <p>Set the upper range of the progress bar <tt>max</tt>.</p> * * @param max the upper range of this progress bar * * @see #getMax() * @see #setProgress(int) * @see #setSecondaryProgress(int) */
@android.view.RemotableViewMethod public synchronized void setMax(int max) { if (mMinInitialized) { if (max < mMin) { max = mMin; } } mMaxInitialized = true; if (mMinInitialized && max != mMax) { mMax = max; postInvalidate(); if (mProgress > max) { mProgress = max; } refreshProgress(R.id.progress, mProgress, false, false); } else { mMax = max; } }

Increase the progress bar's progress by the specified amount.

Params:
  • diff – the amount by which the progress must be increased
See Also:
/** * <p>Increase the progress bar's progress by the specified amount.</p> * * @param diff the amount by which the progress must be increased * * @see #setProgress(int) */
public synchronized final void incrementProgressBy(int diff) { setProgress(mProgress + diff); }

Increase the progress bar's secondary progress by the specified amount.

Params:
  • diff – the amount by which the secondary progress must be increased
See Also:
/** * <p>Increase the progress bar's secondary progress by the specified amount.</p> * * @param diff the amount by which the secondary progress must be increased * * @see #setSecondaryProgress(int) */
public synchronized final void incrementSecondaryProgressBy(int diff) { setSecondaryProgress(mSecondaryProgress + diff); }

Start the indeterminate progress animation.

/** * <p>Start the indeterminate progress animation.</p> */
void startAnimation() { if (getVisibility() != VISIBLE || getWindowVisibility() != VISIBLE) { return; } if (mIndeterminateDrawable instanceof Animatable) { mShouldStartAnimationDrawable = true; mHasAnimation = false; } else { mHasAnimation = true; if (mInterpolator == null) { mInterpolator = new LinearInterpolator(); } if (mTransformation == null) { mTransformation = new Transformation(); } else { mTransformation.clear(); } if (mAnimation == null) { mAnimation = new AlphaAnimation(0.0f, 1.0f); } else { mAnimation.reset(); } mAnimation.setRepeatMode(mBehavior); mAnimation.setRepeatCount(Animation.INFINITE); mAnimation.setDuration(mDuration); mAnimation.setInterpolator(mInterpolator); mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); } postInvalidate(); }

Stop the indeterminate progress animation.

/** * <p>Stop the indeterminate progress animation.</p> */
void stopAnimation() { mHasAnimation = false; if (mIndeterminateDrawable instanceof Animatable) { ((Animatable) mIndeterminateDrawable).stop(); mShouldStartAnimationDrawable = false; } postInvalidate(); }
Sets the acceleration curve for the indeterminate animation. The interpolator is loaded as a resource from the specified context.
Params:
  • context – The application environment
  • resID – The resource identifier of the interpolator to load
/** * Sets the acceleration curve for the indeterminate animation. * The interpolator is loaded as a resource from the specified context. * * @param context The application environment * @param resID The resource identifier of the interpolator to load */
public void setInterpolator(Context context, @InterpolatorRes int resID) { setInterpolator(AnimationUtils.loadInterpolator(context, resID)); }
Sets the acceleration curve for the indeterminate animation. Defaults to a linear interpolation.
Params:
  • interpolator – The interpolator which defines the acceleration curve
/** * Sets the acceleration curve for the indeterminate animation. * Defaults to a linear interpolation. * * @param interpolator The interpolator which defines the acceleration curve */
public void setInterpolator(Interpolator interpolator) { mInterpolator = interpolator; }
Gets the acceleration curve type for the indeterminate animation.
Returns:the Interpolator associated to this animation
/** * Gets the acceleration curve type for the indeterminate animation. * * @return the {@link Interpolator} associated to this animation */
public Interpolator getInterpolator() { return mInterpolator; } @Override public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); if (isVisible != mAggregatedIsVisible) { mAggregatedIsVisible = isVisible; if (mIndeterminate) { // let's be nice with the UI thread if (isVisible) { startAnimation(); } else { stopAnimation(); } } if (mCurrentDrawable != null) { mCurrentDrawable.setVisible(isVisible, false); } } } @Override public void invalidateDrawable(@NonNull Drawable dr) { if (!mInDrawing) { if (verifyDrawable(dr)) { final Rect dirty = dr.getBounds(); final int scrollX = mScrollX + mPaddingLeft; final int scrollY = mScrollY + mPaddingTop; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); } else { super.invalidateDrawable(dr); } } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { updateDrawableBounds(w, h); } private void updateDrawableBounds(int w, int h) { // onDraw will translate the canvas so we draw starting at 0,0. // Subtract out padding for the purposes of the calculations below. w -= mPaddingRight + mPaddingLeft; h -= mPaddingTop + mPaddingBottom; int right = w; int bottom = h; int top = 0; int left = 0; if (mIndeterminateDrawable != null) { // Aspect ratio logic does not apply to AnimationDrawables if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) { // Maintain aspect ratio. Certain kinds of animated drawables // get very confused otherwise. final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth(); final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight(); final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; final float boundAspect = (float) w / h; if (intrinsicAspect != boundAspect) { if (boundAspect > intrinsicAspect) { // New width is larger. Make it smaller to match height. final int width = (int) (h * intrinsicAspect); left = (w - width) / 2; right = left + width; } else { // New height is larger. Make it smaller to match width. final int height = (int) (w * (1 / intrinsicAspect)); top = (h - height) / 2; bottom = top + height; } } } if (isLayoutRtl() && mMirrorForRtl) { int tempLeft = left; left = w - right; right = w - tempLeft; } mIndeterminateDrawable.setBounds(left, top, right, bottom); } if (mProgressDrawable != null) { mProgressDrawable.setBounds(0, 0, right, bottom); } } @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); drawTrack(canvas); }
Draws the progress bar track.
/** * Draws the progress bar track. */
void drawTrack(Canvas canvas) { final Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation final int saveCount = canvas.save(); if (isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { canvas.translate(mPaddingLeft, mPaddingTop); } final long time = getDrawingTime(); if (mHasAnimation) { mAnimation.getTransformation(time, mTransformation); final float scale = mTransformation.getAlpha(); try { mInDrawing = true; d.setLevel((int) (scale * MAX_LEVEL)); } finally { mInDrawing = false; } postInvalidateOnAnimation(); } d.draw(canvas); canvas.restoreToCount(saveCount); if (mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false; } } } @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int dw = 0; int dh = 0; final Drawable d = mCurrentDrawable; if (d != null) { dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); } updateDrawableState(); dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0); final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0); setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); updateDrawableState(); } private void updateDrawableState() { final int[] state = getDrawableState(); boolean changed = false; final Drawable progressDrawable = mProgressDrawable; if (progressDrawable != null && progressDrawable.isStateful()) { changed |= progressDrawable.setState(state); } final Drawable indeterminateDrawable = mIndeterminateDrawable; if (indeterminateDrawable != null && indeterminateDrawable.isStateful()) { changed |= indeterminateDrawable.setState(state); } if (changed) { invalidate(); } } @Override public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (mProgressDrawable != null) { mProgressDrawable.setHotspot(x, y); } if (mIndeterminateDrawable != null) { mIndeterminateDrawable.setHotspot(x, y); } } static class SavedState extends BaseSavedState { int progress; int secondaryProgress;
Constructor called from ProgressBar.onSaveInstanceState()
/** * Constructor called from {@link ProgressBar#onSaveInstanceState()} */
SavedState(Parcelable superState) { super(superState); }
Constructor called from CREATOR
/** * Constructor called from {@link #CREATOR} */
private SavedState(Parcel in) { super(in); progress = in.readInt(); secondaryProgress = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(progress); out.writeInt(secondaryProgress); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.progress = mProgress; ss.secondaryProgress = mSecondaryProgress; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setProgress(ss.progress); setSecondaryProgress(ss.secondaryProgress); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mIndeterminate) { startAnimation(); } if (mRefreshData != null) { synchronized (this) { final int count = mRefreshData.size(); for (int i = 0; i < count; i++) { final RefreshData rd = mRefreshData.get(i); doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate); rd.recycle(); } mRefreshData.clear(); } } mAttached = true; } @Override protected void onDetachedFromWindow() { if (mIndeterminate) { stopAnimation(); } if (mRefreshProgressRunnable != null) { removeCallbacks(mRefreshProgressRunnable); mRefreshIsPosted = false; } if (mAccessibilityEventSender != null) { removeCallbacks(mAccessibilityEventSender); } // This should come after stopAnimation(), otherwise an invalidate message remains in the // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation super.onDetachedFromWindow(); mAttached = false; } @Override public CharSequence getAccessibilityClassName() { return ProgressBar.class.getName(); }
@hide
/** @hide */
@Override public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { super.onInitializeAccessibilityEventInternal(event); event.setItemCount(mMax - mMin); event.setCurrentItemIndex(mProgress); }
@hide
/** @hide */
@Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); if (!isIndeterminate()) { AccessibilityNodeInfo.RangeInfo rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), getProgress()); info.setRangeInfo(rangeInfo); } }
Schedule a command for sending an accessibility event.
Note: A command is used to ensure that accessibility events are sent at most one in a given time frame to save system resources while the progress changes quickly.
/** * Schedule a command for sending an accessibility event. * </br> * Note: A command is used to ensure that accessibility events * are sent at most one in a given time frame to save * system resources while the progress changes quickly. */
private void scheduleAccessibilityEventSender() { if (mAccessibilityEventSender == null) { mAccessibilityEventSender = new AccessibilityEventSender(); } else { removeCallbacks(mAccessibilityEventSender); } postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); }
@hide
/** @hide */
@Override protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { super.encodeProperties(stream); stream.addProperty("progress:max", getMax()); stream.addProperty("progress:progress", getProgress()); stream.addProperty("progress:secondaryProgress", getSecondaryProgress()); stream.addProperty("progress:indeterminate", isIndeterminate()); }
Returns whether the ProgressBar is animating or not. This is essentially the same as whether the ProgressBar is indeterminate and visible, as indeterminate ProgressBars are always animating, and non-indeterminate ProgressBars are not animating.
Returns:true if the ProgressBar is animating, false otherwise.
/** * Returns whether the ProgressBar is animating or not. This is essentially the same * as whether the ProgressBar is {@link #isIndeterminate() indeterminate} and visible, * as indeterminate ProgressBars are always animating, and non-indeterminate * ProgressBars are not animating. * * @return true if the ProgressBar is animating, false otherwise. */
public boolean isAnimating() { return isIndeterminate() && getWindowVisibility() == VISIBLE && isShown(); }
Command for sending an accessibility event.
/** * Command for sending an accessibility event. */
private class AccessibilityEventSender implements Runnable { public void run() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } } private static class ProgressTintInfo { ColorStateList mIndeterminateTintList; PorterDuff.Mode mIndeterminateTintMode; boolean mHasIndeterminateTint; boolean mHasIndeterminateTintMode; ColorStateList mProgressTintList; PorterDuff.Mode mProgressTintMode; boolean mHasProgressTint; boolean mHasProgressTintMode; ColorStateList mProgressBackgroundTintList; PorterDuff.Mode mProgressBackgroundTintMode; boolean mHasProgressBackgroundTint; boolean mHasProgressBackgroundTintMode; ColorStateList mSecondaryProgressTintList; PorterDuff.Mode mSecondaryProgressTintMode; boolean mHasSecondaryProgressTint; boolean mHasSecondaryProgressTintMode; }
Property wrapper around the visual state of the progress functionality handled by the setProgress(int, boolean) method. This does not correspond directly to the actual progress -- only the visual state.
/** * Property wrapper around the visual state of the {@code progress} functionality * handled by the {@link ProgressBar#setProgress(int, boolean)} method. This does * not correspond directly to the actual progress -- only the visual state. */
private final FloatProperty<ProgressBar> VISUAL_PROGRESS = new FloatProperty<ProgressBar>("visual_progress") { @Override public void setValue(ProgressBar object, float value) { object.setVisualProgress(R.id.progress, value); object.mVisualProgress = value; } @Override public Float get(ProgressBar object) { return object.mVisualProgress; } }; }