ColorDrawable.java revision 955d8d69ea6caabce1461dc25b339b9bf9dc61a6
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 17package android.graphics.drawable; 18 19import android.annotation.NonNull; 20import android.graphics.*; 21import android.graphics.PorterDuff.Mode; 22import android.content.res.ColorStateList; 23import android.content.res.Resources; 24import android.content.res.Resources.Theme; 25import android.content.res.TypedArray; 26import android.util.AttributeSet; 27import android.view.ViewDebug; 28 29import com.android.internal.R; 30 31import org.xmlpull.v1.XmlPullParser; 32import org.xmlpull.v1.XmlPullParserException; 33 34import java.io.IOException; 35 36/** 37 * A specialized Drawable that fills the Canvas with a specified color. 38 * Note that a ColorDrawable ignores the ColorFilter. 39 * 40 * <p>It can be defined in an XML file with the <code><color></code> element.</p> 41 * 42 * @attr ref android.R.styleable#ColorDrawable_color 43 */ 44public class ColorDrawable extends Drawable { 45 private final Paint mPaint = new Paint(); 46 47 @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") 48 private ColorState mColorState; 49 private PorterDuffColorFilter mTintFilter; 50 51 private boolean mMutated; 52 53 /** 54 * Creates a new black ColorDrawable. 55 */ 56 public ColorDrawable() { 57 mColorState = new ColorState(); 58 } 59 60 /** 61 * Creates a new ColorDrawable with the specified color. 62 * 63 * @param color The color to draw. 64 */ 65 public ColorDrawable(int color) { 66 mColorState = new ColorState(); 67 68 setColor(color); 69 } 70 71 @Override 72 public int getChangingConfigurations() { 73 return super.getChangingConfigurations() | mColorState.mChangingConfigurations; 74 } 75 76 /** 77 * A mutable BitmapDrawable still shares its Bitmap with any other Drawable 78 * that comes from the same resource. 79 * 80 * @return This drawable. 81 */ 82 @Override 83 public Drawable mutate() { 84 if (!mMutated && super.mutate() == this) { 85 mColorState = new ColorState(mColorState); 86 mMutated = true; 87 } 88 return this; 89 } 90 91 @Override 92 public void draw(Canvas canvas) { 93 final ColorFilter colorFilter = mPaint.getColorFilter(); 94 if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) { 95 if (colorFilter == null) { 96 mPaint.setColorFilter(mTintFilter); 97 } 98 99 mPaint.setColor(mColorState.mUseColor); 100 canvas.drawRect(getBounds(), mPaint); 101 102 // Restore original color filter. 103 mPaint.setColorFilter(colorFilter); 104 } 105 } 106 107 /** 108 * Gets the drawable's color value. 109 * 110 * @return int The color to draw. 111 */ 112 public int getColor() { 113 return mColorState.mUseColor; 114 } 115 116 /** 117 * Sets the drawable's color value. This action will clobber the results of 118 * prior calls to {@link #setAlpha(int)} on this object, which side-affected 119 * the underlying color. 120 * 121 * @param color The color to draw. 122 */ 123 public void setColor(int color) { 124 if (mColorState.mBaseColor != color || mColorState.mUseColor != color) { 125 mColorState.mBaseColor = mColorState.mUseColor = color; 126 invalidateSelf(); 127 } 128 } 129 130 /** 131 * Returns the alpha value of this drawable's color. 132 * 133 * @return A value between 0 and 255. 134 */ 135 @Override 136 public int getAlpha() { 137 return mColorState.mUseColor >>> 24; 138 } 139 140 /** 141 * Sets the color's alpha value. 142 * 143 * @param alpha The alpha value to set, between 0 and 255. 144 */ 145 @Override 146 public void setAlpha(int alpha) { 147 alpha += alpha >> 7; // make it 0..256 148 final int baseAlpha = mColorState.mBaseColor >>> 24; 149 final int useAlpha = baseAlpha * alpha >> 8; 150 final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24); 151 if (mColorState.mUseColor != useColor) { 152 mColorState.mUseColor = useColor; 153 invalidateSelf(); 154 } 155 } 156 157 /** 158 * Sets the color filter applied to this color. 159 * <p> 160 * Only supported on version {@link android.os.Build.VERSION_CODES#LOLLIPOP} and 161 * above. Calling this method has no effect on earlier versions. 162 * 163 * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter) 164 */ 165 @Override 166 public void setColorFilter(ColorFilter colorFilter) { 167 mPaint.setColorFilter(colorFilter); 168 } 169 170 @Override 171 public void setTintList(ColorStateList tint) { 172 mColorState.mTint = tint; 173 mTintFilter = updateTintFilter(mTintFilter, tint, mColorState.mTintMode); 174 invalidateSelf(); 175 } 176 177 @Override 178 public void setTintMode(Mode tintMode) { 179 mColorState.mTintMode = tintMode; 180 mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, tintMode); 181 invalidateSelf(); 182 } 183 184 @Override 185 protected boolean onStateChange(int[] stateSet) { 186 final ColorState state = mColorState; 187 if (state.mTint != null && state.mTintMode != null) { 188 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 189 return true; 190 } 191 return false; 192 } 193 194 @Override 195 public boolean isStateful() { 196 return mColorState.mTint != null && mColorState.mTint.isStateful(); 197 } 198 199 @Override 200 public int getOpacity() { 201 if (mTintFilter != null || mPaint.getColorFilter() != null) { 202 return PixelFormat.TRANSLUCENT; 203 } 204 205 switch (mColorState.mUseColor >>> 24) { 206 case 255: 207 return PixelFormat.OPAQUE; 208 case 0: 209 return PixelFormat.TRANSPARENT; 210 } 211 return PixelFormat.TRANSLUCENT; 212 } 213 214 @Override 215 public void getOutline(@NonNull Outline outline) { 216 outline.setRect(getBounds()); 217 outline.setAlpha(getAlpha() / 255.0f); 218 } 219 220 @Override 221 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 222 throws XmlPullParserException, IOException { 223 super.inflate(r, parser, attrs, theme); 224 225 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); 226 updateStateFromTypedArray(a); 227 a.recycle(); 228 } 229 230 /** 231 * Updates the constant state from the values in the typed array. 232 */ 233 private void updateStateFromTypedArray(TypedArray a) { 234 final ColorState state = mColorState; 235 236 // Account for any configuration changes. 237 state.mChangingConfigurations |= a.getChangingConfigurations(); 238 239 // Extract the theme attributes, if any. 240 state.mThemeAttrs = a.extractThemeAttrs(); 241 242 state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor); 243 state.mUseColor = state.mBaseColor; 244 } 245 246 @Override 247 public void applyTheme(Theme t) { 248 super.applyTheme(t); 249 250 final ColorState state = mColorState; 251 if (state == null || state.mThemeAttrs == null) { 252 return; 253 } 254 255 final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable); 256 updateStateFromTypedArray(a); 257 a.recycle(); 258 } 259 260 @Override 261 public ConstantState getConstantState() { 262 return mColorState; 263 } 264 265 final static class ColorState extends ConstantState { 266 int[] mThemeAttrs; 267 int mBaseColor; // base color, independent of setAlpha() 268 @ViewDebug.ExportedProperty 269 int mUseColor; // basecolor modulated by setAlpha() 270 int mChangingConfigurations; 271 ColorStateList mTint = null; 272 Mode mTintMode = DEFAULT_TINT_MODE; 273 274 ColorState() { 275 // Empty constructor. 276 } 277 278 ColorState(ColorState state) { 279 mThemeAttrs = state.mThemeAttrs; 280 mBaseColor = state.mBaseColor; 281 mUseColor = state.mUseColor; 282 mChangingConfigurations = state.mChangingConfigurations; 283 mTint = state.mTint; 284 mTintMode = state.mTintMode; 285 } 286 287 @Override 288 public boolean canApplyTheme() { 289 return mThemeAttrs != null; 290 } 291 292 @Override 293 public Drawable newDrawable() { 294 return new ColorDrawable(this, null, null); 295 } 296 297 @Override 298 public Drawable newDrawable(Resources res) { 299 return new ColorDrawable(this, res, null); 300 } 301 302 @Override 303 public Drawable newDrawable(Resources res, Theme theme) { 304 return new ColorDrawable(this, res, theme); 305 } 306 307 @Override 308 public int getChangingConfigurations() { 309 return mChangingConfigurations; 310 } 311 } 312 313 private ColorDrawable(ColorState state, Resources res, Theme theme) { 314 if (theme != null && state.canApplyTheme()) { 315 mColorState = new ColorState(state); 316 applyTheme(theme); 317 } else { 318 mColorState = state; 319 } 320 321 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 322 } 323} 324