ScaleDrawable.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
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 org.xmlpull.v1.XmlPullParser; 20import org.xmlpull.v1.XmlPullParserException; 21 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.graphics.*; 25import android.view.Gravity; 26import android.util.AttributeSet; 27 28import java.io.IOException; 29 30/** 31 * A Drawable that changes the size of another Drawable based on its current 32 * level value. You can control how much the child Drawable changes in width 33 * and height based on the level, as well as a gravity to control where it is 34 * placed in its overall container. Most often used to implement things like 35 * progress bars. 36 * 37 * <p>It can be defined in an XML file with the <code><scale></code> element.</p> 38 * 39 * @attr ref android.R.styleable#ScaleDrawable_scaleWidth 40 * @attr ref android.R.styleable#ScaleDrawable_scaleHeight 41 * @attr ref android.R.styleable#ScaleDrawable_scaleGravity 42 * @attr ref android.R.styleable#ScaleDrawable_drawable 43 */ 44public class ScaleDrawable extends Drawable implements Drawable.Callback { 45 private ScaleState mScaleState; 46 private boolean mMutated; 47 private final Rect mTmpRect = new Rect(); 48 49 ScaleDrawable() { 50 this(null); 51 } 52 53 public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) { 54 this(null); 55 56 mScaleState.mDrawable = drawable; 57 mScaleState.mGravity = gravity; 58 mScaleState.mScaleWidth = scaleWidth; 59 mScaleState.mScaleHeight = scaleHeight; 60 61 if (drawable != null) { 62 drawable.setCallback(this); 63 } 64 } 65 66 /** 67 * Returns the drawable scaled by this ScaleDrawable. 68 */ 69 public Drawable getDrawable() { 70 return mScaleState.mDrawable; 71 } 72 73 private static float getPercent(TypedArray a, int name) { 74 String s = a.getString(name); 75 if (s != null) { 76 if (s.endsWith("%")) { 77 String f = s.substring(0, s.length() - 1); 78 return Float.parseFloat(f) / 100.0f; 79 } 80 } 81 return -1; 82 } 83 84 @Override 85 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) 86 throws XmlPullParserException, IOException { 87 super.inflate(r, parser, attrs); 88 89 int type; 90 91 TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ScaleDrawable); 92 93 float sw = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleWidth); 94 float sh = getPercent(a, com.android.internal.R.styleable.ScaleDrawable_scaleHeight); 95 int g = a.getInt(com.android.internal.R.styleable.ScaleDrawable_scaleGravity, Gravity.LEFT); 96 Drawable dr = a.getDrawable(com.android.internal.R.styleable.ScaleDrawable_drawable); 97 98 a.recycle(); 99 100 final int outerDepth = parser.getDepth(); 101 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 102 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 103 if (type != XmlPullParser.START_TAG) { 104 continue; 105 } 106 dr = Drawable.createFromXmlInner(r, parser, attrs); 107 } 108 109 if (dr == null) { 110 throw new IllegalArgumentException("No drawable specified for <scale>"); 111 } 112 113 mScaleState.mDrawable = dr; 114 mScaleState.mScaleWidth = sw; 115 mScaleState.mScaleHeight = sh; 116 mScaleState.mGravity = g; 117 if (dr != null) { 118 dr.setCallback(this); 119 } 120 } 121 122 // overrides from Drawable.Callback 123 124 public void invalidateDrawable(Drawable who) { 125 if (mCallback != null) { 126 mCallback.invalidateDrawable(this); 127 } 128 } 129 130 public void scheduleDrawable(Drawable who, Runnable what, long when) { 131 if (mCallback != null) { 132 mCallback.scheduleDrawable(this, what, when); 133 } 134 } 135 136 public void unscheduleDrawable(Drawable who, Runnable what) { 137 if (mCallback != null) { 138 mCallback.unscheduleDrawable(this, what); 139 } 140 } 141 142 // overrides from Drawable 143 144 @Override 145 public void draw(Canvas canvas) { 146 if (mScaleState.mDrawable.getLevel() != 0) 147 mScaleState.mDrawable.draw(canvas); 148 } 149 150 @Override 151 public int getChangingConfigurations() { 152 return super.getChangingConfigurations() 153 | mScaleState.mChangingConfigurations 154 | mScaleState.mDrawable.getChangingConfigurations(); 155 } 156 157 @Override 158 public boolean getPadding(Rect padding) { 159 // XXX need to adjust padding! 160 return mScaleState.mDrawable.getPadding(padding); 161 } 162 163 @Override 164 public boolean setVisible(boolean visible, boolean restart) { 165 mScaleState.mDrawable.setVisible(visible, restart); 166 return super.setVisible(visible, restart); 167 } 168 169 @Override 170 public void setAlpha(int alpha) { 171 mScaleState.mDrawable.setAlpha(alpha); 172 } 173 174 @Override 175 public void setColorFilter(ColorFilter cf) { 176 mScaleState.mDrawable.setColorFilter(cf); 177 } 178 179 @Override 180 public int getOpacity() { 181 return mScaleState.mDrawable.getOpacity(); 182 } 183 184 @Override 185 public boolean isStateful() { 186 return mScaleState.mDrawable.isStateful(); 187 } 188 189 @Override 190 protected boolean onStateChange(int[] state) { 191 boolean changed = mScaleState.mDrawable.setState(state); 192 onBoundsChange(getBounds()); 193 return changed; 194 } 195 196 @Override 197 protected boolean onLevelChange(int level) { 198 mScaleState.mDrawable.setLevel(level); 199 onBoundsChange(getBounds()); 200 invalidateSelf(); 201 return true; 202 } 203 204 @Override 205 protected void onBoundsChange(Rect bounds) { 206 final Rect r = mTmpRect; 207 int level = getLevel(); 208 int w = bounds.width(); 209 final int iw = 0; //mScaleState.mDrawable.getIntrinsicWidth(); 210 if (mScaleState.mScaleWidth > 0) { 211 w -= (int) ((w - iw) * (10000 - level) * mScaleState.mScaleWidth / 10000); 212 } 213 int h = bounds.height(); 214 final int ih = 0; //mScaleState.mDrawable.getIntrinsicHeight(); 215 if (mScaleState.mScaleHeight > 0) { 216 h -= (int) ((h - ih) * (10000 - level) * mScaleState.mScaleHeight / 10000); 217 } 218 Gravity.apply(mScaleState.mGravity, w, h, bounds, r); 219 220 if (w > 0 && h > 0) { 221 mScaleState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); 222 } 223 } 224 225 @Override 226 public int getIntrinsicWidth() { 227 return mScaleState.mDrawable.getIntrinsicWidth(); 228 } 229 230 @Override 231 public int getIntrinsicHeight() { 232 return mScaleState.mDrawable.getIntrinsicHeight(); 233 } 234 235 @Override 236 public ConstantState getConstantState() { 237 if (mScaleState.canConstantState()) { 238 mScaleState.mChangingConfigurations = super.getChangingConfigurations(); 239 return mScaleState; 240 } 241 return null; 242 } 243 244 @Override 245 public Drawable mutate() { 246 if (!mMutated && super.mutate() == this) { 247 mScaleState.mDrawable.mutate(); 248 mMutated = true; 249 } 250 return this; 251 } 252 253 final static class ScaleState extends ConstantState { 254 Drawable mDrawable; 255 int mChangingConfigurations; 256 float mScaleWidth; 257 float mScaleHeight; 258 int mGravity; 259 260 private boolean mCheckedConstantState; 261 private boolean mCanConstantState; 262 263 ScaleState(ScaleState orig, ScaleDrawable owner) { 264 if (orig != null) { 265 mDrawable = orig.mDrawable.getConstantState().newDrawable(); 266 mDrawable.setCallback(owner); 267 mScaleWidth = orig.mScaleWidth; 268 mScaleHeight = orig.mScaleHeight; 269 mGravity = orig.mGravity; 270 mCheckedConstantState = mCanConstantState = true; 271 } 272 } 273 274 @Override 275 public Drawable newDrawable() { 276 return new ScaleDrawable(this); 277 } 278 279 @Override 280 public int getChangingConfigurations() { 281 return mChangingConfigurations; 282 } 283 284 boolean canConstantState() { 285 if (!mCheckedConstantState) { 286 mCanConstantState = mDrawable.getConstantState() != null; 287 mCheckedConstantState = true; 288 } 289 290 return mCanConstantState; 291 } 292 } 293 294 private ScaleDrawable(ScaleState state) { 295 mScaleState = new ScaleState(state, this); 296 } 297} 298 299