/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics.drawable; import android.graphics.*; public class DrawableContainer extends Drawable implements Drawable.Callback { private DrawableContainerState mDrawableContainerState; private Drawable mCurrDrawable; private int mAlpha = 0xFF; private ColorFilter mColorFilter; private boolean mDither; private int mCurIndex = -1; private boolean mMutated; // overrides from Drawable @Override public void draw(Canvas canvas) { if (mCurrDrawable != null) { mCurrDrawable.draw(canvas); } } @Override public int getChangingConfigurations() { return super.getChangingConfigurations() | mDrawableContainerState.mChangingConfigurations | mDrawableContainerState.mChildrenChangingConfigurations; } @Override public boolean getPadding(Rect padding) { final Rect r = mDrawableContainerState.getConstantPadding(); if (r != null) { padding.set(r); return true; } if (mCurrDrawable != null) { return mCurrDrawable.getPadding(padding); } else { return super.getPadding(padding); } } @Override public void setAlpha(int alpha) { if (mAlpha != alpha) { mAlpha = alpha; if (mCurrDrawable != null) { mCurrDrawable.setAlpha(alpha); } } } @Override public void setDither(boolean dither) { if (mDither != dither) { mDither = dither; if (mCurrDrawable != null) { mCurrDrawable.setDither(mDither); } } } @Override public void setColorFilter(ColorFilter cf) { if (mColorFilter != cf) { mColorFilter = cf; if (mCurrDrawable != null) { mCurrDrawable.setColorFilter(cf); } } } @Override protected void onBoundsChange(Rect bounds) { if (mCurrDrawable != null) { mCurrDrawable.setBounds(bounds); } } @Override public boolean isStateful() { return mDrawableContainerState.isStateful(); } @Override protected boolean onStateChange(int[] state) { if (mCurrDrawable != null) { return mCurrDrawable.setState(state); } return false; } @Override protected boolean onLevelChange(int level) { if (mCurrDrawable != null) { return mCurrDrawable.setLevel(level); } return false; } @Override public int getIntrinsicWidth() { if (mDrawableContainerState.isConstantSize()) { return mDrawableContainerState.getConstantWidth(); } return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1; } @Override public int getIntrinsicHeight() { if (mDrawableContainerState.isConstantSize()) { return mDrawableContainerState.getConstantHeight(); } return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1; } @Override public int getMinimumWidth() { if (mDrawableContainerState.isConstantSize()) { return mDrawableContainerState.getConstantMinimumWidth(); } return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0; } @Override public int getMinimumHeight() { if (mDrawableContainerState.isConstantSize()) { return mDrawableContainerState.getConstantMinimumHeight(); } return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0; } public void invalidateDrawable(Drawable who) { if (who == mCurrDrawable && mCallback != null) { mCallback.invalidateDrawable(this); } } public void scheduleDrawable(Drawable who, Runnable what, long when) { if (who == mCurrDrawable && mCallback != null) { mCallback.scheduleDrawable(this, what, when); } } public void unscheduleDrawable(Drawable who, Runnable what) { if (who == mCurrDrawable && mCallback != null) { mCallback.unscheduleDrawable(this, what); } } @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); if (mCurrDrawable != null) { mCurrDrawable.setVisible(visible, restart); } return changed; } @Override public int getOpacity() { return mDrawableContainerState.getOpacity(); } public boolean selectDrawable(int idx) { if (idx == mCurIndex) { return false; } if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { Drawable d = mDrawableContainerState.mDrawables[idx]; if (mCurrDrawable != null) { mCurrDrawable.setVisible(false, false); } mCurrDrawable = d; mCurIndex = idx; if (d != null) { d.setVisible(isVisible(), true); d.setAlpha(mAlpha); d.setDither(mDither); d.setColorFilter(mColorFilter); d.setState(getState()); d.setLevel(getLevel()); d.setBounds(getBounds()); } } else { if (mCurrDrawable != null) { mCurrDrawable.setVisible(false, false); } mCurrDrawable = null; mCurIndex = -1; } invalidateSelf(); return true; } @Override public Drawable getCurrent() { return mCurrDrawable; } @Override public ConstantState getConstantState() { if (mDrawableContainerState.canConstantState()) { mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations(); return mDrawableContainerState; } return null; } @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { for (Drawable child : mDrawableContainerState.mDrawables) { child.mutate(); } mMutated = true; } return this; } public abstract static class DrawableContainerState extends ConstantState { final DrawableContainer mOwner; int mChangingConfigurations; int mChildrenChangingConfigurations; Drawable[] mDrawables; int mNumChildren; boolean mVariablePadding = false; Rect mConstantPadding = null; boolean mConstantSize = false; boolean mComputedConstantSize = false; int mConstantWidth; int mConstantHeight; int mConstantMinimumWidth; int mConstantMinimumHeight; boolean mHaveOpacity = false; int mOpacity; boolean mHaveStateful = false; boolean mStateful; boolean mCheckedConstantState; boolean mCanConstantState; DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) { mOwner = owner; if (orig != null) { mChangingConfigurations = orig.mChangingConfigurations; mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; final Drawable[] origDr = orig.mDrawables; mDrawables = new Drawable[origDr.length]; mNumChildren = orig.mNumChildren; final int N = mNumChildren; for (int i=0; i<N; i++) { mDrawables[i] = origDr[i].getConstantState().newDrawable(); mDrawables[i].setCallback(owner); } mCheckedConstantState = mCanConstantState = true; mVariablePadding = orig.mVariablePadding; if (orig.mConstantPadding != null) { mConstantPadding = new Rect(orig.mConstantPadding); } mConstantSize = orig.mConstantSize; mComputedConstantSize = orig.mComputedConstantSize; mConstantWidth = orig.mConstantWidth; mConstantHeight = orig.mConstantHeight; mHaveOpacity = orig.mHaveOpacity; mOpacity = orig.mOpacity; mHaveStateful = orig.mHaveStateful; mStateful = orig.mStateful; } else { mDrawables = new Drawable[10]; mNumChildren = 0; mCheckedConstantState = mCanConstantState = false; } } @Override public int getChangingConfigurations() { return mChangingConfigurations; } public final int addChild(Drawable dr) { final int pos = mNumChildren; if (pos >= mDrawables.length) { growArray(pos, pos+10); } dr.setVisible(false, true); dr.setCallback(mOwner); mDrawables[pos] = dr; mNumChildren++; mChildrenChangingConfigurations |= dr.getChangingConfigurations(); mHaveOpacity = false; mHaveStateful = false; mConstantPadding = null; mComputedConstantSize = false; return pos; } public final int getChildCount() { return mNumChildren; } public final Drawable[] getChildren() { return mDrawables; } /** A boolean value indicating whether to use the maximum padding value of * all frames in the set (false), or to use the padding value of the frame * being shown (true). Default value is false. */ public final void setVariablePadding(boolean variable) { mVariablePadding = variable; } public final Rect getConstantPadding() { if (mVariablePadding) { return null; } if (mConstantPadding != null) { return mConstantPadding; } Rect r = new Rect(0, 0, 0, 0); Rect t = new Rect(); final int N = getChildCount(); for (int i=0; i<N; i++) { if (mDrawables[i].getPadding(t)) { if (t.left > r.left) r.left = t.left; if (t.top > r.top) r.top = t.top; if (t.right > r.right) r.right = t.right; if (t.bottom > r.bottom) r.bottom = t.bottom; } } return (mConstantPadding=r); } public final void setConstantSize(boolean constant) { mConstantSize = constant; } public final boolean isConstantSize() { return mConstantSize; } public final int getConstantWidth() { if (!mComputedConstantSize) { computeConstantSize(); } return mConstantWidth; } public final int getConstantHeight() { if (!mComputedConstantSize) { computeConstantSize(); } return mConstantHeight; } public final int getConstantMinimumWidth() { if (!mComputedConstantSize) { computeConstantSize(); } return mConstantMinimumWidth; } public final int getConstantMinimumHeight() { if (!mComputedConstantSize) { computeConstantSize(); } return mConstantMinimumHeight; } private void computeConstantSize() { mComputedConstantSize = true; final int N = getChildCount(); mConstantWidth = mConstantHeight = 0; mConstantMinimumWidth = mConstantMinimumHeight = 0; for (int i=0; i<N; i++) { Drawable dr = mDrawables[i]; int s = dr.getIntrinsicWidth(); if (s > mConstantWidth) mConstantWidth = s; s = dr.getIntrinsicHeight(); if (s > mConstantHeight) mConstantHeight = s; s = dr.getMinimumWidth(); if (s > mConstantMinimumWidth) mConstantMinimumWidth = s; s = dr.getMinimumHeight(); if (s > mConstantMinimumHeight) mConstantMinimumHeight = s; } } public final int getOpacity() { if (mHaveOpacity) { return mOpacity; } final int N = getChildCount(); int op = N > 0 ? mDrawables[0].getOpacity() : PixelFormat.TRANSPARENT; for (int i=1; i<N; i++) { op = Drawable.resolveOpacity(op, mDrawables[i].getOpacity()); } mOpacity = op; mHaveOpacity = true; return op; } public final boolean isStateful() { if (mHaveStateful) { return mStateful; } boolean stateful = false; final int N = getChildCount(); for (int i = 0; i < N; i++) { if (mDrawables[i].isStateful()) { stateful = true; break; } } mStateful = stateful; mHaveStateful = true; return stateful; } public void growArray(int oldSize, int newSize) { Drawable[] newDrawables = new Drawable[newSize]; System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize); mDrawables = newDrawables; } public synchronized boolean canConstantState() { if (!mCheckedConstantState) { mCanConstantState = true; final int N = mNumChildren; for (int i=0; i<N; i++) { if (mDrawables[i].getConstantState() == null) { mCanConstantState = false; break; } } mCheckedConstantState = true; } return mCanConstantState; } } protected void setConstantState(DrawableContainerState state) { mDrawableContainerState = state; } }