InsetDrawable.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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.</p>
36 *
37 * @attr ref android.R.styleable#InsetDrawable_visible
38 * @attr ref android.R.styleable#InsetDrawable_drawable
39 * @attr ref android.R.styleable#InsetDrawable_insetLeft
40 * @attr ref android.R.styleable#InsetDrawable_insetRight
41 * @attr ref android.R.styleable#InsetDrawable_insetTop
42 * @attr ref android.R.styleable#InsetDrawable_insetBottom
43 */
44public class InsetDrawable extends Drawable implements Drawable.Callback
45{
46    // Most of this is copied from ScaleDrawable.
47    private InsetState mInsetState;
48    private final Rect mTmpRect = new Rect();
49    private boolean mMutated;
50
51    /*package*/ InsetDrawable() {
52        this(null);
53    }
54
55    public InsetDrawable(Drawable drawable, int inset) {
56        this(drawable, inset, inset, inset, inset);
57    }
58
59    public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
60                         int insetRight, int insetBottom) {
61        this(null);
62
63        mInsetState.mDrawable = drawable;
64        mInsetState.mInsetLeft = insetLeft;
65        mInsetState.mInsetTop = insetTop;
66        mInsetState.mInsetRight = insetRight;
67        mInsetState.mInsetBottom = insetBottom;
68
69        if (drawable != null) {
70            drawable.setCallback(this);
71        }
72    }
73
74    @Override public void inflate(Resources r, XmlPullParser parser,
75                                  AttributeSet attrs)
76    throws XmlPullParserException, IOException {
77        int type;
78
79        TypedArray a = r.obtainAttributes(attrs,
80                com.android.internal.R.styleable.InsetDrawable);
81
82        super.inflateWithAttributes(r, parser, a,
83                com.android.internal.R.styleable.InsetDrawable_visible);
84
85        int drawableRes = a.getResourceId(com.android.internal.R.styleable.
86                                    InsetDrawable_drawable, 0);
87
88        int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable.
89                                    InsetDrawable_insetLeft, 0);
90        int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable.
91                                    InsetDrawable_insetTop, 0);
92        int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable.
93                                    InsetDrawable_insetRight, 0);
94        int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable.
95                                    InsetDrawable_insetBottom, 0);
96
97        a.recycle();
98
99        Drawable dr;
100        if (drawableRes != 0) {
101            dr = r.getDrawable(drawableRes);
102        } else {
103            while ((type=parser.next()) == XmlPullParser.TEXT) {
104            }
105            if (type != XmlPullParser.START_TAG) {
106                throw new XmlPullParserException(
107                        parser.getPositionDescription()
108                        + ": <inset> tag requires a 'drawable' attribute or "
109                        + "child tag defining a drawable");
110            }
111            dr = Drawable.createFromXmlInner(r, parser, attrs);
112        }
113
114        if (dr == null) {
115            Log.w("drawable", "No drawable specified for <inset>");
116        }
117
118        mInsetState.mDrawable = dr;
119        mInsetState.mInsetLeft = inLeft;
120        mInsetState.mInsetRight = inRight;
121        mInsetState.mInsetTop = inTop;
122        mInsetState.mInsetBottom = inBottom;
123
124        if (dr != null) {
125            dr.setCallback(this);
126        }
127    }
128
129    // overrides from Drawable.Callback
130
131    public void invalidateDrawable(Drawable who) {
132        if (mCallback != null) {
133            mCallback.invalidateDrawable(this);
134        }
135    }
136
137    public void scheduleDrawable(Drawable who, Runnable what, long when) {
138        if (mCallback != null) {
139            mCallback.scheduleDrawable(this, what, when);
140        }
141    }
142
143    public void unscheduleDrawable(Drawable who, Runnable what) {
144        if (mCallback != null) {
145            mCallback.unscheduleDrawable(this, what);
146        }
147    }
148
149    // overrides from Drawable
150
151    @Override
152    public void draw(Canvas canvas) {
153        mInsetState.mDrawable.draw(canvas);
154    }
155
156    @Override
157    public int getChangingConfigurations() {
158        return super.getChangingConfigurations()
159                | mInsetState.mChangingConfigurations
160                | mInsetState.mDrawable.getChangingConfigurations();
161    }
162
163    @Override
164    public boolean getPadding(Rect padding) {
165        boolean pad = mInsetState.mDrawable.getPadding(padding);
166
167        padding.left += mInsetState.mInsetLeft;
168        padding.right += mInsetState.mInsetRight;
169        padding.top += mInsetState.mInsetTop;
170        padding.bottom += mInsetState.mInsetBottom;
171
172        if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
173                    mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
174            return true;
175        } else {
176            return false;
177        }
178    }
179
180    @Override
181    public boolean setVisible(boolean visible, boolean restart) {
182        mInsetState.mDrawable.setVisible(visible, restart);
183        return super.setVisible(visible, restart);
184    }
185
186    @Override
187    public void setAlpha(int alpha) {
188        mInsetState.mDrawable.setAlpha(alpha);
189    }
190
191    @Override
192    public void setColorFilter(ColorFilter cf) {
193        mInsetState.mDrawable.setColorFilter(cf);
194    }
195
196    @Override
197    public int getOpacity() {
198        return mInsetState.mDrawable.getOpacity();
199    }
200
201    @Override
202    public boolean isStateful() {
203        return mInsetState.mDrawable.isStateful();
204    }
205
206    @Override
207    protected boolean onStateChange(int[] state) {
208        boolean changed = mInsetState.mDrawable.setState(state);
209        onBoundsChange(getBounds());
210        return changed;
211    }
212
213    @Override
214    protected void onBoundsChange(Rect bounds) {
215        final Rect r = mTmpRect;
216        r.set(bounds);
217
218        r.left += mInsetState.mInsetLeft;
219        r.top += mInsetState.mInsetTop;
220        r.right -= mInsetState.mInsetRight;
221        r.bottom -= mInsetState.mInsetBottom;
222
223        mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
224    }
225
226    @Override
227    public int getIntrinsicWidth() {
228        return mInsetState.mDrawable.getIntrinsicWidth();
229    }
230
231    @Override
232    public int getIntrinsicHeight() {
233        return mInsetState.mDrawable.getIntrinsicHeight();
234    }
235
236    @Override
237    public ConstantState getConstantState() {
238        if (mInsetState.canConstantState()) {
239            mInsetState.mChangingConfigurations = super.getChangingConfigurations();
240            return mInsetState;
241        }
242        return null;
243    }
244
245    @Override
246    public Drawable mutate() {
247        if (!mMutated && super.mutate() == this) {
248            mInsetState.mDrawable.mutate();
249            mMutated = true;
250        }
251        return this;
252    }
253
254    final static class InsetState extends ConstantState {
255        Drawable mDrawable;
256        int mChangingConfigurations;
257
258        int mInsetLeft;
259        int mInsetTop;
260        int mInsetRight;
261        int mInsetBottom;
262
263        boolean mCheckedConstantState;
264        boolean mCanConstantState;
265
266        InsetState(InsetState orig, InsetDrawable owner) {
267            if (orig != null) {
268                mDrawable = orig.mDrawable.getConstantState().newDrawable();
269                mDrawable.setCallback(owner);
270                mInsetLeft = orig.mInsetLeft;
271                mInsetTop = orig.mInsetTop;
272                mInsetRight = orig.mInsetRight;
273                mInsetBottom = orig.mInsetBottom;
274                mCheckedConstantState = mCanConstantState = true;
275            }
276        }
277
278        @Override
279        public Drawable newDrawable() {
280            return new InsetDrawable(this);
281        }
282
283        @Override
284        public int getChangingConfigurations() {
285            return mChangingConfigurations;
286        }
287
288        boolean canConstantState() {
289            if (!mCheckedConstantState) {
290                mCanConstantState = mDrawable.getConstantState() != null;
291                mCheckedConstantState = true;
292            }
293
294            return mCanConstantState;
295        }
296    }
297
298    private InsetDrawable(InsetState state) {
299        mInsetState = new InsetState(state, this);
300    }
301}
302
303