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