DrawableContainer.java revision 211db4a2874f1a2d0e7a8cb8d33e81fa08801763
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 /** 24 * To be proper, we should have a getter for dither (and alpha, etc.) 25 * so that proxy classes like this can save/restore their delegates' 26 * values, but we don't have getters. Since we do have setters 27 * (e.g. setDither), which this proxy forwards on, we have to have some 28 * default/initial setting. 29 * 30 * The initial setting for dither is now true, since it almost always seems 31 * to improve the quality at negligible cost. 32 */ 33 private static final boolean DEFAULT_DITHER = true; 34 private DrawableContainerState mDrawableContainerState; 35 private Drawable mCurrDrawable; 36 private int mAlpha = 0xFF; 37 private ColorFilter mColorFilter; 38 private boolean mDither = DEFAULT_DITHER; 39 40 private int mCurIndex = -1; 41 private boolean mMutated; 42 43 // overrides from Drawable 44 45 @Override 46 public void draw(Canvas canvas) { 47 if (mCurrDrawable != null) { 48 mCurrDrawable.draw(canvas); 49 } 50 } 51 52 @Override 53 public int getChangingConfigurations() { 54 return super.getChangingConfigurations() 55 | mDrawableContainerState.mChangingConfigurations 56 | mDrawableContainerState.mChildrenChangingConfigurations; 57 } 58 59 @Override 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 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 84 public void setDither(boolean dither) { 85 if (mDither != dither) { 86 mDither = dither; 87 if (mCurrDrawable != null) { 88 mCurrDrawable.setDither(mDither); 89 } 90 } 91 } 92 93 @Override 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 104 protected void onBoundsChange(Rect bounds) { 105 if (mCurrDrawable != null) { 106 mCurrDrawable.setBounds(bounds); 107 } 108 } 109 110 @Override 111 public boolean isStateful() { 112 return mDrawableContainerState.isStateful(); 113 } 114 115 @Override 116 protected boolean onStateChange(int[] state) { 117 if (mCurrDrawable != null) { 118 return mCurrDrawable.setState(state); 119 } 120 return false; 121 } 122 123 @Override 124 protected boolean onLevelChange(int level) { 125 if (mCurrDrawable != null) { 126 return mCurrDrawable.setLevel(level); 127 } 128 return false; 129 } 130 131 @Override 132 public int getIntrinsicWidth() { 133 if (mDrawableContainerState.isConstantSize()) { 134 return mDrawableContainerState.getConstantWidth(); 135 } 136 return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1; 137 } 138 139 @Override 140 public int getIntrinsicHeight() { 141 if (mDrawableContainerState.isConstantSize()) { 142 return mDrawableContainerState.getConstantHeight(); 143 } 144 return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1; 145 } 146 147 @Override 148 public int getMinimumWidth() { 149 if (mDrawableContainerState.isConstantSize()) { 150 return mDrawableContainerState.getConstantMinimumWidth(); 151 } 152 return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0; 153 } 154 155 @Override 156 public int getMinimumHeight() { 157 if (mDrawableContainerState.isConstantSize()) { 158 return mDrawableContainerState.getConstantMinimumHeight(); 159 } 160 return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0; 161 } 162 163 public void invalidateDrawable(Drawable who) 164 { 165 if (who == mCurrDrawable && mCallback != null) { 166 mCallback.invalidateDrawable(this); 167 } 168 } 169 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 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 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 194 public int getOpacity() { 195 return mCurrDrawable == null || !mCurrDrawable.isVisible() ? PixelFormat.TRANSPARENT : 196 mDrawableContainerState.getOpacity(); 197 } 198 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(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 232 public Drawable getCurrent() { 233 return mCurrDrawable; 234 } 235 236 @Override 237 public ConstantState getConstantState() { 238 if (mDrawableContainerState.canConstantState()) { 239 mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations(); 240 return mDrawableContainerState; 241 } 242 return null; 243 } 244 245 @Override 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 DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) { 289 mOwner = owner; 290 291 if (orig != null) { 292 mChangingConfigurations = orig.mChangingConfigurations; 293 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; 294 295 final Drawable[] origDr = orig.mDrawables; 296 297 mDrawables = new Drawable[origDr.length]; 298 mNumChildren = orig.mNumChildren; 299 300 final int N = mNumChildren; 301 for (int i=0; i<N; i++) { 302 mDrawables[i] = origDr[i].getConstantState().newDrawable(); 303 mDrawables[i].setCallback(owner); 304 } 305 306 mCheckedConstantState = mCanConstantState = true; 307 mVariablePadding = orig.mVariablePadding; 308 if (orig.mConstantPadding != null) { 309 mConstantPadding = new Rect(orig.mConstantPadding); 310 } 311 mConstantSize = orig.mConstantSize; 312 mComputedConstantSize = orig.mComputedConstantSize; 313 mConstantWidth = orig.mConstantWidth; 314 mConstantHeight = orig.mConstantHeight; 315 316 mHaveOpacity = orig.mHaveOpacity; 317 mOpacity = orig.mOpacity; 318 mHaveStateful = orig.mHaveStateful; 319 mStateful = orig.mStateful; 320 321 } else { 322 mDrawables = new Drawable[10]; 323 mNumChildren = 0; 324 mCheckedConstantState = mCanConstantState = false; 325 } 326 } 327 328 @Override 329 public int getChangingConfigurations() { 330 return mChangingConfigurations; 331 } 332 333 public final int addChild(Drawable dr) { 334 final int pos = mNumChildren; 335 336 if (pos >= mDrawables.length) { 337 growArray(pos, pos+10); 338 } 339 340 dr.setVisible(false, true); 341 dr.setCallback(mOwner); 342 343 mDrawables[pos] = dr; 344 mNumChildren++; 345 mChildrenChangingConfigurations |= dr.getChangingConfigurations(); 346 mHaveOpacity = false; 347 mHaveStateful = false; 348 349 mConstantPadding = null; 350 mPaddingChecked = false; 351 mComputedConstantSize = false; 352 353 return pos; 354 } 355 356 public final int getChildCount() { 357 return mNumChildren; 358 } 359 360 public final Drawable[] getChildren() { 361 return mDrawables; 362 } 363 364 /** A boolean value indicating whether to use the maximum padding value of 365 * all frames in the set (false), or to use the padding value of the frame 366 * being shown (true). Default value is false. 367 */ 368 public final void setVariablePadding(boolean variable) { 369 mVariablePadding = variable; 370 } 371 372 public final Rect getConstantPadding() { 373 if (mVariablePadding) { 374 return null; 375 } 376 if (mConstantPadding != null || mPaddingChecked) { 377 return mConstantPadding; 378 } 379 380 Rect r = null; 381 final Rect t = new Rect(); 382 final int N = getChildCount(); 383 final Drawable[] drawables = mDrawables; 384 for (int i = 0; i < N; i++) { 385 if (drawables[i].getPadding(t)) { 386 if (r == null) r = new Rect(0, 0, 0, 0); 387 if (t.left > r.left) r.left = t.left; 388 if (t.top > r.top) r.top = t.top; 389 if (t.right > r.right) r.right = t.right; 390 if (t.bottom > r.bottom) r.bottom = t.bottom; 391 } 392 } 393 mPaddingChecked = true; 394 return (mConstantPadding = r); 395 } 396 397 public final void setConstantSize(boolean constant) { 398 mConstantSize = constant; 399 } 400 401 public final boolean isConstantSize() { 402 return mConstantSize; 403 } 404 405 public final int getConstantWidth() { 406 if (!mComputedConstantSize) { 407 computeConstantSize(); 408 } 409 410 return mConstantWidth; 411 } 412 413 public final int getConstantHeight() { 414 if (!mComputedConstantSize) { 415 computeConstantSize(); 416 } 417 418 return mConstantHeight; 419 } 420 421 public final int getConstantMinimumWidth() { 422 if (!mComputedConstantSize) { 423 computeConstantSize(); 424 } 425 426 return mConstantMinimumWidth; 427 } 428 429 public final int getConstantMinimumHeight() { 430 if (!mComputedConstantSize) { 431 computeConstantSize(); 432 } 433 434 return mConstantMinimumHeight; 435 } 436 437 private void computeConstantSize() { 438 mComputedConstantSize = true; 439 440 final int N = getChildCount(); 441 final Drawable[] drawables = mDrawables; 442 mConstantWidth = mConstantHeight = 0; 443 mConstantMinimumWidth = mConstantMinimumHeight = 0; 444 for (int i = 0; i < N; i++) { 445 Drawable dr = drawables[i]; 446 int s = dr.getIntrinsicWidth(); 447 if (s > mConstantWidth) mConstantWidth = s; 448 s = dr.getIntrinsicHeight(); 449 if (s > mConstantHeight) mConstantHeight = s; 450 s = dr.getMinimumWidth(); 451 if (s > mConstantMinimumWidth) mConstantMinimumWidth = s; 452 s = dr.getMinimumHeight(); 453 if (s > mConstantMinimumHeight) mConstantMinimumHeight = s; 454 } 455 } 456 457 public final int getOpacity() { 458 if (mHaveOpacity) { 459 return mOpacity; 460 } 461 462 final int N = getChildCount(); 463 final Drawable[] drawables = mDrawables; 464 int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT; 465 for (int i = 1; i < N; i++) { 466 op = Drawable.resolveOpacity(op, drawables[i].getOpacity()); 467 } 468 mOpacity = op; 469 mHaveOpacity = true; 470 return op; 471 } 472 473 public final boolean isStateful() { 474 if (mHaveStateful) { 475 return mStateful; 476 } 477 478 boolean stateful = false; 479 final int N = getChildCount(); 480 for (int i = 0; i < N; i++) { 481 if (mDrawables[i].isStateful()) { 482 stateful = true; 483 break; 484 } 485 } 486 487 mStateful = stateful; 488 mHaveStateful = true; 489 return stateful; 490 } 491 492 public void growArray(int oldSize, int newSize) { 493 Drawable[] newDrawables = new Drawable[newSize]; 494 System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize); 495 mDrawables = newDrawables; 496 } 497 498 public synchronized boolean canConstantState() { 499 if (!mCheckedConstantState) { 500 mCanConstantState = true; 501 final int N = mNumChildren; 502 for (int i=0; i<N; i++) { 503 if (mDrawables[i].getConstantState() == null) { 504 mCanConstantState = false; 505 break; 506 } 507 } 508 mCheckedConstantState = true; 509 } 510 511 return mCanConstantState; 512 } 513 } 514 515 protected void setConstantState(DrawableContainerState state) 516 { 517 mDrawableContainerState = state; 518 } 519} 520