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