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