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