DrawableContainer.java revision 5140141c2637b89ad0d86c3b715459a1e7b92729
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            final int N = mDrawableContainerState.getChildCount();
238            final Drawable[] drawables = mDrawableContainerState.getChildren();
239            for (int i = 0; i < N; i++) {
240                drawables[i].mutate();
241            }
242            mMutated = true;
243        }
244        return this;
245    }
246
247    public abstract static class DrawableContainerState extends ConstantState {
248        final DrawableContainer mOwner;
249
250        int         mChangingConfigurations;
251        int         mChildrenChangingConfigurations;
252
253        Drawable[]  mDrawables;
254        int         mNumChildren;
255
256        boolean     mVariablePadding = false;
257        Rect        mConstantPadding = null;
258
259        boolean     mConstantSize = false;
260        boolean     mComputedConstantSize = false;
261        int         mConstantWidth;
262        int         mConstantHeight;
263        int         mConstantMinimumWidth;
264        int         mConstantMinimumHeight;
265
266        boolean     mHaveOpacity = false;
267        int         mOpacity;
268
269        boolean     mHaveStateful = false;
270        boolean     mStateful;
271
272        boolean     mCheckedConstantState;
273        boolean     mCanConstantState;
274
275        boolean     mPaddingChecked = false;
276
277        DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) {
278            mOwner = owner;
279
280            if (orig != null) {
281                mChangingConfigurations = orig.mChangingConfigurations;
282                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
283
284                final Drawable[] origDr = orig.mDrawables;
285
286                mDrawables = new Drawable[origDr.length];
287                mNumChildren = orig.mNumChildren;
288
289                final int N = mNumChildren;
290                for (int i=0; i<N; i++) {
291                    mDrawables[i] = origDr[i].getConstantState().newDrawable();
292                    mDrawables[i].setCallback(owner);
293                }
294
295                mCheckedConstantState = mCanConstantState = true;
296                mVariablePadding = orig.mVariablePadding;
297                if (orig.mConstantPadding != null) {
298                    mConstantPadding = new Rect(orig.mConstantPadding);
299                }
300                mConstantSize = orig.mConstantSize;
301                mComputedConstantSize = orig.mComputedConstantSize;
302                mConstantWidth = orig.mConstantWidth;
303                mConstantHeight = orig.mConstantHeight;
304
305                mHaveOpacity = orig.mHaveOpacity;
306                mOpacity = orig.mOpacity;
307                mHaveStateful = orig.mHaveStateful;
308                mStateful = orig.mStateful;
309
310            } else {
311                mDrawables = new Drawable[10];
312                mNumChildren = 0;
313                mCheckedConstantState = mCanConstantState = false;
314            }
315        }
316
317        @Override
318        public int getChangingConfigurations() {
319            return mChangingConfigurations;
320        }
321
322        public final int addChild(Drawable dr) {
323            final int pos = mNumChildren;
324
325            if (pos >= mDrawables.length) {
326                growArray(pos, pos+10);
327            }
328
329            dr.setVisible(false, true);
330            dr.setCallback(mOwner);
331
332            mDrawables[pos] = dr;
333            mNumChildren++;
334            mChildrenChangingConfigurations |= dr.getChangingConfigurations();
335            mHaveOpacity = false;
336            mHaveStateful = false;
337
338            mConstantPadding = null;
339            mPaddingChecked = false;
340            mComputedConstantSize = false;
341
342            return pos;
343        }
344
345        public final int getChildCount() {
346            return mNumChildren;
347        }
348
349        public final Drawable[] getChildren() {
350            return mDrawables;
351        }
352
353        /** A boolean value indicating whether to use the maximum padding value of
354          * all frames in the set (false), or to use the padding value of the frame
355          * being shown (true). Default value is false.
356          */
357        public final void setVariablePadding(boolean variable) {
358            mVariablePadding = variable;
359        }
360
361        public final Rect getConstantPadding() {
362            if (mVariablePadding) {
363                return null;
364            }
365            if (mConstantPadding != null || mPaddingChecked) {
366                return mConstantPadding;
367            }
368
369            Rect r = null;
370            final Rect t = new Rect();
371            final int N = getChildCount();
372            final Drawable[] drawables = mDrawables;
373            for (int i = 0; i < N; i++) {
374                if (drawables[i].getPadding(t)) {
375                    if (r == null) r = new Rect(0, 0, 0, 0);
376                    if (t.left > r.left) r.left = t.left;
377                    if (t.top > r.top) r.top = t.top;
378                    if (t.right > r.right) r.right = t.right;
379                    if (t.bottom > r.bottom) r.bottom = t.bottom;
380                }
381            }
382            mPaddingChecked = true;
383            return (mConstantPadding = r);
384        }
385
386        public final void setConstantSize(boolean constant) {
387            mConstantSize = constant;
388        }
389
390        public final boolean isConstantSize() {
391            return mConstantSize;
392        }
393
394        public final int getConstantWidth() {
395            if (!mComputedConstantSize) {
396                computeConstantSize();
397            }
398
399            return mConstantWidth;
400        }
401
402        public final int getConstantHeight() {
403            if (!mComputedConstantSize) {
404                computeConstantSize();
405            }
406
407            return mConstantHeight;
408        }
409
410        public final int getConstantMinimumWidth() {
411            if (!mComputedConstantSize) {
412                computeConstantSize();
413            }
414
415            return mConstantMinimumWidth;
416        }
417
418        public final int getConstantMinimumHeight() {
419            if (!mComputedConstantSize) {
420                computeConstantSize();
421            }
422
423            return mConstantMinimumHeight;
424        }
425
426        private void computeConstantSize() {
427            mComputedConstantSize = true;
428
429            final int N = getChildCount();
430            final Drawable[] drawables = mDrawables;
431            mConstantWidth = mConstantHeight = 0;
432            mConstantMinimumWidth = mConstantMinimumHeight = 0;
433            for (int i = 0; i < N; i++) {
434                Drawable dr = drawables[i];
435                int s = dr.getIntrinsicWidth();
436                if (s > mConstantWidth) mConstantWidth = s;
437                s = dr.getIntrinsicHeight();
438                if (s > mConstantHeight) mConstantHeight = s;
439                s = dr.getMinimumWidth();
440                if (s > mConstantMinimumWidth) mConstantMinimumWidth = s;
441                s = dr.getMinimumHeight();
442                if (s > mConstantMinimumHeight) mConstantMinimumHeight = s;
443            }
444        }
445
446        public final int getOpacity() {
447            if (mHaveOpacity) {
448                return mOpacity;
449            }
450
451            final int N = getChildCount();
452            final Drawable[] drawables = mDrawables;
453            int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
454            for (int i = 1; i < N; i++) {
455                op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
456            }
457            mOpacity = op;
458            mHaveOpacity = true;
459            return op;
460        }
461
462        public final boolean isStateful() {
463            if (mHaveStateful) {
464                return mStateful;
465            }
466
467            boolean stateful = false;
468            final int N = getChildCount();
469            for (int i = 0; i < N; i++) {
470                if (mDrawables[i].isStateful()) {
471                    stateful = true;
472                    break;
473                }
474            }
475
476            mStateful = stateful;
477            mHaveStateful = true;
478            return stateful;
479        }
480
481        public void growArray(int oldSize, int newSize) {
482            Drawable[] newDrawables = new Drawable[newSize];
483            System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize);
484            mDrawables = newDrawables;
485        }
486
487        public synchronized boolean canConstantState() {
488            if (!mCheckedConstantState) {
489                mCanConstantState = true;
490                final int N = mNumChildren;
491                for (int i=0; i<N; i++) {
492                    if (mDrawables[i].getConstantState() == null) {
493                        mCanConstantState = false;
494                        break;
495                    }
496                }
497                mCheckedConstantState = true;
498            }
499
500            return mCanConstantState;
501        }
502    }
503
504    protected void setConstantState(DrawableContainerState state)
505    {
506        mDrawableContainerState = state;
507    }
508}
509