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