InsetDrawable.java revision 860126b78aa4d6e8db5208c7f96764a8556cf95f
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 org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.content.res.Resources.Theme;
25import android.graphics.*;
26import android.util.AttributeSet;
27import android.util.Log;
28
29import java.io.IOException;
30
31/**
32 * A Drawable that insets another Drawable by a specified distance.
33 * This is used when a View needs a background that is smaller than
34 * the View's actual bounds.
35 *
36 * <p>It can be defined in an XML file with the <code>&lt;inset></code> element. For more
37 * information, see the guide to <a
38 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
39 *
40 * @attr ref android.R.styleable#InsetDrawable_visible
41 * @attr ref android.R.styleable#InsetDrawable_drawable
42 * @attr ref android.R.styleable#InsetDrawable_insetLeft
43 * @attr ref android.R.styleable#InsetDrawable_insetRight
44 * @attr ref android.R.styleable#InsetDrawable_insetTop
45 * @attr ref android.R.styleable#InsetDrawable_insetBottom
46 */
47public class InsetDrawable extends Drawable implements Drawable.Callback
48{
49    // Most of this is copied from ScaleDrawable.
50    private InsetState mInsetState;
51    private final Rect mTmpRect = new Rect();
52    private boolean mMutated;
53
54    /*package*/ InsetDrawable() {
55        this(null, null);
56    }
57
58    public InsetDrawable(Drawable drawable, int inset) {
59        this(drawable, inset, inset, inset, inset);
60    }
61
62    public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
63                         int insetRight, int insetBottom) {
64        this(null, null);
65
66        mInsetState.mDrawable = drawable;
67        mInsetState.mInsetLeft = insetLeft;
68        mInsetState.mInsetTop = insetTop;
69        mInsetState.mInsetRight = insetRight;
70        mInsetState.mInsetBottom = insetBottom;
71
72        if (drawable != null) {
73            drawable.setCallback(this);
74        }
75    }
76
77    @Override
78    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
79            throws XmlPullParserException, IOException {
80        int type;
81
82        TypedArray a = r.obtainAttributes(attrs,
83                com.android.internal.R.styleable.InsetDrawable);
84
85        super.inflateWithAttributes(r, parser, a,
86                com.android.internal.R.styleable.InsetDrawable_visible);
87
88        int drawableRes = a.getResourceId(com.android.internal.R.styleable.
89                                    InsetDrawable_drawable, 0);
90
91        int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable.
92                                    InsetDrawable_insetLeft, 0);
93        int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable.
94                                    InsetDrawable_insetTop, 0);
95        int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable.
96                                    InsetDrawable_insetRight, 0);
97        int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable.
98                                    InsetDrawable_insetBottom, 0);
99
100        a.recycle();
101
102        Drawable dr;
103        if (drawableRes != 0) {
104            dr = r.getDrawable(drawableRes);
105        } else {
106            while ((type=parser.next()) == XmlPullParser.TEXT) {
107            }
108            if (type != XmlPullParser.START_TAG) {
109                throw new XmlPullParserException(
110                        parser.getPositionDescription()
111                        + ": <inset> tag requires a 'drawable' attribute or "
112                        + "child tag defining a drawable");
113            }
114            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
115        }
116
117        if (dr == null) {
118            Log.w("drawable", "No drawable specified for <inset>");
119        }
120
121        mInsetState.mDrawable = dr;
122        mInsetState.mInsetLeft = inLeft;
123        mInsetState.mInsetRight = inRight;
124        mInsetState.mInsetTop = inTop;
125        mInsetState.mInsetBottom = inBottom;
126
127        if (dr != null) {
128            dr.setCallback(this);
129        }
130    }
131
132    // overrides from Drawable.Callback
133
134    public void invalidateDrawable(Drawable who) {
135        final Callback callback = getCallback();
136        if (callback != null) {
137            callback.invalidateDrawable(this);
138        }
139    }
140
141    public void scheduleDrawable(Drawable who, Runnable what, long when) {
142        final Callback callback = getCallback();
143        if (callback != null) {
144            callback.scheduleDrawable(this, what, when);
145        }
146    }
147
148    public void unscheduleDrawable(Drawable who, Runnable what) {
149        final Callback callback = getCallback();
150        if (callback != null) {
151            callback.unscheduleDrawable(this, what);
152        }
153    }
154
155    // overrides from Drawable
156
157    @Override
158    public void draw(Canvas canvas) {
159        mInsetState.mDrawable.draw(canvas);
160    }
161
162    @Override
163    public int getChangingConfigurations() {
164        return super.getChangingConfigurations()
165                | mInsetState.mChangingConfigurations
166                | mInsetState.mDrawable.getChangingConfigurations();
167    }
168
169    @Override
170    public boolean getPadding(Rect padding) {
171        boolean pad = mInsetState.mDrawable.getPadding(padding);
172
173        padding.left += mInsetState.mInsetLeft;
174        padding.right += mInsetState.mInsetRight;
175        padding.top += mInsetState.mInsetTop;
176        padding.bottom += mInsetState.mInsetBottom;
177
178        if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
179                    mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
180            return true;
181        } else {
182            return false;
183        }
184    }
185
186    @Override
187    public boolean supportsHotspots() {
188        return mInsetState.mDrawable.supportsHotspots();
189    }
190
191    @Override
192    public void setHotspot(int id, float x, float y) {
193        mInsetState.mDrawable.setHotspot(id, x, y);
194    }
195
196    @Override
197    public void removeHotspot(int id) {
198        mInsetState.mDrawable.removeHotspot(id);
199    }
200
201    @Override
202    public void clearHotspots() {
203        mInsetState.mDrawable.clearHotspots();
204    }
205
206    @Override
207    public boolean setVisible(boolean visible, boolean restart) {
208        mInsetState.mDrawable.setVisible(visible, restart);
209        return super.setVisible(visible, restart);
210    }
211
212    @Override
213    public void setAlpha(int alpha) {
214        mInsetState.mDrawable.setAlpha(alpha);
215    }
216
217    @Override
218    public int getAlpha() {
219        return mInsetState.mDrawable.getAlpha();
220    }
221
222    @Override
223    public void setColorFilter(ColorFilter cf) {
224        mInsetState.mDrawable.setColorFilter(cf);
225    }
226
227    /** {@hide} */
228    @Override
229    public void setLayoutDirection(int layoutDirection) {
230        mInsetState.mDrawable.setLayoutDirection(layoutDirection);
231    }
232
233    @Override
234    public int getOpacity() {
235        return mInsetState.mDrawable.getOpacity();
236    }
237
238    @Override
239    public boolean isStateful() {
240        return mInsetState.mDrawable.isStateful();
241    }
242
243    @Override
244    protected boolean onStateChange(int[] state) {
245        boolean changed = mInsetState.mDrawable.setState(state);
246        onBoundsChange(getBounds());
247        return changed;
248    }
249
250    @Override
251    protected void onBoundsChange(Rect bounds) {
252        final Rect r = mTmpRect;
253        r.set(bounds);
254
255        r.left += mInsetState.mInsetLeft;
256        r.top += mInsetState.mInsetTop;
257        r.right -= mInsetState.mInsetRight;
258        r.bottom -= mInsetState.mInsetBottom;
259
260        mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
261    }
262
263    @Override
264    public int getIntrinsicWidth() {
265        return mInsetState.mDrawable.getIntrinsicWidth();
266    }
267
268    @Override
269    public int getIntrinsicHeight() {
270        return mInsetState.mDrawable.getIntrinsicHeight();
271    }
272
273    @Override
274    public ConstantState getConstantState() {
275        if (mInsetState.canConstantState()) {
276            mInsetState.mChangingConfigurations = getChangingConfigurations();
277            return mInsetState;
278        }
279        return null;
280    }
281
282    @Override
283    public Drawable mutate() {
284        if (!mMutated && super.mutate() == this) {
285            mInsetState.mDrawable.mutate();
286            mMutated = true;
287        }
288        return this;
289    }
290
291    /**
292     * Returns the drawable wrapped by this InsetDrawable. May be null.
293     */
294    public Drawable getDrawable() {
295        return mInsetState.mDrawable;
296    }
297
298    final static class InsetState extends ConstantState {
299        Drawable mDrawable;
300        int mChangingConfigurations;
301
302        int mInsetLeft;
303        int mInsetTop;
304        int mInsetRight;
305        int mInsetBottom;
306
307        boolean mCheckedConstantState;
308        boolean mCanConstantState;
309
310        InsetState(InsetState orig, InsetDrawable owner, Resources res) {
311            if (orig != null) {
312                if (res != null) {
313                    mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
314                } else {
315                    mDrawable = orig.mDrawable.getConstantState().newDrawable();
316                }
317                mDrawable.setCallback(owner);
318                mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
319                mInsetLeft = orig.mInsetLeft;
320                mInsetTop = orig.mInsetTop;
321                mInsetRight = orig.mInsetRight;
322                mInsetBottom = orig.mInsetBottom;
323                mCheckedConstantState = mCanConstantState = true;
324            }
325        }
326
327        @Override
328        public Drawable newDrawable() {
329            return new InsetDrawable(this, null);
330        }
331
332        @Override
333        public Drawable newDrawable(Resources res) {
334            return new InsetDrawable(this, res);
335        }
336
337        @Override
338        public int getChangingConfigurations() {
339            return mChangingConfigurations;
340        }
341
342        boolean canConstantState() {
343            if (!mCheckedConstantState) {
344                mCanConstantState = mDrawable.getConstantState() != null;
345                mCheckedConstantState = true;
346            }
347
348            return mCanConstantState;
349        }
350    }
351
352    private InsetDrawable(InsetState state, Resources res) {
353        mInsetState = new InsetState(state, this, res);
354    }
355}
356
357