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