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