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