NinePatchDrawable.java revision 11ea33471e1a14a8594f0b2cd012d86340dd3bd8
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 android.graphics.*; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.util.AttributeSet; 23import android.util.DisplayMetrics; 24import android.util.TypedValue; 25import org.xmlpull.v1.XmlPullParser; 26import org.xmlpull.v1.XmlPullParserException; 27 28import java.io.IOException; 29import java.io.InputStream; 30 31/** 32 * 33 * A resizeable bitmap, with stretchable areas that you define. This type of image 34 * is defined in a .png file with a special format, described in <a link="../../../resources.html#ninepatch"> 35 * Resources</a>. 36 * 37 */ 38public class NinePatchDrawable extends Drawable { 39 private NinePatchState mNinePatchState; 40 private NinePatch mNinePatch; 41 private Rect mPadding; 42 private Paint mPaint; 43 private boolean mMutated; 44 45 private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; 46 47 // These are scaled to match the target density. 48 private int mBitmapWidth; 49 private int mBitmapHeight; 50 51 NinePatchDrawable() { 52 } 53 54 /** 55 * Create drawable from raw nine-patch data, not dealing with density. 56 * @deprecated Use {@link #NinePatchDrawable(Resources, Bitmap, byte[], Rect, String)} 57 * to ensure that the drawable has correctly set its target density. 58 */ 59 public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) { 60 this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); 61 } 62 63 /** 64 * Create drawable from raw nine-patch data, setting initial target density 65 * based on the display metrics of the resources. 66 */ 67 public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk, 68 Rect padding, String srcName) { 69 this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); 70 if (res != null) { 71 setTargetDensity(res.getDisplayMetrics()); 72 mNinePatchState.mTargetDensity = mTargetDensity; 73 } 74 } 75 76 /** 77 * Create drawable from existing nine-patch, not dealing with density. 78 * @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)} 79 * to ensure that the drawable has correctly set its target density. 80 */ 81 public NinePatchDrawable(NinePatch patch) { 82 this(new NinePatchState(patch, null)); 83 } 84 85 /** 86 * Create drawable from existing nine-patch, setting initial target density 87 * based on the display metrics of the resources. 88 */ 89 public NinePatchDrawable(Resources res, NinePatch patch) { 90 this(new NinePatchState(patch, null)); 91 if (res != null) { 92 setTargetDensity(res.getDisplayMetrics()); 93 mNinePatchState.mTargetDensity = mTargetDensity; 94 } 95 } 96 97 private void setNinePatchState(NinePatchState state) { 98 mNinePatchState = state; 99 mNinePatch = state.mNinePatch; 100 mPadding = state.mPadding; 101 mTargetDensity = state.mTargetDensity; 102 if (state.mDither) setDither(state.mDither); 103 if (mNinePatch != null) { 104 computeBitmapSize(); 105 } 106 } 107 108 /** 109 * Set the density scale at which this drawable will be rendered. This 110 * method assumes the drawable will be rendered at the same density as the 111 * specified canvas. 112 * 113 * @param canvas The Canvas from which the density scale must be obtained. 114 * 115 * @see android.graphics.Bitmap#setDensity(int) 116 * @see android.graphics.Bitmap#getDensity() 117 */ 118 public void setTargetDensity(Canvas canvas) { 119 setTargetDensity(canvas.getDensity()); 120 } 121 122 /** 123 * Set the density scale at which this drawable will be rendered. 124 * 125 * @param metrics The DisplayMetrics indicating the density scale for this drawable. 126 * 127 * @see android.graphics.Bitmap#setDensity(int) 128 * @see android.graphics.Bitmap#getDensity() 129 */ 130 public void setTargetDensity(DisplayMetrics metrics) { 131 mTargetDensity = metrics.densityDpi; 132 if (mNinePatch != null) { 133 computeBitmapSize(); 134 } 135 } 136 137 /** 138 * Set the density at which this drawable will be rendered. 139 * 140 * @param density The density scale for this drawable. 141 * 142 * @see android.graphics.Bitmap#setDensity(int) 143 * @see android.graphics.Bitmap#getDensity() 144 */ 145 public void setTargetDensity(int density) { 146 mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density; 147 if (mNinePatch != null) { 148 computeBitmapSize(); 149 } 150 } 151 152 private void computeBitmapSize() { 153 final int sdensity = mNinePatch.getDensity(); 154 final int tdensity = mTargetDensity; 155 mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), 156 sdensity, tdensity); 157 mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), 158 sdensity, tdensity); 159 } 160 161 // overrides 162 163 @Override 164 public void draw(Canvas canvas) { 165 mNinePatch.draw(canvas, getBounds(), mPaint); 166 } 167 168 @Override 169 public int getChangingConfigurations() { 170 return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations; 171 } 172 173 @Override 174 public boolean getPadding(Rect padding) { 175 padding.set(mPadding); 176 return true; 177 } 178 179 @Override 180 public void setAlpha(int alpha) { 181 getPaint().setAlpha(alpha); 182 } 183 184 @Override 185 public void setColorFilter(ColorFilter cf) { 186 getPaint().setColorFilter(cf); 187 } 188 189 @Override 190 public void setDither(boolean dither) { 191 getPaint().setDither(dither); 192 } 193 194 @Override 195 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) 196 throws XmlPullParserException, IOException { 197 super.inflate(r, parser, attrs); 198 199 TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.NinePatchDrawable); 200 201 final int id = a.getResourceId(com.android.internal.R.styleable.NinePatchDrawable_src, 0); 202 if (id == 0) { 203 throw new XmlPullParserException(parser.getPositionDescription() + 204 ": <nine-patch> requires a valid src attribute"); 205 } 206 207 final boolean dither = a.getBoolean( 208 com.android.internal.R.styleable.NinePatchDrawable_dither, false); 209 final BitmapFactory.Options options = new BitmapFactory.Options(); 210 if (dither) { 211 options.inDither = false; 212 } 213 options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; 214 215 final Rect padding = new Rect(); 216 Bitmap bitmap = null; 217 218 try { 219 final TypedValue value = new TypedValue(); 220 final InputStream is = r.openRawResource(id, value); 221 222 bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); 223 224 is.close(); 225 } catch (IOException e) { 226 // Ignore 227 } 228 229 if (bitmap == null) { 230 throw new XmlPullParserException(parser.getPositionDescription() + 231 ": <nine-patch> requires a valid src attribute"); 232 } else if (bitmap.getNinePatchChunk() == null) { 233 throw new XmlPullParserException(parser.getPositionDescription() + 234 ": <nine-patch> requires a valid 9-patch source image"); 235 } 236 237 setNinePatchState(new NinePatchState( 238 new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"), padding, dither)); 239 mNinePatchState.mTargetDensity = mTargetDensity; 240 241 a.recycle(); 242 } 243 244 245 public Paint getPaint() { 246 if (mPaint == null) { 247 mPaint = new Paint(); 248 } 249 return mPaint; 250 } 251 252 /** 253 * Retrieves the width of the source .png file (before resizing). 254 */ 255 @Override 256 public int getIntrinsicWidth() { 257 return mBitmapWidth; 258 } 259 260 /** 261 * Retrieves the height of the source .png file (before resizing). 262 */ 263 @Override 264 public int getIntrinsicHeight() { 265 return mBitmapHeight; 266 } 267 268 @Override 269 public int getMinimumWidth() { 270 return mBitmapWidth; 271 } 272 273 @Override 274 public int getMinimumHeight() { 275 return mBitmapHeight; 276 } 277 278 /** 279 * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat} 280 * value of OPAQUE or TRANSLUCENT. 281 */ 282 @Override 283 public int getOpacity() { 284 return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ? 285 PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; 286 } 287 288 @Override 289 public Region getTransparentRegion() { 290 return mNinePatch.getTransparentRegion(getBounds()); 291 } 292 293 @Override 294 public ConstantState getConstantState() { 295 mNinePatchState.mChangingConfigurations = super.getChangingConfigurations(); 296 return mNinePatchState; 297 } 298 299 @Override 300 public Drawable mutate() { 301 if (!mMutated && super.mutate() == this) { 302 mNinePatchState = new NinePatchState(mNinePatchState); 303 mNinePatch = mNinePatchState.mNinePatch; 304 mPadding = mNinePatchState.mPadding; 305 mMutated = true; 306 } 307 return this; 308 } 309 310 final static class NinePatchState extends ConstantState { 311 final NinePatch mNinePatch; 312 final Rect mPadding; 313 final boolean mDither; 314 int mChangingConfigurations; 315 int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; 316 317 NinePatchState(NinePatch ninePatch, Rect padding) { 318 this(ninePatch, padding, false); 319 } 320 321 NinePatchState(NinePatch ninePatch, Rect rect, boolean dither) { 322 mNinePatch = ninePatch; 323 mPadding = rect; 324 mDither = dither; 325 } 326 327 NinePatchState(NinePatchState state) { 328 mNinePatch = new NinePatch(state.mNinePatch); 329 mPadding = new Rect(state.mPadding); 330 mDither = state.mDither; 331 mChangingConfigurations = state.mChangingConfigurations; 332 mTargetDensity = state.mTargetDensity; 333 } 334 335 @Override 336 public Drawable newDrawable() { 337 return new NinePatchDrawable(this); 338 } 339 340 @Override 341 public int getChangingConfigurations() { 342 return mChangingConfigurations; 343 } 344 } 345 346 private NinePatchDrawable(NinePatchState state) { 347 setNinePatchState(state); 348 } 349} 350 351