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