DrawableContainer.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 android.graphics.*;
20
21public class DrawableContainer extends Drawable implements Drawable.Callback {
22
23    private DrawableContainerState mDrawableContainerState;
24    private Drawable        mCurrDrawable;
25    private int             mAlpha = 0xFF;
26    private ColorFilter     mColorFilter;
27    private boolean         mDither;
28
29    private int             mCurIndex = -1;
30
31    // overrides from Drawable
32
33    @Override
34    public void draw(Canvas canvas) {
35        if (mCurrDrawable != null) {
36            mCurrDrawable.draw(canvas);
37        }
38    }
39
40    @Override
41    public int getChangingConfigurations() {
42        return super.getChangingConfigurations()
43                | mDrawableContainerState.mChangingConfigurations
44                | mDrawableContainerState.mChildrenChangingConfigurations;
45    }
46
47    @Override
48    public boolean getPadding(Rect padding) {
49        final Rect r = mDrawableContainerState.getConstantPadding();
50        if (r != null) {
51            padding.set(r);
52            return true;
53        }
54        if (mCurrDrawable != null) {
55            return mCurrDrawable.getPadding(padding);
56        } else {
57            return super.getPadding(padding);
58        }
59    }
60
61    @Override
62    public void setAlpha(int alpha) {
63        if (mAlpha != alpha) {
64            mAlpha = alpha;
65            if (mCurrDrawable != null) {
66                mCurrDrawable.setAlpha(alpha);
67            }
68        }
69    }
70
71    @Override
72    public void setDither(boolean dither) {
73        if (mDither != dither) {
74            mDither = dither;
75            if (mCurrDrawable != null) {
76                mCurrDrawable.setDither(mDither);
77            }
78        }
79    }
80
81    @Override
82    public void setColorFilter(ColorFilter cf) {
83        if (mColorFilter != cf) {
84            mColorFilter = cf;
85            if (mCurrDrawable != null) {
86                mCurrDrawable.setColorFilter(cf);
87            }
88        }
89    }
90
91    @Override
92    protected void onBoundsChange(Rect bounds) {
93        if (mCurrDrawable != null) {
94            mCurrDrawable.setBounds(bounds);
95        }
96    }
97
98    @Override
99    public boolean isStateful() {
100        return mDrawableContainerState.isStateful();
101    }
102
103    @Override
104    protected boolean onStateChange(int[] state) {
105        if (mCurrDrawable != null) {
106            return mCurrDrawable.setState(state);
107        }
108        return false;
109    }
110
111    @Override
112    protected boolean onLevelChange(int level) {
113        if (mCurrDrawable != null) {
114            return mCurrDrawable.setLevel(level);
115        }
116        return false;
117    }
118
119    @Override
120    public int getIntrinsicWidth() {
121        if (mDrawableContainerState.isConstantSize()) {
122            return mDrawableContainerState.getConstantWidth();
123        }
124        return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1;
125    }
126
127    @Override
128    public int getIntrinsicHeight() {
129        if (mDrawableContainerState.isConstantSize()) {
130            return mDrawableContainerState.getConstantHeight();
131        }
132        return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
133    }
134
135    @Override
136    public int getMinimumWidth() {
137        if (mDrawableContainerState.isConstantSize()) {
138            return mDrawableContainerState.getConstantMinimumWidth();
139        }
140        return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0;
141    }
142
143    @Override
144    public int getMinimumHeight() {
145        if (mDrawableContainerState.isConstantSize()) {
146            return mDrawableContainerState.getConstantMinimumHeight();
147        }
148        return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
149    }
150
151    public void invalidateDrawable(Drawable who)
152    {
153        if (who == mCurrDrawable && mCallback != null) {
154            mCallback.invalidateDrawable(this);
155        }
156    }
157
158    public void scheduleDrawable(Drawable who, Runnable what, long when)
159    {
160        if (who == mCurrDrawable && mCallback != null) {
161            mCallback.scheduleDrawable(this, what, when);
162        }
163    }
164
165    public void unscheduleDrawable(Drawable who, Runnable what)
166    {
167        if (who == mCurrDrawable && mCallback != null) {
168            mCallback.unscheduleDrawable(this, what);
169        }
170    }
171
172    @Override
173    public boolean setVisible(boolean visible, boolean restart) {
174        boolean changed = super.setVisible(visible, restart);
175        if (mCurrDrawable != null) {
176            mCurrDrawable.setVisible(visible, restart);
177        }
178        return changed;
179    }
180
181    @Override
182    public int getOpacity() {
183        return mDrawableContainerState.getOpacity();
184    }
185
186    public boolean selectDrawable(int idx)
187    {
188        if (idx == mCurIndex) {
189            return false;
190        }
191        if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
192            Drawable d = mDrawableContainerState.mDrawables[idx];
193            if (mCurrDrawable != null) {
194                mCurrDrawable.setVisible(false, false);
195            }
196            mCurrDrawable = d;
197            mCurIndex = idx;
198            if (d != null) {
199                d.setVisible(isVisible(), true);
200                d.setAlpha(mAlpha);
201                d.setDither(mDither);
202                d.setColorFilter(mColorFilter);
203                d.setState(getState());
204                d.setLevel(getLevel());
205                d.setBounds(getBounds());
206            }
207        } else {
208            if (mCurrDrawable != null) {
209                mCurrDrawable.setVisible(false, false);
210            }
211            mCurrDrawable = null;
212            mCurIndex = -1;
213        }
214        invalidateSelf();
215        return true;
216    }
217
218    @Override
219    public Drawable getCurrent() {
220        return mCurrDrawable;
221    }
222
223    @Override
224    public ConstantState getConstantState() {
225        if (mDrawableContainerState.canConstantState()) {
226            mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations();
227            return mDrawableContainerState;
228        }
229        return null;
230    }
231
232    public abstract static class DrawableContainerState extends ConstantState {
233        final DrawableContainer mOwner;
234
235        int         mChangingConfigurations;
236        int         mChildrenChangingConfigurations;
237
238        Drawable[]  mDrawables;
239        int         mNumChildren;
240
241        boolean     mVariablePadding = false;
242        Rect        mConstantPadding = null;
243
244        boolean     mConstantSize = false;
245        boolean     mComputedConstantSize = false;
246        int         mConstantWidth;
247        int         mConstantHeight;
248        int         mConstantMinimumWidth;
249        int         mConstantMinimumHeight;
250
251        boolean     mHaveOpacity = false;
252        int         mOpacity;
253
254        boolean     mHaveStateful = false;
255        boolean     mStateful;
256
257        boolean     mCheckedConstantState;
258        boolean     mCanConstantState;
259
260        DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) {
261            mOwner = owner;
262
263            if (orig != null) {
264                mChangingConfigurations = orig.mChangingConfigurations;
265                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
266
267                final Drawable[] origDr = orig.mDrawables;
268
269                mDrawables = new Drawable[origDr.length];
270                mNumChildren = orig.mNumChildren;
271
272                final int N = mNumChildren;
273                for (int i=0; i<N; i++) {
274                    mDrawables[i] = origDr[i].getConstantState().newDrawable();
275                    mDrawables[i].setCallback(owner);
276                }
277
278                mCheckedConstantState = mCanConstantState = true;
279                mVariablePadding = orig.mVariablePadding;
280                mConstantPadding = orig.mConstantPadding;
281                mConstantSize = orig.mConstantSize;
282                mComputedConstantSize = orig.mComputedConstantSize;
283                mConstantWidth = orig.mConstantWidth;
284                mConstantHeight = orig.mConstantHeight;
285
286                mHaveOpacity = orig.mHaveOpacity;
287                mOpacity = orig.mOpacity;
288                mHaveStateful = orig.mHaveStateful;
289                mStateful = orig.mStateful;
290
291            } else {
292                mDrawables = new Drawable[10];
293                mNumChildren = 0;
294                mCheckedConstantState = mCanConstantState = false;
295            }
296        }
297
298        @Override
299        public int getChangingConfigurations() {
300            return mChangingConfigurations;
301        }
302
303        public final int addChild(Drawable dr) {
304            final int pos = mNumChildren;
305
306            if (pos >= mDrawables.length) {
307                growArray(pos, pos+10);
308            }
309
310            dr.setVisible(false, true);
311            dr.setCallback(mOwner);
312
313            mDrawables[pos] = dr;
314            mNumChildren++;
315            mChildrenChangingConfigurations |= dr.getChangingConfigurations();
316            mHaveOpacity = false;
317            mHaveStateful = false;
318
319            mConstantPadding = null;
320            mComputedConstantSize = false;
321
322            return pos;
323        }
324
325        public final int getChildCount()
326        {
327            return mNumChildren;
328        }
329
330        public final Drawable[] getChildren()
331        {
332            return mDrawables;
333        }
334
335        /** A boolean value indicating whether to use the maximum padding value of
336          * all frames in the set (false), or to use the padding value of the frame
337          * being shown (true). Default value is false.
338          */
339        public final void setVariablePadding(boolean variable)
340        {
341            mVariablePadding = variable;
342        }
343
344        public final Rect getConstantPadding()
345        {
346            if (mVariablePadding) {
347                return null;
348            }
349            if (mConstantPadding != null) {
350                return mConstantPadding;
351            }
352
353            Rect r = new Rect(0, 0, 0, 0);
354            Rect t = new Rect();
355            final int N = getChildCount();
356            for (int i=0; i<N; i++) {
357                if (mDrawables[i].getPadding(t)) {
358                    if (t.left > r.left) r.left = t.left;
359                    if (t.top > r.top) r.top = t.top;
360                    if (t.right > r.right) r.right = t.right;
361                    if (t.bottom > r.bottom) r.bottom = t.bottom;
362                }
363            }
364            return (mConstantPadding=r);
365        }
366
367        public final void setConstantSize(boolean constant)
368        {
369            mConstantSize = constant;
370        }
371
372        public final boolean isConstantSize()
373        {
374            return mConstantSize;
375        }
376
377        public final int getConstantWidth()
378        {
379            if (!mComputedConstantSize) {
380                computeConstantSize();
381            }
382
383            return mConstantWidth;
384        }
385
386        public final int getConstantHeight()
387        {
388            if (!mComputedConstantSize) {
389                computeConstantSize();
390            }
391
392            return mConstantHeight;
393        }
394
395        public final int getConstantMinimumWidth()
396        {
397            if (!mComputedConstantSize) {
398                computeConstantSize();
399            }
400
401            return mConstantMinimumWidth;
402        }
403
404        public final int getConstantMinimumHeight()
405        {
406            if (!mComputedConstantSize) {
407                computeConstantSize();
408            }
409
410            return mConstantMinimumHeight;
411        }
412
413        private void computeConstantSize()
414        {
415            mComputedConstantSize = true;
416
417            final int N = getChildCount();
418            mConstantWidth = mConstantHeight = 0;
419            mConstantMinimumWidth = mConstantMinimumHeight = 0;
420            for (int i=0; i<N; i++) {
421                Drawable dr = mDrawables[i];
422                int s = dr.getIntrinsicWidth();
423                if (s > mConstantWidth) mConstantWidth = s;
424                s = dr.getIntrinsicHeight();
425                if (s > mConstantHeight) mConstantHeight = s;
426                s = dr.getMinimumWidth();
427                if (s > mConstantMinimumWidth) mConstantMinimumWidth = s;
428                s = dr.getMinimumHeight();
429                if (s > mConstantMinimumHeight) mConstantMinimumHeight = s;
430            }
431        }
432
433        public final int getOpacity()
434        {
435            if (mHaveOpacity) {
436                return mOpacity;
437            }
438
439            final int N = getChildCount();
440            int op = N > 0
441                ? mDrawables[0].getOpacity() : PixelFormat.TRANSPARENT;
442            for (int i=1; i<N; i++) {
443                op = Drawable.resolveOpacity(op, mDrawables[i].getOpacity());
444            }
445            mOpacity = op;
446            mHaveOpacity = true;
447            return op;
448        }
449
450        public final boolean isStateful() {
451            if (mHaveStateful) {
452                return mStateful;
453            }
454
455            boolean stateful = false;
456            final int N = getChildCount();
457            for (int i = 0; i < N; i++) {
458                if (mDrawables[i].isStateful()) {
459                    stateful = true;
460                    break;
461                }
462            }
463
464            mStateful = stateful;
465            mHaveStateful = true;
466            return stateful;
467        }
468
469        public void growArray(int oldSize, int newSize)
470        {
471            Drawable[] newDrawables = new Drawable[newSize];
472            System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize);
473            mDrawables = newDrawables;
474        }
475
476        public synchronized boolean canConstantState() {
477            if (!mCheckedConstantState) {
478                mCanConstantState = true;
479                final int N = mNumChildren;
480                for (int i=0; i<N; i++) {
481                    if (mDrawables[i].getConstantState() == null) {
482                        mCanConstantState = false;
483                        break;
484                    }
485                }
486                mCheckedConstantState = true;
487            }
488
489            return mCanConstantState;
490        }
491    }
492
493    protected void setConstantState(DrawableContainerState state)
494    {
495        mDrawableContainerState = state;
496    }
497}
498