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