summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt6
-rw-r--r--core/java/android/widget/TextView.java415
-rw-r--r--core/res/res/values/attrs.xml22
-rw-r--r--core/res/res/values/public.xml2
4 files changed, 295 insertions, 150 deletions
diff --git a/api/current.txt b/api/current.txt
index 3eff5e41063a..0e0c50bf4228 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -502,6 +502,8 @@ package android {
field public static final int drawablePadding = 16843121; // 0x1010171
field public static final int drawableRight = 16843120; // 0x1010170
field public static final int drawableStart = 16843666; // 0x1010392
+ field public static final int drawableTint = 16843990; // 0x10104d6
+ field public static final int drawableTintMode = 16843991; // 0x10104d7
field public static final int drawableTop = 16843117; // 0x101016d
field public static final int drawingCacheQuality = 16842984; // 0x10100e8
field public static final int dropDownAnchor = 16843363; // 0x1010263
@@ -39008,6 +39010,8 @@ package android.widget {
method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
method public final int getAutoLinkMask();
method public int getCompoundDrawablePadding();
+ method public android.content.res.ColorStateList getCompoundDrawableTintList();
+ method public android.graphics.PorterDuff.Mode getCompoundDrawableTintMode();
method public android.graphics.drawable.Drawable[] getCompoundDrawables();
method public android.graphics.drawable.Drawable[] getCompoundDrawablesRelative();
method public int getCompoundPaddingBottom();
@@ -39107,6 +39111,8 @@ package android.widget {
method public void setAllCaps(boolean);
method public final void setAutoLinkMask(int);
method public void setCompoundDrawablePadding(int);
+ method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
+ method public void setCompoundDrawableTintMode(android.graphics.PorterDuff.Mode);
method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
method public void setCompoundDrawablesRelative(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
method public void setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cc8a90c40dc5..92ca42718ee3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -32,6 +32,7 @@ import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
@@ -214,6 +215,8 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawablePadding
+ * @attr ref android.R.styleable#TextView_drawableTint
+ * @attr ref android.R.styleable#TextView_drawableTintMode
* @attr ref android.R.styleable#TextView_lineSpacingExtra
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
* @attr ref android.R.styleable#TextView_marqueeRepeatLimit
@@ -298,7 +301,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private float mShadowRadius, mShadowDx, mShadowDy;
private int mShadowColor;
-
private boolean mPreDrawRegistered;
private boolean mPreDrawListenerDetached;
@@ -312,16 +314,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private TextUtils.TruncateAt mEllipsize;
static class Drawables {
- final static int DRAWABLE_NONE = -1;
- final static int DRAWABLE_RIGHT = 0;
- final static int DRAWABLE_LEFT = 1;
+ static final int LEFT = 0;
+ static final int TOP = 1;
+ static final int RIGHT = 2;
+ static final int BOTTOM = 3;
+
+ static final int DRAWABLE_NONE = -1;
+ static final int DRAWABLE_RIGHT = 0;
+ static final int DRAWABLE_LEFT = 1;
final Rect mCompoundRect = new Rect();
- Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
- mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
+ final Drawable[] mShowing = new Drawable[4];
+ ColorStateList mTintList;
+ PorterDuff.Mode mTintMode;
+ boolean mHasTint;
+ boolean mHasTintMode;
+
+ Drawable mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
Drawable mDrawableLeftInitial, mDrawableRightInitial;
+
boolean mIsRtlCompatibilityMode;
boolean mOverride;
@@ -344,19 +357,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void resolveWithLayoutDirection(int layoutDirection) {
// First reset "left" and "right" drawables to their initial values
- mDrawableLeft = mDrawableLeftInitial;
- mDrawableRight = mDrawableRightInitial;
+ mShowing[Drawables.LEFT] = mDrawableLeftInitial;
+ mShowing[Drawables.RIGHT] = mDrawableRightInitial;
if (mIsRtlCompatibilityMode) {
// Use "start" drawable as "left" drawable if the "left" drawable was not defined
- if (mDrawableStart != null && mDrawableLeft == null) {
- mDrawableLeft = mDrawableStart;
+ if (mDrawableStart != null && mShowing[Drawables.LEFT] == null) {
+ mShowing[Drawables.LEFT] = mDrawableStart;
mDrawableSizeLeft = mDrawableSizeStart;
mDrawableHeightLeft = mDrawableHeightStart;
}
// Use "end" drawable as "right" drawable if the "right" drawable was not defined
- if (mDrawableEnd != null && mDrawableRight == null) {
- mDrawableRight = mDrawableEnd;
+ if (mDrawableEnd != null && mShowing[Drawables.RIGHT] == null) {
+ mShowing[Drawables.RIGHT] = mDrawableEnd;
mDrawableSizeRight = mDrawableSizeEnd;
mDrawableHeightRight = mDrawableHeightEnd;
}
@@ -366,11 +379,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
switch(layoutDirection) {
case LAYOUT_DIRECTION_RTL:
if (mOverride) {
- mDrawableRight = mDrawableStart;
+ mShowing[Drawables.RIGHT] = mDrawableStart;
mDrawableSizeRight = mDrawableSizeStart;
mDrawableHeightRight = mDrawableHeightStart;
- mDrawableLeft = mDrawableEnd;
+ mShowing[Drawables.LEFT] = mDrawableEnd;
mDrawableSizeLeft = mDrawableSizeEnd;
mDrawableHeightLeft = mDrawableHeightEnd;
}
@@ -379,11 +392,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case LAYOUT_DIRECTION_LTR:
default:
if (mOverride) {
- mDrawableLeft = mDrawableStart;
+ mShowing[Drawables.LEFT] = mDrawableStart;
mDrawableSizeLeft = mDrawableSizeStart;
mDrawableHeightLeft = mDrawableHeightStart;
- mDrawableRight = mDrawableEnd;
+ mShowing[Drawables.RIGHT] = mDrawableEnd;
mDrawableSizeRight = mDrawableSizeEnd;
mDrawableHeightRight = mDrawableHeightEnd;
}
@@ -395,17 +408,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void updateDrawablesLayoutDirection(int layoutDirection) {
- if (mDrawableLeft != null) {
- mDrawableLeft.setLayoutDirection(layoutDirection);
- }
- if (mDrawableRight != null) {
- mDrawableRight.setLayoutDirection(layoutDirection);
- }
- if (mDrawableTop != null) {
- mDrawableTop.setLayoutDirection(layoutDirection);
- }
- if (mDrawableBottom != null) {
- mDrawableBottom.setLayoutDirection(layoutDirection);
+ for (Drawable dr : mShowing) {
+ if (dr != null) {
+ dr.setLayoutDirection(layoutDirection);
+ }
}
}
@@ -415,10 +421,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
mDrawableError = dr;
- final Rect compoundRect = mCompoundRect;
- int[] state = tv.getDrawableState();
-
if (mDrawableError != null) {
+ final Rect compoundRect = mCompoundRect;
+ final int[] state = tv.getDrawableState();
+
mDrawableError.setState(state);
mDrawableError.copyBounds(compoundRect);
mDrawableError.setCallback(tv);
@@ -433,12 +439,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// first restore the initial state if needed
switch (mDrawableSaved) {
case DRAWABLE_LEFT:
- mDrawableLeft = mDrawableTemp;
+ mShowing[Drawables.LEFT] = mDrawableTemp;
mDrawableSizeLeft = mDrawableSizeTemp;
mDrawableHeightLeft = mDrawableHeightTemp;
break;
case DRAWABLE_RIGHT:
- mDrawableRight = mDrawableTemp;
+ mShowing[Drawables.RIGHT] = mDrawableTemp;
mDrawableSizeRight = mDrawableSizeTemp;
mDrawableHeightRight = mDrawableHeightTemp;
break;
@@ -451,11 +457,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case LAYOUT_DIRECTION_RTL:
mDrawableSaved = DRAWABLE_LEFT;
- mDrawableTemp = mDrawableLeft;
+ mDrawableTemp = mShowing[Drawables.LEFT];
mDrawableSizeTemp = mDrawableSizeLeft;
mDrawableHeightTemp = mDrawableHeightLeft;
- mDrawableLeft = mDrawableError;
+ mShowing[Drawables.LEFT] = mDrawableError;
mDrawableSizeLeft = mDrawableSizeError;
mDrawableHeightLeft = mDrawableHeightError;
break;
@@ -463,11 +469,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
default:
mDrawableSaved = DRAWABLE_RIGHT;
- mDrawableTemp = mDrawableRight;
+ mDrawableTemp = mShowing[Drawables.RIGHT];
mDrawableSizeTemp = mDrawableSizeRight;
mDrawableHeightTemp = mDrawableHeightRight;
- mDrawableRight = mDrawableError;
+ mShowing[Drawables.RIGHT] = mDrawableError;
mDrawableSizeRight = mDrawableSizeError;
mDrawableHeightRight = mDrawableHeightError;
break;
@@ -770,6 +776,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean selectallonfocus = false;
Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
drawableBottom = null, drawableStart = null, drawableEnd = null;
+ ColorStateList drawableTint = null;
+ PorterDuff.Mode drawableTintMode = null;
int drawablePadding = 0;
int ellipsize = -1;
boolean singleLine = false;
@@ -855,6 +863,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
drawableEnd = a.getDrawable(attr);
break;
+ case com.android.internal.R.styleable.TextView_drawableTint:
+ drawableTint = a.getColorStateList(attr);
+ break;
+
+ case com.android.internal.R.styleable.TextView_drawableTintMode:
+ drawableTintMode = Drawable.parseTintMode(a.getInt(attr, -1), drawableTintMode);
+ break;
+
case com.android.internal.R.styleable.TextView_drawablePadding:
drawablePadding = a.getDimensionPixelSize(attr, drawablePadding);
break;
@@ -1238,6 +1254,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
bufferType = BufferType.SPANNABLE;
}
+ // Set up the tint (if needed) before setting the drawables so that it
+ // gets applied correctly.
+ if (drawableTint != null || drawableTintMode != null) {
+ if (mDrawables == null) {
+ mDrawables = new Drawables(context);
+ }
+ if (drawableTint != null) {
+ mDrawables.mTintList = drawableTint;
+ mDrawables.mHasTint = true;
+ }
+ if (drawableTintMode != null) {
+ mDrawables.mTintMode = drawableTintMode;
+ mDrawables.mHasTintMode = true;
+ }
+ }
+
// This call will save the initial left/right drawables
setCompoundDrawablesWithIntrinsicBounds(
drawableLeft, drawableTop, drawableRight, drawableBottom);
@@ -1420,6 +1452,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
resetResolvedDrawables();
resolveDrawables();
+ applyCompoundDrawableTint();
}
}
@@ -1778,7 +1811,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public int getCompoundPaddingTop() {
final Drawables dr = mDrawables;
- if (dr == null || dr.mDrawableTop == null) {
+ if (dr == null || dr.mShowing[Drawables.TOP] == null) {
return mPaddingTop;
} else {
return mPaddingTop + dr.mDrawablePadding + dr.mDrawableSizeTop;
@@ -1791,7 +1824,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public int getCompoundPaddingBottom() {
final Drawables dr = mDrawables;
- if (dr == null || dr.mDrawableBottom == null) {
+ if (dr == null || dr.mShowing[Drawables.BOTTOM] == null) {
return mPaddingBottom;
} else {
return mPaddingBottom + dr.mDrawablePadding + dr.mDrawableSizeBottom;
@@ -1804,7 +1837,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public int getCompoundPaddingLeft() {
final Drawables dr = mDrawables;
- if (dr == null || dr.mDrawableLeft == null) {
+ if (dr == null || dr.mShowing[Drawables.LEFT] == null) {
return mPaddingLeft;
} else {
return mPaddingLeft + dr.mDrawablePadding + dr.mDrawableSizeLeft;
@@ -1817,7 +1850,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public int getCompoundPaddingRight() {
final Drawables dr = mDrawables;
- if (dr == null || dr.mDrawableRight == null) {
+ if (dr == null || dr.mShowing[Drawables.RIGHT] == null) {
return mPaddingRight;
} else {
return mPaddingRight + dr.mDrawablePadding + dr.mDrawableSizeRight;
@@ -2015,14 +2048,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else {
// We need to retain the last set padding, so just clear
// out all of the fields in the existing structure.
- if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null);
- dr.mDrawableLeft = null;
- if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null);
- dr.mDrawableTop = null;
- if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null);
- dr.mDrawableRight = null;
- if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null);
- dr.mDrawableBottom = null;
+ for (int i = dr.mShowing.length - 1; i >= 0; i--) {
+ if (dr.mShowing[i] != null) {
+ dr.mShowing[i].setCallback(null);
+ }
+ dr.mShowing[i] = null;
+ }
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
@@ -2036,25 +2067,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mDrawables.mOverride = false;
- if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) {
- dr.mDrawableLeft.setCallback(null);
+ if (dr.mShowing[Drawables.LEFT] != left && dr.mShowing[Drawables.LEFT] != null) {
+ dr.mShowing[Drawables.LEFT].setCallback(null);
}
- dr.mDrawableLeft = left;
+ dr.mShowing[Drawables.LEFT] = left;
- if (dr.mDrawableTop != top && dr.mDrawableTop != null) {
- dr.mDrawableTop.setCallback(null);
+ if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
+ dr.mShowing[Drawables.TOP].setCallback(null);
}
- dr.mDrawableTop = top;
+ dr.mShowing[Drawables.TOP] = top;
- if (dr.mDrawableRight != right && dr.mDrawableRight != null) {
- dr.mDrawableRight.setCallback(null);
+ if (dr.mShowing[Drawables.RIGHT] != right && dr.mShowing[Drawables.RIGHT] != null) {
+ dr.mShowing[Drawables.RIGHT].setCallback(null);
}
- dr.mDrawableRight = right;
+ dr.mShowing[Drawables.RIGHT] = right;
- if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) {
- dr.mDrawableBottom.setCallback(null);
+ if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
+ dr.mShowing[Drawables.BOTTOM].setCallback(null);
}
- dr.mDrawableBottom = bottom;
+ dr.mShowing[Drawables.BOTTOM] = bottom;
final Rect compoundRect = dr.mCompoundRect;
int[] state;
@@ -2110,6 +2141,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
resetResolvedDrawables();
resolveDrawables();
+ applyCompoundDrawableTint();
invalidate();
requestLayout();
}
@@ -2193,10 +2225,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// We're switching to relative, discard absolute.
if (dr != null) {
- if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null);
- dr.mDrawableLeft = dr.mDrawableLeftInitial = null;
- if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null);
- dr.mDrawableRight = dr.mDrawableRightInitial = null;
+ if (dr.mShowing[Drawables.LEFT] != null) {
+ dr.mShowing[Drawables.LEFT].setCallback(null);
+ }
+ dr.mShowing[Drawables.LEFT] = dr.mDrawableLeftInitial = null;
+ if (dr.mShowing[Drawables.RIGHT] != null) {
+ dr.mShowing[Drawables.RIGHT].setCallback(null);
+ }
+ dr.mShowing[Drawables.RIGHT] = dr.mDrawableRightInitial = null;
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
}
@@ -2214,12 +2250,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// out all of the fields in the existing structure.
if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
dr.mDrawableStart = null;
- if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null);
- dr.mDrawableTop = null;
- if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null);
+ if (dr.mShowing[Drawables.TOP] != null) {
+ dr.mShowing[Drawables.TOP].setCallback(null);
+ }
+ dr.mShowing[Drawables.TOP] = null;
+ if (dr.mDrawableEnd != null) {
+ dr.mDrawableEnd.setCallback(null);
+ }
dr.mDrawableEnd = null;
- if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null);
- dr.mDrawableBottom = null;
+ if (dr.mShowing[Drawables.BOTTOM] != null) {
+ dr.mShowing[Drawables.BOTTOM].setCallback(null);
+ }
+ dr.mShowing[Drawables.BOTTOM] = null;
dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
@@ -2238,20 +2280,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
dr.mDrawableStart = start;
- if (dr.mDrawableTop != top && dr.mDrawableTop != null) {
- dr.mDrawableTop.setCallback(null);
+ if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
+ dr.mShowing[Drawables.TOP].setCallback(null);
}
- dr.mDrawableTop = top;
+ dr.mShowing[Drawables.TOP] = top;
if (dr.mDrawableEnd != end && dr.mDrawableEnd != null) {
dr.mDrawableEnd.setCallback(null);
}
dr.mDrawableEnd = end;
- if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) {
- dr.mDrawableBottom.setCallback(null);
+ if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
+ dr.mShowing[Drawables.BOTTOM].setCallback(null);
}
- dr.mDrawableBottom = bottom;
+ dr.mShowing[Drawables.BOTTOM] = bottom;
final Rect compoundRect = dr.mCompoundRect;
int[] state;
@@ -2377,9 +2419,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public Drawable[] getCompoundDrawables() {
final Drawables dr = mDrawables;
if (dr != null) {
- return new Drawable[] {
- dr.mDrawableLeft, dr.mDrawableTop, dr.mDrawableRight, dr.mDrawableBottom
- };
+ return dr.mShowing.clone();
} else {
return new Drawable[] { null, null, null, null };
}
@@ -2398,7 +2438,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final Drawables dr = mDrawables;
if (dr != null) {
return new Drawable[] {
- dr.mDrawableStart, dr.mDrawableTop, dr.mDrawableEnd, dr.mDrawableBottom
+ dr.mDrawableStart, dr.mShowing[Drawables.TOP],
+ dr.mDrawableEnd, dr.mShowing[Drawables.BOTTOM]
};
} else {
return new Drawable[] { null, null, null, null };
@@ -2439,6 +2480,118 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return dr != null ? dr.mDrawablePadding : 0;
}
+ /**
+ * Applies a tint to the compound drawables. Does not modify the
+ * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to
+ * {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)}
+ * and related methods will automatically mutate the drawables 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#TextView_drawableTint
+ * @see #getCompoundDrawableTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setCompoundDrawableTintList(@Nullable ColorStateList tint) {
+ if (mDrawables == null) {
+ mDrawables = new Drawables(getContext());
+ }
+ mDrawables.mTintList = tint;
+ mDrawables.mHasTint = true;
+
+ applyCompoundDrawableTint();
+ }
+
+ /**
+ * @return the tint applied to the compound drawables
+ * @attr ref android.R.styleable#TextView_drawableTint
+ * @see #setCompoundDrawableTintList(ColorStateList)
+ */
+ public ColorStateList getCompoundDrawableTintList() {
+ return mDrawables != null ? mDrawables.mTintList : null;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setCompoundDrawableTintList(ColorStateList)} to the compound
+ * drawables. 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#TextView_drawableTintMode
+ * @see #setCompoundDrawableTintList(ColorStateList)
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setCompoundDrawableTintMode(@Nullable PorterDuff.Mode tintMode) {
+ if (mDrawables == null) {
+ mDrawables = new Drawables(getContext());
+ }
+ mDrawables.mTintMode = tintMode;
+ mDrawables.mHasTintMode = true;
+
+ applyCompoundDrawableTint();
+ }
+
+ /**
+ * Returns the blending mode used to apply the tint to the compound
+ * drawables, if specified.
+ *
+ * @return the blending mode used to apply the tint to the compound
+ * drawables
+ * @attr ref android.R.styleable#TextView_drawableTintMode
+ * @see #setCompoundDrawableTintMode(PorterDuff.Mode)
+ */
+ public PorterDuff.Mode getCompoundDrawableTintMode() {
+ return mDrawables != null ? mDrawables.mTintMode : null;
+ }
+
+ private void applyCompoundDrawableTint() {
+ if (mDrawables == null) {
+ return;
+ }
+
+ if (mDrawables.mHasTint || mDrawables.mHasTintMode) {
+ final ColorStateList tintList = mDrawables.mTintList;
+ final PorterDuff.Mode tintMode = mDrawables.mTintMode;
+ final boolean hasTint = mDrawables.mHasTint;
+ final boolean hasTintMode = mDrawables.mHasTintMode;
+ final int[] state = getDrawableState();
+
+ for (Drawable dr : mDrawables.mShowing) {
+ if (dr == null) {
+ continue;
+ }
+
+ if (dr == mDrawables.mDrawableError) {
+ // From a developer's perspective, the error drawable isn't
+ // a compound drawable. Don't apply the generic compound
+ // drawable tint to it.
+ continue;
+ }
+
+ dr.mutate();
+
+ if (hasTint) {
+ dr.setTintList(tintList);
+ }
+
+ if (hasTintMode) {
+ dr.setTintMode(tintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (dr.isStateful()) {
+ dr.setState(state);
+ }
+ }
+ }
+ }
+
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (left != mPaddingLeft ||
@@ -3657,26 +3810,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
updateTextColors();
}
- final Drawables dr = mDrawables;
- if (dr != null) {
- int[] state = getDrawableState();
- if (dr.mDrawableTop != null && dr.mDrawableTop.isStateful()) {
- dr.mDrawableTop.setState(state);
- }
- if (dr.mDrawableBottom != null && dr.mDrawableBottom.isStateful()) {
- dr.mDrawableBottom.setState(state);
- }
- if (dr.mDrawableLeft != null && dr.mDrawableLeft.isStateful()) {
- dr.mDrawableLeft.setState(state);
- }
- if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) {
- dr.mDrawableRight.setState(state);
- }
- if (dr.mDrawableStart != null && dr.mDrawableStart.isStateful()) {
- dr.mDrawableStart.setState(state);
- }
- if (dr.mDrawableEnd != null && dr.mDrawableEnd.isStateful()) {
- dr.mDrawableEnd.setState(state);
+ if (mDrawables != null) {
+ final int[] state = getDrawableState();
+ for (Drawable dr : mDrawables.mShowing) {
+ if (dr != null && dr.isStateful()) {
+ dr.setState(state);
+ }
}
}
}
@@ -3685,25 +3824,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void drawableHotspotChanged(float x, float y) {
super.drawableHotspotChanged(x, y);
- final Drawables dr = mDrawables;
- if (dr != null) {
- if (dr.mDrawableTop != null) {
- dr.mDrawableTop.setHotspot(x, y);
- }
- if (dr.mDrawableBottom != null) {
- dr.mDrawableBottom.setHotspot(x, y);
- }
- if (dr.mDrawableLeft != null) {
- dr.mDrawableLeft.setHotspot(x, y);
- }
- if (dr.mDrawableRight != null) {
- dr.mDrawableRight.setHotspot(x, y);
- }
- if (dr.mDrawableStart != null) {
- dr.mDrawableStart.setHotspot(x, y);
- }
- if (dr.mDrawableEnd != null) {
- dr.mDrawableEnd.setHotspot(x, y);
+ if (mDrawables != null) {
+ final int[] state = getDrawableState();
+ for (Drawable dr : mDrawables.mShowing) {
+ if (dr != null && dr.isStateful()) {
+ dr.setHotspot(x, y);
+ }
}
}
}
@@ -5028,9 +5154,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
protected boolean verifyDrawable(Drawable who) {
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
- return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop ||
- who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom ||
- who == mDrawables.mDrawableStart || who == mDrawables.mDrawableEnd;
+ for (Drawable dr : mDrawables.mShowing) {
+ if (who == dr) {
+ return true;
+ }
+ }
}
return verified;
}
@@ -5039,23 +5167,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
if (mDrawables != null) {
- if (mDrawables.mDrawableLeft != null) {
- mDrawables.mDrawableLeft.jumpToCurrentState();
- }
- if (mDrawables.mDrawableTop != null) {
- mDrawables.mDrawableTop.jumpToCurrentState();
- }
- if (mDrawables.mDrawableRight != null) {
- mDrawables.mDrawableRight.jumpToCurrentState();
- }
- if (mDrawables.mDrawableBottom != null) {
- mDrawables.mDrawableBottom.jumpToCurrentState();
- }
- if (mDrawables.mDrawableStart != null) {
- mDrawables.mDrawableStart.jumpToCurrentState();
- }
- if (mDrawables.mDrawableEnd != null) {
- mDrawables.mDrawableEnd.jumpToCurrentState();
+ for (Drawable dr : mDrawables.mShowing) {
+ if (dr != null) {
+ dr.jumpToCurrentState();
+ }
}
}
}
@@ -5074,7 +5189,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// accordingly.
final TextView.Drawables drawables = mDrawables;
if (drawables != null) {
- if (drawable == drawables.mDrawableLeft) {
+ if (drawable == drawables.mShowing[Drawables.LEFT]) {
final int compoundPaddingTop = getCompoundPaddingTop();
final int compoundPaddingBottom = getCompoundPaddingBottom();
final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
@@ -5082,7 +5197,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
scrollX += mPaddingLeft;
scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2;
handled = true;
- } else if (drawable == drawables.mDrawableRight) {
+ } else if (drawable == drawables.mShowing[Drawables.RIGHT]) {
final int compoundPaddingTop = getCompoundPaddingTop();
final int compoundPaddingBottom = getCompoundPaddingBottom();
final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
@@ -5090,7 +5205,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight);
scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2;
handled = true;
- } else if (drawable == drawables.mDrawableTop) {
+ } else if (drawable == drawables.mShowing[Drawables.TOP]) {
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
@@ -5098,7 +5213,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2;
scrollY += mPaddingTop;
handled = true;
- } else if (drawable == drawables.mDrawableBottom) {
+ } else if (drawable == drawables.mShowing[Drawables.BOTTOM]) {
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
@@ -5302,44 +5417,44 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
// Make sure to update invalidateDrawable() when changing this code.
- if (dr.mDrawableLeft != null) {
+ if (dr.mShowing[Drawables.LEFT] != null) {
canvas.save();
canvas.translate(scrollX + mPaddingLeft + leftOffset,
scrollY + compoundPaddingTop +
(vspace - dr.mDrawableHeightLeft) / 2);
- dr.mDrawableLeft.draw(canvas);
+ dr.mShowing[Drawables.LEFT].draw(canvas);
canvas.restore();
}
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
// Make sure to update invalidateDrawable() when changing this code.
- if (dr.mDrawableRight != null) {
+ if (dr.mShowing[Drawables.RIGHT] != null) {
canvas.save();
canvas.translate(scrollX + right - left - mPaddingRight
- dr.mDrawableSizeRight - rightOffset,
scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
- dr.mDrawableRight.draw(canvas);
+ dr.mShowing[Drawables.RIGHT].draw(canvas);
canvas.restore();
}
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
// Make sure to update invalidateDrawable() when changing this code.
- if (dr.mDrawableTop != null) {
+ if (dr.mShowing[Drawables.TOP] != null) {
canvas.save();
canvas.translate(scrollX + compoundPaddingLeft +
(hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
- dr.mDrawableTop.draw(canvas);
+ dr.mShowing[Drawables.TOP].draw(canvas);
canvas.restore();
}
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
// Make sure to update invalidateDrawable() when changing this code.
- if (dr.mDrawableBottom != null) {
+ if (dr.mShowing[Drawables.BOTTOM] != null) {
canvas.save();
canvas.translate(scrollX + compoundPaddingLeft +
(hspace - dr.mDrawableWidthBottom) / 2,
scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
- dr.mDrawableBottom.draw(canvas);
+ dr.mShowing[Drawables.BOTTOM].draw(canvas);
canvas.restore();
}
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 15bd2f76e7fb..52cf8c105e2b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4163,6 +4163,28 @@
<attr name="drawableEnd" format="reference|color" />
<!-- The padding between the drawables and the text. -->
<attr name="drawablePadding" format="dimension" />
+ <!-- Tint to apply to the compound (left, top, etc.) drawables. -->
+ <attr name="drawableTint" format="color" />
+ <!-- Blending mode used to apply the compound (left, top, etc.) drawables tint. -->
+ <attr name="drawableTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
<!-- Extra spacing between lines of text. -->
<attr name="lineSpacingExtra" format="dimension" />
<!-- Extra spacing between lines of text, as a multiplier. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c7b5615a626d..dbf2b4d198f4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2594,6 +2594,8 @@
<public type="attr" name="dialogPreferredPadding" id="0x010104d3" />
<public type="attr" name="searchHintIcon" id="0x010104d4" />
<public type="attr" name="revisionCode" />
+ <public type="attr" name="drawableTint" />
+ <public type="attr" name="drawableTintMode" />
<public type="style" name="Theme.DeviceDefault.Dialog.Alert" />
<public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />