InsetDrawable.java revision b3c56086d802ae28888dd97ba1f49bd6cee0b673
1/* 2 * Copyright (C) 2008 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.ColorStateList; 23import android.content.res.Resources; 24import android.content.res.TypedArray; 25import android.content.res.Resources.Theme; 26import android.graphics.*; 27import android.graphics.PorterDuff.Mode; 28import android.util.AttributeSet; 29import android.util.Log; 30 31import java.io.IOException; 32 33/** 34 * A Drawable that insets another Drawable by a specified distance. 35 * This is used when a View needs a background that is smaller than 36 * the View's actual bounds. 37 * 38 * <p>It can be defined in an XML file with the <code><inset></code> element. For more 39 * information, see the guide to <a 40 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p> 41 * 42 * @attr ref android.R.styleable#InsetDrawable_visible 43 * @attr ref android.R.styleable#InsetDrawable_drawable 44 * @attr ref android.R.styleable#InsetDrawable_insetLeft 45 * @attr ref android.R.styleable#InsetDrawable_insetRight 46 * @attr ref android.R.styleable#InsetDrawable_insetTop 47 * @attr ref android.R.styleable#InsetDrawable_insetBottom 48 */ 49public class InsetDrawable extends Drawable implements Drawable.Callback { 50 // Most of this is copied from ScaleDrawable. 51 private InsetState mInsetState; 52 private final Rect mTmpRect = new Rect(); 53 private boolean mMutated; 54 55 /*package*/ InsetDrawable() { 56 this(null, null); 57 } 58 59 public InsetDrawable(Drawable drawable, int inset) { 60 this(drawable, inset, inset, inset, inset); 61 } 62 63 public InsetDrawable(Drawable drawable, int insetLeft, int insetTop, 64 int insetRight, int insetBottom) { 65 this(null, null); 66 67 mInsetState.mDrawable = drawable; 68 mInsetState.mInsetLeft = insetLeft; 69 mInsetState.mInsetTop = insetTop; 70 mInsetState.mInsetRight = insetRight; 71 mInsetState.mInsetBottom = insetBottom; 72 73 if (drawable != null) { 74 drawable.setCallback(this); 75 } 76 } 77 78 @Override 79 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 80 throws XmlPullParserException, IOException { 81 int type; 82 83 TypedArray a = r.obtainAttributes(attrs, 84 com.android.internal.R.styleable.InsetDrawable); 85 86 super.inflateWithAttributes(r, parser, a, 87 com.android.internal.R.styleable.InsetDrawable_visible); 88 89 int drawableRes = a.getResourceId(com.android.internal.R.styleable. 90 InsetDrawable_drawable, 0); 91 92 int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable. 93 InsetDrawable_insetLeft, 0); 94 int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable. 95 InsetDrawable_insetTop, 0); 96 int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable. 97 InsetDrawable_insetRight, 0); 98 int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable. 99 InsetDrawable_insetBottom, 0); 100 101 a.recycle(); 102 103 Drawable dr; 104 if (drawableRes != 0) { 105 dr = r.getDrawable(drawableRes); 106 } else { 107 while ((type=parser.next()) == XmlPullParser.TEXT) { 108 } 109 if (type != XmlPullParser.START_TAG) { 110 throw new XmlPullParserException( 111 parser.getPositionDescription() 112 + ": <inset> tag requires a 'drawable' attribute or " 113 + "child tag defining a drawable"); 114 } 115 dr = Drawable.createFromXmlInner(r, parser, attrs, theme); 116 } 117 118 if (dr == null) { 119 Log.w("drawable", "No drawable specified for <inset>"); 120 } 121 122 mInsetState.mDrawable = dr; 123 mInsetState.mInsetLeft = inLeft; 124 mInsetState.mInsetRight = inRight; 125 mInsetState.mInsetTop = inTop; 126 mInsetState.mInsetBottom = inBottom; 127 128 if (dr != null) { 129 dr.setCallback(this); 130 } 131 } 132 133 // overrides from Drawable.Callback 134 135 @Override 136 public void invalidateDrawable(Drawable who) { 137 final Callback callback = getCallback(); 138 if (callback != null) { 139 callback.invalidateDrawable(this); 140 } 141 } 142 143 @Override 144 public void scheduleDrawable(Drawable who, Runnable what, long when) { 145 final Callback callback = getCallback(); 146 if (callback != null) { 147 callback.scheduleDrawable(this, what, when); 148 } 149 } 150 151 @Override 152 public void unscheduleDrawable(Drawable who, Runnable what) { 153 final Callback callback = getCallback(); 154 if (callback != null) { 155 callback.unscheduleDrawable(this, what); 156 } 157 } 158 159 // overrides from Drawable 160 161 @Override 162 public void draw(Canvas canvas) { 163 mInsetState.mDrawable.draw(canvas); 164 } 165 166 @Override 167 public int getChangingConfigurations() { 168 return super.getChangingConfigurations() 169 | mInsetState.mChangingConfigurations 170 | mInsetState.mDrawable.getChangingConfigurations(); 171 } 172 173 @Override 174 public boolean getPadding(Rect padding) { 175 boolean pad = mInsetState.mDrawable.getPadding(padding); 176 177 padding.left += mInsetState.mInsetLeft; 178 padding.right += mInsetState.mInsetRight; 179 padding.top += mInsetState.mInsetTop; 180 padding.bottom += mInsetState.mInsetBottom; 181 182 if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight | 183 mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) { 184 return true; 185 } else { 186 return false; 187 } 188 } 189 190 @Override 191 public void setHotspot(float x, float y) { 192 mInsetState.mDrawable.setHotspot(x, y); 193 } 194 195 @Override 196 public void setHotspotBounds(int left, int top, int right, int bottom) { 197 mInsetState.mDrawable.setHotspotBounds(left, top, right, bottom); 198 } 199 200 @Override 201 public boolean setVisible(boolean visible, boolean restart) { 202 mInsetState.mDrawable.setVisible(visible, restart); 203 return super.setVisible(visible, restart); 204 } 205 206 @Override 207 public void setAlpha(int alpha) { 208 mInsetState.mDrawable.setAlpha(alpha); 209 } 210 211 @Override 212 public int getAlpha() { 213 return mInsetState.mDrawable.getAlpha(); 214 } 215 216 @Override 217 public void setColorFilter(ColorFilter cf) { 218 mInsetState.mDrawable.setColorFilter(cf); 219 } 220 221 @Override 222 public void setTint(ColorStateList tint, Mode tintMode) { 223 mInsetState.mDrawable.setTint(tint, tintMode); 224 } 225 226 /** {@hide} */ 227 @Override 228 public void setLayoutDirection(int layoutDirection) { 229 mInsetState.mDrawable.setLayoutDirection(layoutDirection); 230 } 231 232 @Override 233 public int getOpacity() { 234 return mInsetState.mDrawable.getOpacity(); 235 } 236 237 @Override 238 public boolean isStateful() { 239 return mInsetState.mDrawable.isStateful(); 240 } 241 242 @Override 243 protected boolean onStateChange(int[] state) { 244 boolean changed = mInsetState.mDrawable.setState(state); 245 onBoundsChange(getBounds()); 246 return changed; 247 } 248 249 @Override 250 protected boolean onLevelChange(int level) { 251 return mInsetState.mDrawable.setLevel(level); 252 } 253 254 @Override 255 protected void onBoundsChange(Rect bounds) { 256 final Rect r = mTmpRect; 257 r.set(bounds); 258 259 r.left += mInsetState.mInsetLeft; 260 r.top += mInsetState.mInsetTop; 261 r.right -= mInsetState.mInsetRight; 262 r.bottom -= mInsetState.mInsetBottom; 263 264 mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom); 265 } 266 267 @Override 268 public int getIntrinsicWidth() { 269 return mInsetState.mDrawable.getIntrinsicWidth(); 270 } 271 272 @Override 273 public int getIntrinsicHeight() { 274 return mInsetState.mDrawable.getIntrinsicHeight(); 275 } 276 277 @Override 278 public ConstantState getConstantState() { 279 if (mInsetState.canConstantState()) { 280 mInsetState.mChangingConfigurations = getChangingConfigurations(); 281 return mInsetState; 282 } 283 return null; 284 } 285 286 @Override 287 public Drawable mutate() { 288 if (!mMutated && super.mutate() == this) { 289 mInsetState.mDrawable.mutate(); 290 mMutated = true; 291 } 292 return this; 293 } 294 295 /** 296 * Returns the drawable wrapped by this InsetDrawable. May be null. 297 */ 298 public Drawable getDrawable() { 299 return mInsetState.mDrawable; 300 } 301 302 final static class InsetState extends ConstantState { 303 Drawable mDrawable; 304 int mChangingConfigurations; 305 306 int mInsetLeft; 307 int mInsetTop; 308 int mInsetRight; 309 int mInsetBottom; 310 311 boolean mCheckedConstantState; 312 boolean mCanConstantState; 313 314 InsetState(InsetState orig, InsetDrawable owner, Resources res) { 315 if (orig != null) { 316 if (res != null) { 317 mDrawable = orig.mDrawable.getConstantState().newDrawable(res); 318 } else { 319 mDrawable = orig.mDrawable.getConstantState().newDrawable(); 320 } 321 mDrawable.setCallback(owner); 322 mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection()); 323 mInsetLeft = orig.mInsetLeft; 324 mInsetTop = orig.mInsetTop; 325 mInsetRight = orig.mInsetRight; 326 mInsetBottom = orig.mInsetBottom; 327 mCheckedConstantState = mCanConstantState = true; 328 } 329 } 330 331 @Override 332 public Drawable newDrawable() { 333 return new InsetDrawable(this, null); 334 } 335 336 @Override 337 public Drawable newDrawable(Resources res) { 338 return new InsetDrawable(this, res); 339 } 340 341 @Override 342 public int getChangingConfigurations() { 343 return mChangingConfigurations; 344 } 345 346 boolean canConstantState() { 347 if (!mCheckedConstantState) { 348 mCanConstantState = mDrawable.getConstantState() != null; 349 mCheckedConstantState = true; 350 } 351 352 return mCanConstantState; 353 } 354 } 355 356 private InsetDrawable(InsetState state, Resources res) { 357 mInsetState = new InsetState(state, this, res); 358 } 359} 360 361