1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.drawable; 18 19 import android.annotation.ColorInt; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.content.pm.ActivityInfo.Config; 24 import android.graphics.*; 25 import android.graphics.PorterDuff.Mode; 26 import android.content.res.ColorStateList; 27 import android.content.res.Resources; 28 import android.content.res.Resources.Theme; 29 import android.content.res.TypedArray; 30 import android.util.AttributeSet; 31 import android.view.ViewDebug; 32 33 import com.android.internal.R; 34 35 import org.xmlpull.v1.XmlPullParser; 36 import org.xmlpull.v1.XmlPullParserException; 37 38 import java.io.IOException; 39 40 /** 41 * A specialized Drawable that fills the Canvas with a specified color. 42 * Note that a ColorDrawable ignores the ColorFilter. 43 * 44 * <p>It can be defined in an XML file with the <code><color></code> element.</p> 45 * 46 * @attr ref android.R.styleable#ColorDrawable_color 47 */ 48 public class ColorDrawable extends Drawable { 49 private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 50 51 @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") 52 private ColorState mColorState; 53 private PorterDuffColorFilter mTintFilter; 54 55 private boolean mMutated; 56 57 /** 58 * Creates a new black ColorDrawable. 59 */ ColorDrawable()60 public ColorDrawable() { 61 mColorState = new ColorState(); 62 } 63 64 /** 65 * Creates a new ColorDrawable with the specified color. 66 * 67 * @param color The color to draw. 68 */ ColorDrawable(@olorInt int color)69 public ColorDrawable(@ColorInt int color) { 70 mColorState = new ColorState(); 71 72 setColor(color); 73 } 74 75 @Override getChangingConfigurations()76 public @Config int getChangingConfigurations() { 77 return super.getChangingConfigurations() | mColorState.getChangingConfigurations(); 78 } 79 80 /** 81 * A mutable BitmapDrawable still shares its Bitmap with any other Drawable 82 * that comes from the same resource. 83 * 84 * @return This drawable. 85 */ 86 @Override mutate()87 public Drawable mutate() { 88 if (!mMutated && super.mutate() == this) { 89 mColorState = new ColorState(mColorState); 90 mMutated = true; 91 } 92 return this; 93 } 94 95 /** 96 * @hide 97 */ clearMutated()98 public void clearMutated() { 99 super.clearMutated(); 100 mMutated = false; 101 } 102 103 @Override draw(Canvas canvas)104 public void draw(Canvas canvas) { 105 final ColorFilter colorFilter = mPaint.getColorFilter(); 106 if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) { 107 if (colorFilter == null) { 108 mPaint.setColorFilter(mTintFilter); 109 } 110 111 mPaint.setColor(mColorState.mUseColor); 112 canvas.drawRect(getBounds(), mPaint); 113 114 // Restore original color filter. 115 mPaint.setColorFilter(colorFilter); 116 } 117 } 118 119 /** 120 * Gets the drawable's color value. 121 * 122 * @return int The color to draw. 123 */ 124 @ColorInt getColor()125 public int getColor() { 126 return mColorState.mUseColor; 127 } 128 129 /** 130 * Sets the drawable's color value. This action will clobber the results of 131 * prior calls to {@link #setAlpha(int)} on this object, which side-affected 132 * the underlying color. 133 * 134 * @param color The color to draw. 135 */ setColor(@olorInt int color)136 public void setColor(@ColorInt int color) { 137 if (mColorState.mBaseColor != color || mColorState.mUseColor != color) { 138 mColorState.mBaseColor = mColorState.mUseColor = color; 139 invalidateSelf(); 140 } 141 } 142 143 /** 144 * Returns the alpha value of this drawable's color. 145 * 146 * @return A value between 0 and 255. 147 */ 148 @Override getAlpha()149 public int getAlpha() { 150 return mColorState.mUseColor >>> 24; 151 } 152 153 /** 154 * Sets the color's alpha value. 155 * 156 * @param alpha The alpha value to set, between 0 and 255. 157 */ 158 @Override setAlpha(int alpha)159 public void setAlpha(int alpha) { 160 alpha += alpha >> 7; // make it 0..256 161 final int baseAlpha = mColorState.mBaseColor >>> 24; 162 final int useAlpha = baseAlpha * alpha >> 8; 163 final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24); 164 if (mColorState.mUseColor != useColor) { 165 mColorState.mUseColor = useColor; 166 invalidateSelf(); 167 } 168 } 169 170 /** 171 * Sets the color filter applied to this color. 172 * <p> 173 * Only supported on version {@link android.os.Build.VERSION_CODES#LOLLIPOP} and 174 * above. Calling this method has no effect on earlier versions. 175 * 176 * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter) 177 */ 178 @Override setColorFilter(ColorFilter colorFilter)179 public void setColorFilter(ColorFilter colorFilter) { 180 mPaint.setColorFilter(colorFilter); 181 } 182 183 @Override setTintList(ColorStateList tint)184 public void setTintList(ColorStateList tint) { 185 mColorState.mTint = tint; 186 mTintFilter = updateTintFilter(mTintFilter, tint, mColorState.mTintMode); 187 invalidateSelf(); 188 } 189 190 @Override setTintMode(Mode tintMode)191 public void setTintMode(Mode tintMode) { 192 mColorState.mTintMode = tintMode; 193 mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, tintMode); 194 invalidateSelf(); 195 } 196 197 @Override onStateChange(int[] stateSet)198 protected boolean onStateChange(int[] stateSet) { 199 final ColorState state = mColorState; 200 if (state.mTint != null && state.mTintMode != null) { 201 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 202 return true; 203 } 204 return false; 205 } 206 207 @Override isStateful()208 public boolean isStateful() { 209 return mColorState.mTint != null && mColorState.mTint.isStateful(); 210 } 211 212 /** @hide */ 213 @Override hasFocusStateSpecified()214 public boolean hasFocusStateSpecified() { 215 return mColorState.mTint != null && mColorState.mTint.hasFocusStateSpecified(); 216 } 217 218 /** 219 * @hide 220 * @param mode new transfer mode 221 */ 222 @Override setXfermode(@ullable Xfermode mode)223 public void setXfermode(@Nullable Xfermode mode) { 224 mPaint.setXfermode(mode); 225 invalidateSelf(); 226 } 227 228 /** 229 * @hide 230 * @return current transfer mode 231 */ 232 @TestApi getXfermode()233 public Xfermode getXfermode() { 234 return mPaint.getXfermode(); 235 } 236 237 @Override getOpacity()238 public int getOpacity() { 239 if (mTintFilter != null || mPaint.getColorFilter() != null) { 240 return PixelFormat.TRANSLUCENT; 241 } 242 243 switch (mColorState.mUseColor >>> 24) { 244 case 255: 245 return PixelFormat.OPAQUE; 246 case 0: 247 return PixelFormat.TRANSPARENT; 248 } 249 return PixelFormat.TRANSLUCENT; 250 } 251 252 @Override getOutline(@onNull Outline outline)253 public void getOutline(@NonNull Outline outline) { 254 outline.setRect(getBounds()); 255 outline.setAlpha(getAlpha() / 255.0f); 256 } 257 258 @Override inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)259 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 260 throws XmlPullParserException, IOException { 261 super.inflate(r, parser, attrs, theme); 262 263 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); 264 updateStateFromTypedArray(a); 265 a.recycle(); 266 267 updateLocalState(r); 268 } 269 270 /** 271 * Updates the constant state from the values in the typed array. 272 */ updateStateFromTypedArray(TypedArray a)273 private void updateStateFromTypedArray(TypedArray a) { 274 final ColorState state = mColorState; 275 276 // Account for any configuration changes. 277 state.mChangingConfigurations |= a.getChangingConfigurations(); 278 279 // Extract the theme attributes, if any. 280 state.mThemeAttrs = a.extractThemeAttrs(); 281 282 state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor); 283 state.mUseColor = state.mBaseColor; 284 } 285 286 @Override canApplyTheme()287 public boolean canApplyTheme() { 288 return mColorState.canApplyTheme() || super.canApplyTheme(); 289 } 290 291 @Override applyTheme(Theme t)292 public void applyTheme(Theme t) { 293 super.applyTheme(t); 294 295 final ColorState state = mColorState; 296 if (state == null) { 297 return; 298 } 299 300 if (state.mThemeAttrs != null) { 301 final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable); 302 updateStateFromTypedArray(a); 303 a.recycle(); 304 } 305 306 if (state.mTint != null && state.mTint.canApplyTheme()) { 307 state.mTint = state.mTint.obtainForTheme(t); 308 } 309 310 updateLocalState(t.getResources()); 311 } 312 313 @Override getConstantState()314 public ConstantState getConstantState() { 315 return mColorState; 316 } 317 318 final static class ColorState extends ConstantState { 319 int[] mThemeAttrs; 320 int mBaseColor; // base color, independent of setAlpha() 321 @ViewDebug.ExportedProperty 322 int mUseColor; // basecolor modulated by setAlpha() 323 @Config int mChangingConfigurations; 324 ColorStateList mTint = null; 325 Mode mTintMode = DEFAULT_TINT_MODE; 326 ColorState()327 ColorState() { 328 // Empty constructor. 329 } 330 ColorState(ColorState state)331 ColorState(ColorState state) { 332 mThemeAttrs = state.mThemeAttrs; 333 mBaseColor = state.mBaseColor; 334 mUseColor = state.mUseColor; 335 mChangingConfigurations = state.mChangingConfigurations; 336 mTint = state.mTint; 337 mTintMode = state.mTintMode; 338 } 339 340 @Override canApplyTheme()341 public boolean canApplyTheme() { 342 return mThemeAttrs != null 343 || (mTint != null && mTint.canApplyTheme()); 344 } 345 346 @Override newDrawable()347 public Drawable newDrawable() { 348 return new ColorDrawable(this, null); 349 } 350 351 @Override newDrawable(Resources res)352 public Drawable newDrawable(Resources res) { 353 return new ColorDrawable(this, res); 354 } 355 356 @Override getChangingConfigurations()357 public @Config int getChangingConfigurations() { 358 return mChangingConfigurations 359 | (mTint != null ? mTint.getChangingConfigurations() : 0); 360 } 361 } 362 ColorDrawable(ColorState state, Resources res)363 private ColorDrawable(ColorState state, Resources res) { 364 mColorState = state; 365 366 updateLocalState(res); 367 } 368 369 /** 370 * Initializes local dynamic properties from state. This should be called 371 * after significant state changes, e.g. from the One True Constructor and 372 * after inflating or applying a theme. 373 */ updateLocalState(Resources r)374 private void updateLocalState(Resources r) { 375 mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, mColorState.mTintMode); 376 } 377 } 378