ScaleDrawable.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
1/*
2 * Copyright (C) 2006 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.view.Gravity;
26import android.util.AttributeSet;
27
28import java.io.IOException;
29
30/**
31 * A Drawable that changes the size of another Drawable based on its current
32 * level value.  You can control how much the child Drawable changes in width
33 * and height based on the level, as well as a gravity to control where it is
34 * placed in its overall container.  Most often used to implement things like
35 * progress bars.
36 *
37 * <p>It can be defined in an XML file with the <code>&lt;scale></code> element.</p>
38 *
39 * @attr ref android.R.styleable#ScaleDrawable_scaleWidth
40 * @attr ref android.R.styleable#ScaleDrawable_scaleHeight
41 * @attr ref android.R.styleable#ScaleDrawable_scaleGravity
42 * @attr ref android.R.styleable#ScaleDrawable_drawable
43 */
44public class ScaleDrawable extends Drawable implements Drawable.Callback {
45    private ScaleState mScaleState;
46    private boolean mMutated;
47    private final Rect mTmpRect = new Rect();
48
49    ScaleDrawable() {
50        this(null);
51    }
52
53    public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) {
54        this(null);
55
56        mScaleState.mDrawable = drawable;
57        mScaleState.mGravity = gravity;
58        mScaleState.mScaleWidth = scaleWidth;
59        mScaleState.mScaleHeight = scaleHeight;
60
61        if (drawable != null) {
62            drawable.setCallback(this);
63        }
64    }
65
66    /**
67     * Returns the drawable scaled by this ScaleDrawable.
68     */
69    public Drawable getDrawable() {
70        return mScaleState.mDrawable;
71    }
72
73    private static float getPercent(TypedArray a, int name) {
74        String s = a.getString(name);
75        if (s != null) {
76            if (s.endsWith("%")) {
77                String f = s.substring(0, s.length() - 1);
78                return Float.parseFloat(f) / 100.0f;
79            }
80        }
81        return -1;
82    }
83
84    @Override
85    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
86            throws XmlPullParserException, IOException {
87        super.inflate(r, parser, attrs);
88
89        int type;
90
91        TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ScaleDrawable);
92
93        float sw = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleWidth);
94        float sh = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleHeight);
95        int g = a.getInt(com.android.internal.R.styleable.ScaleDrawable_scaleGravity, Gravity.LEFT);
96        Drawable dr = a.getDrawable(com.android.internal.R.styleable.ScaleDrawable_drawable);
97
98        a.recycle();
99
100        final int outerDepth = parser.getDepth();
101        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
102                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
103            if (type != XmlPullParser.START_TAG) {
104                continue;
105            }
106            dr = Drawable.createFromXmlInner(r, parser, attrs);
107        }
108
109        if (dr == null) {
110            throw new IllegalArgumentException("No drawable specified for <scale>");
111        }
112
113        mScaleState.mDrawable = dr;
114        mScaleState.mScaleWidth = sw;
115        mScaleState.mScaleHeight = sh;
116        mScaleState.mGravity = g;
117        if (dr != null) {
118            dr.setCallback(this);
119        }
120    }
121
122    // overrides from Drawable.Callback
123
124    public void invalidateDrawable(Drawable who) {
125        if (mCallback != null) {
126            mCallback.invalidateDrawable(this);
127        }
128    }
129
130    public void scheduleDrawable(Drawable who, Runnable what, long when) {
131        if (mCallback != null) {
132            mCallback.scheduleDrawable(this, what, when);
133        }
134    }
135
136    public void unscheduleDrawable(Drawable who, Runnable what) {
137        if (mCallback != null) {
138            mCallback.unscheduleDrawable(this, what);
139        }
140    }
141
142    // overrides from Drawable
143
144    @Override
145    public void draw(Canvas canvas) {
146        if (mScaleState.mDrawable.getLevel() != 0)
147            mScaleState.mDrawable.draw(canvas);
148    }
149
150    @Override
151    public int getChangingConfigurations() {
152        return super.getChangingConfigurations()
153                | mScaleState.mChangingConfigurations
154                | mScaleState.mDrawable.getChangingConfigurations();
155    }
156
157    @Override
158    public boolean getPadding(Rect padding) {
159        // XXX need to adjust padding!
160        return mScaleState.mDrawable.getPadding(padding);
161    }
162
163    @Override
164    public boolean setVisible(boolean visible, boolean restart) {
165        mScaleState.mDrawable.setVisible(visible, restart);
166        return super.setVisible(visible, restart);
167    }
168
169    @Override
170    public void setAlpha(int alpha) {
171        mScaleState.mDrawable.setAlpha(alpha);
172    }
173
174    @Override
175    public void setColorFilter(ColorFilter cf) {
176        mScaleState.mDrawable.setColorFilter(cf);
177    }
178
179    @Override
180    public int getOpacity() {
181        return mScaleState.mDrawable.getOpacity();
182    }
183
184    @Override
185    public boolean isStateful() {
186        return mScaleState.mDrawable.isStateful();
187    }
188
189    @Override
190    protected boolean onStateChange(int[] state) {
191        boolean changed = mScaleState.mDrawable.setState(state);
192        onBoundsChange(getBounds());
193        return changed;
194    }
195
196    @Override
197    protected boolean onLevelChange(int level) {
198        mScaleState.mDrawable.setLevel(level);
199        onBoundsChange(getBounds());
200        invalidateSelf();
201        return true;
202    }
203
204    @Override
205    protected void onBoundsChange(Rect bounds) {
206        final Rect r = mTmpRect;
207        int level = getLevel();
208        int w = bounds.width();
209        final int iw = 0; //mScaleState.mDrawable.getIntrinsicWidth();
210        if (mScaleState.mScaleWidth > 0) {
211            w -= (int) ((w - iw) * (10000 - level) * mScaleState.mScaleWidth / 10000);
212        }
213        int h = bounds.height();
214        final int ih = 0; //mScaleState.mDrawable.getIntrinsicHeight();
215        if (mScaleState.mScaleHeight > 0) {
216            h -= (int) ((h - ih) * (10000 - level) * mScaleState.mScaleHeight / 10000);
217        }
218        Gravity.apply(mScaleState.mGravity, w, h, bounds, r);
219
220        if (w > 0 && h > 0) {
221            mScaleState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
222        }
223    }
224
225    @Override
226    public int getIntrinsicWidth() {
227        return mScaleState.mDrawable.getIntrinsicWidth();
228    }
229
230    @Override
231    public int getIntrinsicHeight() {
232        return mScaleState.mDrawable.getIntrinsicHeight();
233    }
234
235    @Override
236    public ConstantState getConstantState() {
237        if (mScaleState.canConstantState()) {
238            mScaleState.mChangingConfigurations = super.getChangingConfigurations();
239            return mScaleState;
240        }
241        return null;
242    }
243
244    @Override
245    public Drawable mutate() {
246        if (!mMutated && super.mutate() == this) {
247            mScaleState.mDrawable.mutate();
248            mMutated = true;
249        }
250        return this;
251    }
252
253    final static class ScaleState extends ConstantState {
254        Drawable mDrawable;
255        int mChangingConfigurations;
256        float mScaleWidth;
257        float mScaleHeight;
258        int mGravity;
259
260        private boolean mCheckedConstantState;
261        private boolean mCanConstantState;
262
263        ScaleState(ScaleState orig, ScaleDrawable owner) {
264            if (orig != null) {
265                mDrawable = orig.mDrawable.getConstantState().newDrawable();
266                mDrawable.setCallback(owner);
267                mScaleWidth = orig.mScaleWidth;
268                mScaleHeight = orig.mScaleHeight;
269                mGravity = orig.mGravity;
270                mCheckedConstantState = mCanConstantState = true;
271            }
272        }
273
274        @Override
275        public Drawable newDrawable() {
276            return new ScaleDrawable(this);
277        }
278
279        @Override
280        public int getChangingConfigurations() {
281            return mChangingConfigurations;
282        }
283
284        boolean canConstantState() {
285            if (!mCheckedConstantState) {
286                mCanConstantState = mDrawable.getConstantState() != null;
287                mCheckedConstantState = true;
288            }
289
290            return mCanConstantState;
291        }
292    }
293
294    private ScaleDrawable(ScaleState state) {
295        mScaleState = new ScaleState(state, this);
296    }
297}
298
299