ColorDrawable.java revision 4b17118aca1e67963254ab83504b0753a3eac7ce
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.graphics.*; 20import android.graphics.PorterDuff.Mode; 21import android.content.res.ColorStateList; 22import android.content.res.Resources; 23import android.content.res.Resources.Theme; 24import android.content.res.TypedArray; 25import android.util.AttributeSet; 26import android.view.ViewDebug; 27 28import com.android.internal.R; 29 30import org.xmlpull.v1.XmlPullParser; 31import org.xmlpull.v1.XmlPullParserException; 32 33import java.io.IOException; 34 35/** 36 * A specialized Drawable that fills the Canvas with a specified color. 37 * Note that a ColorDrawable ignores the ColorFilter. 38 * 39 * <p>It can be defined in an XML file with the <code><color></code> element.</p> 40 * 41 * @attr ref android.R.styleable#ColorDrawable_color 42 */ 43public class ColorDrawable extends Drawable { 44 private final Paint mPaint = new Paint(); 45 46 @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") 47 private ColorState mColorState; 48 private ColorStateList mTint; 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 @Override 158 public void setColorFilter(ColorFilter colorFilter) { 159 mPaint.setColorFilter(colorFilter); 160 } 161 162 @Override 163 public void setTint(ColorStateList tint, Mode tintMode) { 164 final ColorState state = mColorState; 165 if (state.mTint != tint || state.mTintMode != tintMode) { 166 state.mTint = tint; 167 state.mTintMode = tintMode; 168 169 mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); 170 invalidateSelf(); 171 } 172 } 173 174 @Override 175 protected boolean onStateChange(int[] stateSet) { 176 final ColorState state = mColorState; 177 if (state.mTint != null && state.mTintMode != null) { 178 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 179 return true; 180 } 181 return false; 182 } 183 184 @Override 185 public boolean isStateful() { 186 return mTint != null && mTint.isStateful(); 187 } 188 189 @Override 190 public int getOpacity() { 191 if (mTintFilter != null || mPaint.getColorFilter() != null) { 192 return PixelFormat.TRANSLUCENT; 193 } 194 195 switch (mColorState.mUseColor >>> 24) { 196 case 255: 197 return PixelFormat.OPAQUE; 198 case 0: 199 return PixelFormat.TRANSPARENT; 200 } 201 return PixelFormat.TRANSLUCENT; 202 } 203 204 @Override 205 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 206 throws XmlPullParserException, IOException { 207 super.inflate(r, parser, attrs, theme); 208 209 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); 210 inflateStateFromTypedArray(a); 211 a.recycle(); 212 } 213 214 /** 215 * Initializes the constant state from the values in the typed array. 216 */ 217 private void inflateStateFromTypedArray(TypedArray a) { 218 final ColorState state = mColorState; 219 220 // Extract the theme attributes, if any. 221 final int[] themeAttrs = a.extractThemeAttrs(); 222 state.mThemeAttrs = themeAttrs; 223 224 if (themeAttrs == null || themeAttrs[R.styleable.ColorDrawable_color] == 0) { 225 final int color = a.getColor(R.styleable.ColorDrawable_color, 0); 226 state.mBaseColor = color; 227 state.mUseColor = color; 228 } 229 } 230 231 @Override 232 public void applyTheme(Theme t) { 233 super.applyTheme(t); 234 235 final ColorState state = mColorState; 236 if (state == null) { 237 throw new RuntimeException("Can't apply theme to <color> with no constant state"); 238 } 239 240 final int[] themeAttrs = state.mThemeAttrs; 241 if (themeAttrs != null) { 242 final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable); 243 updateStateFromTypedArray(a); 244 a.recycle(); 245 } 246 } 247 248 /** 249 * Updates the constant state from the values in the typed array. 250 */ 251 private void updateStateFromTypedArray(TypedArray a) { 252 final ColorState state = mColorState; 253 254 if (a.hasValue(R.styleable.ColorDrawable_color)) { 255 final int color = a.getColor(R.styleable.ColorDrawable_color, 0); 256 state.mBaseColor = color; 257 state.mUseColor = color; 258 } 259 } 260 261 @Override 262 public ConstantState getConstantState() { 263 mColorState.mChangingConfigurations = getChangingConfigurations(); 264 return mColorState; 265 } 266 267 final static class ColorState extends ConstantState { 268 int[] mThemeAttrs; 269 int mBaseColor; // base color, independent of setAlpha() 270 @ViewDebug.ExportedProperty 271 int mUseColor; // basecolor modulated by setAlpha() 272 int mChangingConfigurations; 273 ColorStateList mTint; 274 Mode mTintMode; 275 276 ColorState() { 277 // Empty constructor. 278 } 279 280 ColorState(ColorState state) { 281 mThemeAttrs = state.mThemeAttrs; 282 mBaseColor = state.mBaseColor; 283 mUseColor = state.mUseColor; 284 mChangingConfigurations = state.mChangingConfigurations; 285 mTint = state.mTint; 286 mTintMode = state.mTintMode; 287 } 288 289 @Override 290 public boolean canApplyTheme() { 291 return mThemeAttrs != null; 292 } 293 294 @Override 295 public Drawable newDrawable() { 296 return new ColorDrawable(this, null, null); 297 } 298 299 @Override 300 public Drawable newDrawable(Resources res) { 301 return new ColorDrawable(this, res, null); 302 } 303 304 @Override 305 public Drawable newDrawable(Resources res, Theme theme) { 306 return new ColorDrawable(this, res, theme); 307 } 308 309 @Override 310 public int getChangingConfigurations() { 311 return mChangingConfigurations; 312 } 313 } 314 315 private ColorDrawable(ColorState state, Resources res, Theme theme) { 316 if (theme != null && state.canApplyTheme()) { 317 mColorState = new ColorState(state); 318 applyTheme(theme); 319 } else { 320 mColorState = state; 321 } 322 323 // No local properties to initialize. 324 } 325} 326