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