diff options
| author | Alan Viverette <alanv@google.com> | 2014-06-17 22:12:18 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-06-17 20:42:58 +0000 |
| commit | 1f681448c6b7db451c31af7d61c0b85b7b5af04f (patch) | |
| tree | 797449e7721c34a3823851cfa0515b4b406f0353 /core/java | |
| parent | 6170cca05eb13cfb44c8d13e7a447cd24f27a62c (diff) | |
| parent | 911743652b597057a1bd7ef8a921e9ff8dce0f4a (diff) | |
| download | frameworks_base-1f681448c6b7db451c31af7d61c0b85b7b5af04f.zip frameworks_base-1f681448c6b7db451c31af7d61c0b85b7b5af04f.tar.gz frameworks_base-1f681448c6b7db451c31af7d61c0b85b7b5af04f.tar.bz2 | |
Merge "Add attributes and accessors for tinting View drawables"
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/view/View.java | 106 | ||||
| -rw-r--r-- | core/java/android/widget/AbsSeekBar.java | 108 | ||||
| -rw-r--r-- | core/java/android/widget/CompoundButton.java | 135 | ||||
| -rw-r--r-- | core/java/android/widget/FrameLayout.java | 121 | ||||
| -rw-r--r-- | core/java/android/widget/ImageView.java | 122 | ||||
| -rw-r--r-- | core/java/android/widget/ProgressBar.java | 553 |
6 files changed, 1073 insertions, 72 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d780c88..9156216 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -44,6 +45,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; @@ -3187,6 +3189,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_") private Drawable mBackground; + private ColorStateList mBackgroundTint = null; + private PorterDuff.Mode mBackgroundTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasBackgroundTint = false; /** * Display list used for backgrounds. @@ -4022,6 +4027,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, a.getResourceId(attr, 0))); break; + case R.styleable.View_backgroundTint: + // This will get applied later during setBackground(). + mBackgroundTint = a.getColorStateList(R.styleable.View_backgroundTint); + mHasBackgroundTint = true; + break; + case R.styleable.View_backgroundTintMode: + // This will get applied later during setBackground(). + mBackgroundTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.View_backgroundTintMode, -1), mBackgroundTintMode); + break; } } @@ -15698,7 +15713,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - Drawable d= null; + Drawable d = null; if (resid != 0) { d = mContext.getDrawable(resid); } @@ -15774,8 +15789,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Compare the minimum sizes of the old Drawable and the new. If there isn't an old or // if it has a different minimum size, we should layout again - if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() || - mBackground.getMinimumWidth() != background.getMinimumWidth()) { + if (mBackground == null + || mBackground.getMinimumHeight() != background.getMinimumHeight() + || mBackground.getMinimumWidth() != background.getMinimumWidth()) { requestLayout = true; } @@ -15786,6 +15802,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, background.setVisible(getVisibility() == VISIBLE, false); mBackground = background; + applyBackgroundTint(); + if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) { mPrivateFlags &= ~PFLAG_SKIP_DRAW; mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND; @@ -15841,6 +15859,88 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Applies a tint to the background drawable. + * <p> + * Subsequent calls to {@link #setBackground(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#View_backgroundTint + * @attr ref android.R.styleable#View_backgroundTintMode + * @see Drawable#setTint(ColorStateList, PorterDuff.Mode) + */ + private void setBackgroundTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mBackgroundTint = tint; + mBackgroundTintMode = tintMode; + mHasBackgroundTint = true; + + applyBackgroundTint(); + } + + /** + * Applies a tint to the background drawable. Does not modify the current tint + * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default. + * <p> + * Subsequent calls to {@link #setBackground(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#View_backgroundTint + * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + public void setBackgroundTint(@Nullable ColorStateList tint) { + setBackgroundTint(tint, mBackgroundTintMode); + } + + /** + * @return the tint applied to the background drawable + * @attr ref android.R.styleable#View_backgroundTint + * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getBackgroundTint() { + return mBackgroundTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setBackgroundTint(ColorStateList)}} to the background drawable. + * 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#View_backgroundTintMode + * @see #setBackgroundTint(ColorStateList) + */ + public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + setBackgroundTint(mBackgroundTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the background drawable + * @attr ref android.R.styleable#View_backgroundTintMode + * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getBackgroundTintMode() { + return mBackgroundTintMode; + } + + private void applyBackgroundTint() { + if (mBackground != null && mHasBackgroundTint) { + mBackground = mBackground.mutate(); + mBackground.setTint(mBackgroundTint, mBackgroundTintMode); + } + } + + /** * Sets the padding. The view may add on the space required to display * the scrollbars, depending on the style and visibility of the scrollbars. * So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop}, diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 08931fe..eb3f882 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -16,11 +16,15 @@ package android.widget; +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.Insets; +import android.graphics.PorterDuff; import android.graphics.Rect; +import android.graphics.PorterDuff.Mode; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -31,10 +35,16 @@ import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import com.android.internal.R; + public abstract class AbsSeekBar extends ProgressBar { private final Rect mTempRect = new Rect(); private Drawable mThumb; + private ColorStateList mThumbTint = null; + private PorterDuff.Mode mThumbTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasThumbTint = false; + private int mThumbOffset; private boolean mSplitTrack; @@ -83,6 +93,15 @@ public abstract class AbsSeekBar extends ProgressBar { final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); setThumb(thumb); + if (a.hasValue(R.styleable.SeekBar_thumbTint)) { + mThumbTint = a.getColorStateList(R.styleable.SeekBar_thumbTint); + mThumbTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode); + mHasThumbTint = true; + + applyThumbTint(); + } + // Guess thumb offset if thumb != null, but allow layout to override. final int thumbOffset = a.getDimensionPixelOffset( com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); @@ -108,7 +127,7 @@ public abstract class AbsSeekBar extends ProgressBar { * @param thumb Drawable representing the thumb */ public void setThumb(Drawable thumb) { - boolean needUpdate; + final boolean needUpdate; // This way, calling setThumb again with the same bitmap will result in // it recalcuating mThumbOffset (if for example it the bounds of the // drawable changed) @@ -118,6 +137,7 @@ public abstract class AbsSeekBar extends ProgressBar { } else { needUpdate = false; } + if (thumb != null) { thumb.setCallback(this); if (canResolveLayoutDirection()) { @@ -136,8 +156,12 @@ public abstract class AbsSeekBar extends ProgressBar { requestLayout(); } } + mThumb = thumb; + + applyThumbTint(); invalidate(); + if (needUpdate) { updateThumbAndTrackPos(getWidth(), getHeight()); if (thumb != null && thumb.isStateful()) { @@ -160,6 +184,88 @@ public abstract class AbsSeekBar extends ProgressBar { } /** + * Applies a tint to the thumb drawable. + * <p> + * Subsequent calls to {@link #setThumb(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#SeekBar_thumbTint + * @attr ref android.R.styleable#SeekBar_thumbTintMode + * @see Drawable#setTint(ColorStateList, PorterDuff.Mode) + */ + private void setThumbTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mThumbTint = tint; + mThumbTintMode = tintMode; + mHasThumbTint = true; + + applyThumbTint(); + } + + /** + * Applies a tint to the thumb drawable. Does not modify the current tint + * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default. + * <p> + * Subsequent calls to {@link #setThumb(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#SeekBar_thumbTint + * @see #setThumbTint(ColorStateList, PorterDuff.Mode) + */ + public void setThumbTint(@Nullable ColorStateList tint) { + setThumbTint(tint, mThumbTintMode); + } + + /** + * @return the tint applied to the thumb drawable + * @attr ref android.R.styleable#SeekBar_thumbTint + * @see #setThumbTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getThumbTint() { + return mThumbTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setThumbTint(ColorStateList)}} to the thumb drawable. 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#SeekBar_thumbTintMode + * @see #setThumbTint(ColorStateList) + */ + public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) { + setThumbTint(mThumbTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the thumb drawable + * @attr ref android.R.styleable#SeekBar_thumbTintMode + * @see #setThumbTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getThumbTintMode() { + return mThumbTintMode; + } + + private void applyThumbTint() { + if (mThumb != null && mHasThumbTint) { + mThumb = mThumb.mutate(); + mThumb.setTint(mThumbTint, mThumbTintMode); + } + } + + /** * @see #setThumbOffset(int) */ public int getThumbOffset() { diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index c934ad7..747d2b1 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -16,11 +16,15 @@ package android.widget; +import android.annotation.Nullable; +import android.graphics.PorterDuff; import com.android.internal.R; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -48,7 +52,12 @@ public abstract class CompoundButton extends Button implements Checkable { private boolean mChecked; private int mButtonResource; private boolean mBroadcasting; + private Drawable mButtonDrawable; + private ColorStateList mButtonTint = null; + private PorterDuff.Mode mButtonTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasButtonTint = false; + private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener; @@ -74,13 +83,22 @@ public abstract class CompoundButton extends Button implements Checkable { final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes); - Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); + final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); if (d != null) { setButtonDrawable(d); } - boolean checked = a - .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false); + if (a.hasValue(R.styleable.CompoundButton_buttonTint)) { + mButtonTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint); + mButtonTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode); + mHasButtonTint = true; + + applyButtonTint(); + } + + final boolean checked = a.getBoolean( + com.android.internal.R.styleable.CompoundButton_checked, false); setChecked(checked); a.recycle(); @@ -173,9 +191,11 @@ public abstract class CompoundButton extends Button implements Checkable { } /** - * Set the background to a given Drawable, identified by its resource id. + * Set the button graphic to a given Drawable, identified by its resource + * id. * - * @param resid the resource id of the drawable to use as the background + * @param resid the resource id of the drawable to use as the button + * graphic */ public void setButtonDrawable(int resid) { if (resid != 0 && resid == mButtonResource) { @@ -192,23 +212,114 @@ public abstract class CompoundButton extends Button implements Checkable { } /** - * Set the background to a given Drawable + * Set the button graphic to a given Drawable * - * @param d The Drawable to use as the background + * @param d The Drawable to use as the button graphic */ public void setButtonDrawable(Drawable d) { - if (d != null) { + if (mButtonDrawable != d) { if (mButtonDrawable != null) { mButtonDrawable.setCallback(null); unscheduleDrawable(mButtonDrawable); } - d.setCallback(this); - d.setVisible(getVisibility() == VISIBLE, false); + mButtonDrawable = d; - setMinHeight(mButtonDrawable.getIntrinsicHeight()); + + if (d != null) { + d.setCallback(this); + d.setLayoutDirection(getLayoutDirection()); + if (d.isStateful()) { + d.setState(getDrawableState()); + } + d.setVisible(getVisibility() == VISIBLE, false); + setMinHeight(d.getIntrinsicHeight()); + applyButtonTint(); + } } + } - refreshDrawableState(); + /** + * Applies a tint to the button drawable. + * <p> + * Subsequent calls to {@link #setButtonDrawable(Drawable)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using + * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#CompoundButton_buttonTint + * @attr ref android.R.styleable#CompoundButton_buttonTintMode + * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + private void setButtonTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mButtonTint = tint; + mButtonTintMode = tintMode; + mHasButtonTint = true; + + applyButtonTint(); + } + + /** + * Applies a tint to the button drawable. Does not modify the current tint + * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default. + * <p> + * Subsequent calls to {@link #setButtonDrawable(Drawable)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using + * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#CompoundButton_buttonTint + * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + public void setButtonTint(@Nullable ColorStateList tint) { + setButtonTint(tint, mButtonTintMode); + } + + /** + * @return the tint applied to the button drawable + * @attr ref android.R.styleable#CompoundButton_buttonTint + * @see #setButtonTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getButtonTint() { + return mButtonTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setButtonTint(ColorStateList)}} to the button drawable. 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#CompoundButton_buttonTintMode + * @see #setButtonTint(ColorStateList) + */ + public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) { + setButtonTint(mButtonTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the button drawable + * @attr ref android.R.styleable#CompoundButton_buttonTintMode + * @see #setButtonTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getButtonTintMode() { + return mButtonTintMode; + } + + private void applyButtonTint() { + if (mButtonDrawable != null && mHasButtonTint) { + mButtonDrawable = mButtonDrawable.mutate(); + mButtonDrawable.setTint(mButtonTint, mButtonTintMode); + } } @Override diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 01a6b8a..5a14929 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -18,11 +18,15 @@ package android.widget; import java.util.ArrayList; +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.Region; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -33,6 +37,8 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; +import com.android.internal.R; + /** * FrameLayout is designed to block out an area on the screen to display @@ -62,6 +68,9 @@ public class FrameLayout extends ViewGroup { @ViewDebug.ExportedProperty(category = "drawing") private Drawable mForeground; + private ColorStateList mForegroundTint = null; + private PorterDuff.Mode mForegroundTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasForegroundTint = false; @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingLeft = 0; @@ -119,6 +128,15 @@ public class FrameLayout extends ViewGroup { setMeasureAllChildren(true); } + if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) { + mForegroundTint = a.getColorStateList(R.styleable.FrameLayout_foregroundTint); + mForegroundTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode); + mHasForegroundTint = true; + + applyForegroundTint(); + } + mForegroundInPadding = a.getBoolean( com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true); @@ -231,32 +249,34 @@ public class FrameLayout extends ViewGroup { * into account by ensuring that the children are inset to be placed * inside of the padding area. * - * @param drawable The Drawable to be drawn on top of the children. + * @param d The Drawable to be drawn on top of the children. * * @attr ref android.R.styleable#FrameLayout_foreground */ - public void setForeground(Drawable drawable) { - if (mForeground != drawable) { + public void setForeground(Drawable d) { + if (mForeground != d) { if (mForeground != null) { mForeground.setCallback(null); unscheduleDrawable(mForeground); } - mForeground = drawable; + mForeground = d; mForegroundPaddingLeft = 0; mForegroundPaddingTop = 0; mForegroundPaddingRight = 0; mForegroundPaddingBottom = 0; - if (drawable != null) { + if (d != null) { setWillNotDraw(false); - drawable.setCallback(this); - if (drawable.isStateful()) { - drawable.setState(getDrawableState()); + d.setCallback(this); + d.setLayoutDirection(getLayoutDirection()); + if (d.isStateful()) { + d.setState(getDrawableState()); } + applyForegroundTint(); if (mForegroundGravity == Gravity.FILL) { Rect padding = new Rect(); - if (drawable.getPadding(padding)) { + if (d.getPadding(padding)) { mForegroundPaddingLeft = padding.left; mForegroundPaddingTop = padding.top; mForegroundPaddingRight = padding.right; @@ -281,6 +301,89 @@ public class FrameLayout extends ViewGroup { return mForeground; } + /** + * Applies a tint to the foreground drawable. + * <p> + * Subsequent calls to {@link #setForeground(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#FrameLayout_foregroundTint + * @attr ref android.R.styleable#FrameLayout_foregroundTintMode + * @see Drawable#setTint(ColorStateList, PorterDuff.Mode) + */ + private void setForegroundTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mForegroundTint = tint; + mForegroundTintMode = tintMode; + mHasForegroundTint = true; + + applyForegroundTint(); + } + + /** + * Applies a tint to the foreground drawable. Does not modify the current + * tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default. + * <p> + * Subsequent calls to {@link #setForeground(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#FrameLayout_foregroundTint + * @see #setForegroundTint(ColorStateList, PorterDuff.Mode) + */ + public void setForegroundTint(@Nullable ColorStateList tint) { + setForegroundTint(tint, mForegroundTintMode); + } + + /** + * @return the tint applied to the foreground drawable + * @attr ref android.R.styleable#FrameLayout_foregroundTint + * @see #setForegroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getForegroundTint() { + return mForegroundTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setForegroundTint(ColorStateList)}} to the foreground drawable. + * 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#FrameLayout_foregroundTintMode + * @see #setForegroundTint(ColorStateList) + */ + public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) { + setForegroundTint(mForegroundTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the foreground + * drawable + * @attr ref android.R.styleable#FrameLayout_foregroundTintMode + * @see #setForegroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getForegroundTintMode() { + return mForegroundTintMode; + } + + private void applyForegroundTint() { + if (mForeground != null && mHasForegroundTint) { + mForeground = mForeground.mutate(); + mForeground.setTint(mForegroundTint, mForegroundTintMode); + } + } + int getPaddingLeftWithForeground() { return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) : mPaddingLeft + mForegroundPaddingLeft; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index a40b85e..399e087 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -16,8 +16,10 @@ package android.widget; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -30,6 +32,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Xfermode; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -44,6 +47,8 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; +import com.android.internal.R; + import java.io.IOException; import java.io.InputStream; @@ -75,13 +80,18 @@ public class ImageView extends View { private int mMaxHeight = Integer.MAX_VALUE; // these are applied to the drawable - private ColorFilter mColorFilter; + private ColorFilter mColorFilter = null; + private boolean mHasColorFilter = false; private Xfermode mXfermode; private int mAlpha = 255; private int mViewAlphaScale = 256; private boolean mColorMod = false; private Drawable mDrawable = null; + private ColorStateList mDrawableTint = null; + private PorterDuff.Mode mDrawableTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasDrawableTint = false; + private int[] mState = null; private boolean mMergeState = false; private int mLevel = 0; @@ -154,17 +164,21 @@ public class ImageView extends View { setMaxHeight(a.getDimensionPixelSize( com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE)); - int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1); + final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1); if (index >= 0) { setScaleType(sScaleTypeArray[index]); } - int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0); - if (tint != 0) { - setColorFilter(tint); + if (a.hasValue(R.styleable.ImageView_tint)) { + mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint); + mDrawableTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.ImageView_tintMode, -1), mDrawableTintMode); + mHasDrawableTint = true; + + applyDrawableTint(); } - - int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255); + + final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255); if (alpha != 255) { setAlpha(alpha); } @@ -435,6 +449,88 @@ public class ImageView extends View { } /** + * Applies a tint to the image drawable. + * <p> + * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#ImageView_tint + * @attr ref android.R.styleable#ImageView_tintMode + * @see Drawable#setTint(ColorStateList, PorterDuff.Mode) + */ + private void setTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mDrawableTint = tint; + mDrawableTintMode = tintMode; + mHasDrawableTint = true; + + applyDrawableTint(); + } + + /** + * Applies a tint to the image drawable. Does not modify the current tint + * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default. + * <p> + * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically + * mutate the drawable and apply the specified tint and tint mode using + * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#ImageView_tint + * @see Drawable#setTint(ColorStateList, PorterDuff.Mode) + */ + public void setTint(@Nullable ColorStateList tint) { + setTint(tint, mDrawableTintMode); + } + + /** + * @return the tint applied to the image drawable + * @attr ref android.R.styleable#ImageView_tint + * @see #setTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getTint() { + return mDrawableTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setTint(ColorStateList)}} to the image drawable. 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#ImageView_tintMode + * @see #setTint(ColorStateList) + */ + public void setTintMode(@Nullable PorterDuff.Mode tintMode) { + setTint(mDrawableTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the image drawable + * @attr ref android.R.styleable#ImageView_tintMode + * @see #setTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getTintMode() { + return mDrawableTintMode; + } + + private void applyDrawableTint() { + if (mDrawable != null && mHasDrawableTint) { + mDrawable = mDrawable.mutate(); + mDrawable.setTint(mDrawableTint, mDrawableTintMode); + } + } + + /** * Sets a Bitmap as the content of this ImageView. * * @param bm The bitmap to set @@ -709,17 +805,20 @@ public class ImageView extends View { mDrawable.setCallback(null); unscheduleDrawable(mDrawable); } + mDrawable = d; + if (d != null) { d.setCallback(this); + d.setLayoutDirection(getLayoutDirection()); if (d.isStateful()) { d.setState(getDrawableState()); } - d.setLevel(mLevel); - d.setLayoutDirection(getLayoutDirection()); d.setVisible(getVisibility() == VISIBLE, true); + d.setLevel(mLevel); mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); + applyDrawableTint(); applyColorMod(); configureBounds(); } else { @@ -1177,6 +1276,7 @@ public class ImageView extends View { public void setColorFilter(ColorFilter cf) { if (mColorFilter != cf) { mColorFilter = cf; + mHasColorFilter = true; mColorMod = true; applyColorMod(); invalidate(); @@ -1231,7 +1331,9 @@ public class ImageView extends View { // re-applied if the Drawable is changed. if (mDrawable != null && mColorMod) { mDrawable = mDrawable.mutate(); - mDrawable.setColorFilter(mColorFilter); + if (mHasColorFilter) { + mDrawable.setColorFilter(mColorFilter); + } mDrawable.setXfermode(mXfermode); mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index af32f1c..62a8bec 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -16,13 +16,17 @@ package android.widget; +import android.annotation.Nullable; +import android.graphics.PorterDuff; import com.android.internal.R; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; +import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Animatable; @@ -210,8 +214,26 @@ public class ProgressBar extends View { private Transformation mTransformation; private AlphaAnimation mAnimation; private boolean mHasAnimation; + private Drawable mIndeterminateDrawable; + private ColorStateList mIndeterminateTint = null; + private PorterDuff.Mode mIndeterminateTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasIndeterminateTint = false; + private Drawable mProgressDrawable; + + private ColorStateList mProgressTint = null; + private PorterDuff.Mode mProgressTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasProgressTint = false; + + private ColorStateList mProgressBackgroundTint = null; + private PorterDuff.Mode mProgressBackgroundTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasProgressBackgroundTint = false; + + private ColorStateList mSecondaryProgressTint = null; + private PorterDuff.Mode mSecondaryProgressTintMode = PorterDuff.Mode.SRC_ATOP; + private boolean mHasSecondaryProgressTint = false; + private Drawable mCurrentDrawable; Bitmap mSampleTile; private boolean mNoInvalidate; @@ -257,11 +279,11 @@ public class ProgressBar extends View { mNoInvalidate = true; - Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); - if (drawable != null) { + final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); + if (progressDrawable != null) { // Calling this method can set mMaxHeight, make sure the corresponding // XML attribute for mMaxHeight is read after calling this method - setProgressDrawableTiled(drawable); + setProgressDrawableTiled(progressDrawable); } @@ -288,9 +310,10 @@ public class ProgressBar extends View { setSecondaryProgress( a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); - drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable); - if (drawable != null) { - setIndeterminateDrawableTiled(drawable); + final Drawable indeterminateDrawable = a.getDrawable( + R.styleable.ProgressBar_indeterminateDrawable); + if (indeterminateDrawable != null) { + setIndeterminateDrawableTiled(indeterminateDrawable); } mOnlyIndeterminate = a.getBoolean( @@ -303,6 +326,53 @@ public class ProgressBar extends View { mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); + if (a.hasValue(R.styleable.ProgressBar_progressTint)) { + mProgressTint = a.getColorStateList( + R.styleable.ProgressBar_progressTint); + mProgressTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.ProgressBar_progressBackgroundTintMode, -1), + mProgressTintMode); + mHasProgressTint = true; + + applyProgressLayerTint(R.id.progress, mProgressTint, + mProgressTintMode, true); + } + + if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) { + mProgressBackgroundTint = a.getColorStateList( + R.styleable.ProgressBar_progressBackgroundTint); + mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.ProgressBar_progressTintMode, -1), + mProgressBackgroundTintMode); + mHasProgressBackgroundTint = true; + + applyProgressLayerTint(R.id.background, mProgressBackgroundTint, + mProgressBackgroundTintMode, false); + } + + if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) { + mSecondaryProgressTint = a.getColorStateList( + R.styleable.ProgressBar_secondaryProgressTint); + mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.ProgressBar_secondaryProgressTintMode, -1), + mSecondaryProgressTintMode); + mHasSecondaryProgressTint = true; + + applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint, + mSecondaryProgressTintMode, false); + } + + if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) { + mIndeterminateTint = a.getColorStateList( + R.styleable.ProgressBar_indeterminateTint); + mIndeterminateTintMode = Drawable.parseTintMode(a.getInt( + R.styleable.ProgressBar_indeterminateTintMode, -1), + mIndeterminateTintMode); + mHasIndeterminateTint = true; + + applyIndeterminateTint(); + } + a.recycle(); // If not explicitly specified this view is important for accessibility. @@ -479,16 +549,111 @@ public class ProgressBar extends View { * @see #setIndeterminate(boolean) */ public void setIndeterminateDrawable(Drawable d) { - if (d != null) { - d.setCallback(this); - } - mIndeterminateDrawable = d; - if (mIndeterminateDrawable != null && canResolveLayoutDirection()) { - mIndeterminateDrawable.setLayoutDirection(getLayoutDirection()); + 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) { + mCurrentDrawable = d; + postInvalidate(); + } } - if (mIndeterminate) { - mCurrentDrawable = d; - postInvalidate(); + } + + /** + * Applies a tint to the indeterminate drawable. + * <p> + * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will + * automatically mutate the drawable and apply the specified tint and + * tint mode using + * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_indeterminateTint + * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode + * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + private void setIndeterminateTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mIndeterminateTint = tint; + mIndeterminateTintMode = tintMode; + mHasIndeterminateTint = true; + + applyIndeterminateTint(); + } + + /** + * Applies a tint to the indeterminate drawable. Does not modify the + * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} 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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_indeterminateTint + * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode) + */ + public void setIndeterminateTint(@Nullable ColorStateList tint) { + setIndeterminateTint(tint, mIndeterminateTintMode); + } + + /** + * @return the tint applied to the indeterminate drawable + * @attr ref android.R.styleable#ProgressBar_indeterminateTint + * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getIndeterminateTint() { + return mIndeterminateTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setIndeterminateTint(ColorStateList)} to the indeterminate + * drawable. 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_indeterminateTintMode + * @see #setIndeterminateTint(ColorStateList) + */ + public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { + setIndeterminateTint(mIndeterminateTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the indeterminate drawable + * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode + * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getIndeterminateTintMode() { + return mIndeterminateTintMode; + } + + private void applyIndeterminateTint() { + if (mIndeterminateDrawable != null && mHasIndeterminateTint) { + mIndeterminateDrawable = mIndeterminateDrawable.mutate(); + mIndeterminateDrawable.setTint(mIndeterminateTint, mIndeterminateTintMode); } } @@ -532,42 +697,340 @@ public class ProgressBar extends View { * @see #setIndeterminate(boolean) */ public void setProgressDrawable(Drawable d) { - boolean needUpdate; - if (mProgressDrawable != null && d != mProgressDrawable) { - mProgressDrawable.setCallback(null); - needUpdate = true; - } else { - needUpdate = false; - } + if (mProgressDrawable != d) { + if (mProgressDrawable != null) { + mProgressDrawable.setCallback(null); + unscheduleDrawable(mProgressDrawable); + } - if (d != null) { - d.setCallback(this); - if (canResolveLayoutDirection()) { + 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(); + } + + if (mHasProgressTint) { + applyProgressLayerTint(R.id.progress, mProgressTint, mProgressTintMode, true); + } + + if (mHasProgressBackgroundTint) { + applyProgressLayerTint(R.id.background, mProgressBackgroundTint, + mProgressBackgroundTintMode, false); + } + + if (mHasSecondaryProgressTint) { + applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint, + mSecondaryProgressTintMode, false); + } } - // Make sure the ProgressBar is always tall enough - int drawableHeight = d.getMinimumHeight(); - if (mMaxHeight < drawableHeight) { - mMaxHeight = drawableHeight; - requestLayout(); + if (!mIndeterminate) { + mCurrentDrawable = d; + postInvalidate(); } - } - mProgressDrawable = d; - if (!mIndeterminate) { - mCurrentDrawable = d; - postInvalidate(); - } - if (needUpdate) { updateDrawableBounds(getWidth(), getHeight()); updateDrawableState(); + doRefreshProgress(R.id.progress, mProgress, false, false); doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false); } } /** + * Applies a tint to the progress indicator, if one exists, or to the + * entire progress drawable otherwise. + * <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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_progressTint + * @attr ref android.R.styleable#ProgressBar_progressTintMode + * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + private void setProgressTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mProgressTint = tint; + mProgressTintMode = tintMode; + mHasProgressTint = true; + + applyProgressLayerTint(R.id.progress, tint, tintMode, true); + } + + /** + * 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_ATOP} 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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_progressTint + * @see #setProgressTint(ColorStateList) + */ + public void setProgressTint(@Nullable ColorStateList tint) { + setProgressTint(tint, mProgressTintMode); + } + + /** + * @return the tint applied to the progress drawable + * @attr ref android.R.styleable#ProgressBar_progressTint + * @see #setProgressTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getProgressTint() { + return mProgressTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setProgressTint(ColorStateList)}} to the 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_progressTintMode + * @see #setProgressTint(ColorStateList) + */ + public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + setProgressTint(mProgressTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the progress drawable + * @attr ref android.R.styleable#ProgressBar_progressTintMode + * @see #setProgressTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getProgressTintMode() { + return mProgressTintMode; + } + + /** + * Applies a tint to the progress background, if one exists. + * <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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint + * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode + * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + private void setProgressBackgroundTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mProgressBackgroundTint = tint; + mProgressBackgroundTintMode = tintMode; + mHasProgressBackgroundTint = true; + + applyProgressLayerTint(R.id.background, tint, tintMode, false); + } + + /** + * 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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint + * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + public void setProgressBackgroundTint(@Nullable ColorStateList tint) { + setProgressBackgroundTint(tint, mProgressBackgroundTintMode); + } + + /** + * @return the tint applied to the progress background + * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint + * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getProgressBackgroundTint() { + return mProgressBackgroundTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setProgressBackgroundTint(ColorStateList)}} to the progress + * background. 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_progressBackgroundTintMode + * @see #setProgressBackgroundTint(ColorStateList) + */ + public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + setProgressBackgroundTint(mProgressBackgroundTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the progress + * background + * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode + * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getProgressBackgroundTintMode() { + return mProgressBackgroundTintMode; + } + + /** + * Applies a tint to the secondary progress indicator, if one exists. + * <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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * @param tintMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint + * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode + * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode) + */ + private void setSecondaryProgressTint(@Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode) { + mSecondaryProgressTint = tint; + mSecondaryProgressTintMode = tintMode; + mHasSecondaryProgressTint = true; + + applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false); + } + + /** + * 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#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint + * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode) + */ + public void setSecondaryProgressTint(@Nullable ColorStateList tint) { + setSecondaryProgressTint(tint, mSecondaryProgressTintMode); + } + + /** + * @return the tint applied to the secondary progress drawable + * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint + * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public ColorStateList getSecondaryProgressTint() { + return mSecondaryProgressTint; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setSecondaryProgressTint(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 #setSecondaryProgressTint(ColorStateList) + */ + public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + setSecondaryProgressTint(mSecondaryProgressTint, tintMode); + } + + /** + * @return the blending mode used to apply the tint to the secondary + * progress drawable + * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode + * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode) + */ + @Nullable + public PorterDuff.Mode getSecondaryProgressTintMode() { + return mSecondaryProgressTintMode; + } + + private void applyProgressLayerTint(int layerId, @Nullable ColorStateList tint, + @Nullable PorterDuff.Mode tintMode, boolean shouldFallback) { + final Drawable d = mProgressDrawable; + if (d != null) { + mProgressDrawable = d.mutate(); + + Drawable layer = null; + if (d instanceof LayerDrawable) { + layer = ((LayerDrawable) d).findDrawableByLayerId(layerId); + } + + if (shouldFallback && layer == null) { + layer = d; + } + + if (layer != null) { + layer.setTint(tint, tintMode); + } + } + } + + /** * Define the tileable drawable used to draw the progress bar in * progress mode. * <p> @@ -670,6 +1133,22 @@ public class ProgressBar extends View { } } + private void setDrawableTint(int id, ColorStateList tint, Mode tintMode, boolean fallback) { + Drawable layer = null; + + // We expect a layer drawable, so try to find the target ID. + final Drawable d = mCurrentDrawable; + if (d instanceof LayerDrawable) { + layer = ((LayerDrawable) d).findDrawableByLayerId(id); + } + + if (fallback && layer == null) { + layer = d; + } + + layer.mutate().setTint(tint, tintMode); + } + private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp) { float scale = mMax > 0 ? (float) progress / (float) mMax : 0; |
