ImageView.java revision f88872655e85570f3b9bfebe78647ed36916b3f4
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.widget; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.graphics.Bitmap; 23import android.graphics.Canvas; 24import android.graphics.ColorFilter; 25import android.graphics.Matrix; 26import android.graphics.PorterDuff; 27import android.graphics.PorterDuffColorFilter; 28import android.graphics.RectF; 29import android.graphics.drawable.BitmapDrawable; 30import android.graphics.drawable.Drawable; 31import android.net.Uri; 32import android.util.AttributeSet; 33import android.util.Log; 34import android.view.View; 35import android.view.accessibility.AccessibilityEvent; 36import android.view.accessibility.AccessibilityManager; 37import android.widget.RemoteViews.RemoteView; 38 39 40/** 41 * Displays an arbitrary image, such as an icon. The ImageView class 42 * can load images from various sources (such as resources or content 43 * providers), takes care of computing its measurement from the image so that 44 * it can be used in any layout manager, and provides various display options 45 * such as scaling and tinting. 46 * 47 * @attr ref android.R.styleable#ImageView_adjustViewBounds 48 * @attr ref android.R.styleable#ImageView_src 49 * @attr ref android.R.styleable#ImageView_maxWidth 50 * @attr ref android.R.styleable#ImageView_maxHeight 51 * @attr ref android.R.styleable#ImageView_tint 52 * @attr ref android.R.styleable#ImageView_scaleType 53 * @attr ref android.R.styleable#ImageView_cropToPadding 54 */ 55@RemoteView 56public class ImageView extends View { 57 // settable by the client 58 private Uri mUri; 59 private int mResource = 0; 60 private Matrix mMatrix; 61 private ScaleType mScaleType; 62 private boolean mHaveFrame = false; 63 private boolean mAdjustViewBounds = false; 64 private int mMaxWidth = Integer.MAX_VALUE; 65 private int mMaxHeight = Integer.MAX_VALUE; 66 67 // these are applied to the drawable 68 private ColorFilter mColorFilter; 69 private int mAlpha = 255; 70 private int mViewAlphaScale = 256; 71 72 private Drawable mDrawable = null; 73 private int[] mState = null; 74 private boolean mMergeState = false; 75 private int mLevel = 0; 76 private int mDrawableWidth; 77 private int mDrawableHeight; 78 private Matrix mDrawMatrix = null; 79 80 // Avoid allocations... 81 private RectF mTempSrc = new RectF(); 82 private RectF mTempDst = new RectF(); 83 84 private boolean mCropToPadding; 85 86 private boolean mBaselineAligned = false; 87 88 private static final ScaleType[] sScaleTypeArray = { 89 ScaleType.MATRIX, 90 ScaleType.FIT_XY, 91 ScaleType.FIT_START, 92 ScaleType.FIT_CENTER, 93 ScaleType.FIT_END, 94 ScaleType.CENTER, 95 ScaleType.CENTER_CROP, 96 ScaleType.CENTER_INSIDE 97 }; 98 99 public ImageView(Context context) { 100 super(context); 101 initImageView(); 102 } 103 104 public ImageView(Context context, AttributeSet attrs) { 105 this(context, attrs, 0); 106 } 107 108 public ImageView(Context context, AttributeSet attrs, int defStyle) { 109 super(context, attrs, defStyle); 110 initImageView(); 111 112 TypedArray a = context.obtainStyledAttributes(attrs, 113 com.android.internal.R.styleable.ImageView, defStyle, 0); 114 115 Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); 116 if (d != null) { 117 setImageDrawable(d); 118 } 119 120 mBaselineAligned = a.getBoolean( 121 com.android.internal.R.styleable.ImageView_baselineAlignBottom, false); 122 123 setAdjustViewBounds( 124 a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds, 125 false)); 126 127 setMaxWidth(a.getDimensionPixelSize( 128 com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE)); 129 130 setMaxHeight(a.getDimensionPixelSize( 131 com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE)); 132 133 int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1); 134 if (index >= 0) { 135 setScaleType(sScaleTypeArray[index]); 136 } 137 138 int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0); 139 if (tint != 0) { 140 setColorFilter(tint, PorterDuff.Mode.SRC_ATOP); 141 } 142 143 mCropToPadding = a.getBoolean( 144 com.android.internal.R.styleable.ImageView_cropToPadding, false); 145 146 a.recycle(); 147 148 //need inflate syntax/reader for matrix 149 } 150 151 private void initImageView() { 152 mMatrix = new Matrix(); 153 mScaleType = ScaleType.FIT_CENTER; 154 } 155 156 @Override 157 protected boolean verifyDrawable(Drawable dr) { 158 return mDrawable == dr || super.verifyDrawable(dr); 159 } 160 161 @Override 162 public void invalidateDrawable(Drawable dr) { 163 if (dr == mDrawable) { 164 /* we invalidate the whole view in this case because it's very 165 * hard to know where the drawable actually is. This is made 166 * complicated because of the offsets and transformations that 167 * can be applied. In theory we could get the drawable's bounds 168 * and run them through the transformation and offsets, but this 169 * is probably not worth the effort. 170 */ 171 invalidate(); 172 } else { 173 super.invalidateDrawable(dr); 174 } 175 } 176 177 @Override 178 protected boolean onSetAlpha(int alpha) { 179 if (getBackground() == null) { 180 int scale = alpha + (alpha >> 7); 181 if (mViewAlphaScale != scale) { 182 mViewAlphaScale = scale; 183 applyColorMod(); 184 } 185 return true; 186 } 187 return false; 188 } 189 190 /** 191 * Set this to true if you want the ImageView to adjust its bounds 192 * to preserve the aspect ratio of its drawable. 193 * @param adjustViewBounds Whether to adjust the bounds of this view 194 * to presrve the original aspect ratio of the drawable 195 * 196 * @attr ref android.R.styleable#ImageView_adjustViewBounds 197 */ 198 @android.view.RemotableViewMethod 199 public void setAdjustViewBounds(boolean adjustViewBounds) { 200 mAdjustViewBounds = adjustViewBounds; 201 if (adjustViewBounds) { 202 setScaleType(ScaleType.FIT_CENTER); 203 } 204 } 205 206 /** 207 * An optional argument to supply a maximum width for this view. Only valid if 208 * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x 209 * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to 210 * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to 211 * WRAP_CONTENT. 212 * 213 * <p> 214 * Note that this view could be still smaller than 100 x 100 using this approach if the original 215 * image is small. To set an image to a fixed size, specify that size in the layout params and 216 * then use {@link #setScaleType} to determine how to fit the image within the bounds. 217 * </p> 218 * 219 * @param maxWidth maximum width for this view 220 * 221 * @attr ref android.R.styleable#ImageView_maxWidth 222 */ 223 @android.view.RemotableViewMethod 224 public void setMaxWidth(int maxWidth) { 225 mMaxWidth = maxWidth; 226 } 227 228 /** 229 * An optional argument to supply a maximum height for this view. Only valid if 230 * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x 231 * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to 232 * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to 233 * WRAP_CONTENT. 234 * 235 * <p> 236 * Note that this view could be still smaller than 100 x 100 using this approach if the original 237 * image is small. To set an image to a fixed size, specify that size in the layout params and 238 * then use {@link #setScaleType} to determine how to fit the image within the bounds. 239 * </p> 240 * 241 * @param maxHeight maximum height for this view 242 * 243 * @attr ref android.R.styleable#ImageView_maxHeight 244 */ 245 @android.view.RemotableViewMethod 246 public void setMaxHeight(int maxHeight) { 247 mMaxHeight = maxHeight; 248 } 249 250 /** Return the view's drawable, or null if no drawable has been 251 assigned. 252 */ 253 public Drawable getDrawable() { 254 return mDrawable; 255 } 256 257 /** 258 * Sets a drawable as the content of this ImageView. 259 * 260 * @param resId the resource identifier of the the drawable 261 * 262 * @attr ref android.R.styleable#ImageView_src 263 */ 264 @android.view.RemotableViewMethod 265 public void setImageResource(int resId) { 266 if (mUri != null || mResource != resId) { 267 updateDrawable(null); 268 mResource = resId; 269 mUri = null; 270 resolveUri(); 271 requestLayout(); 272 invalidate(); 273 } 274 } 275 276 /** 277 * Sets the content of this ImageView to the specified Uri. 278 * 279 * @param uri The Uri of an image 280 */ 281 @android.view.RemotableViewMethod 282 public void setImageURI(Uri uri) { 283 if (mResource != 0 || 284 (mUri != uri && 285 (uri == null || mUri == null || !uri.equals(mUri)))) { 286 updateDrawable(null); 287 mResource = 0; 288 mUri = uri; 289 resolveUri(); 290 requestLayout(); 291 invalidate(); 292 } 293 } 294 295 296 /** 297 * Sets a drawable as the content of this ImageView. 298 * 299 * @param drawable The drawable to set 300 */ 301 public void setImageDrawable(Drawable drawable) { 302 if (mDrawable != drawable) { 303 mResource = 0; 304 mUri = null; 305 updateDrawable(drawable); 306 requestLayout(); 307 invalidate(); 308 } 309 } 310 311 /** 312 * Sets a Bitmap as the content of this ImageView. 313 * 314 * @param bm The bitmap to set 315 */ 316 @android.view.RemotableViewMethod 317 public void setImageBitmap(Bitmap bm) { 318 // if this is used frequently, may handle bitmaps explicitly 319 // to reduce the intermediate drawable object 320 setImageDrawable(new BitmapDrawable(mContext.getResources(), bm)); 321 } 322 323 public void setImageState(int[] state, boolean merge) { 324 mState = state; 325 mMergeState = merge; 326 if (mDrawable != null) { 327 refreshDrawableState(); 328 resizeFromDrawable(); 329 } 330 } 331 332 @Override 333 public void setSelected(boolean selected) { 334 super.setSelected(selected); 335 resizeFromDrawable(); 336 } 337 338 /** 339 * Sets the image level, when it is constructed from a 340 * {@link android.graphics.drawable.LevelListDrawable}. 341 * 342 * @param level The new level for the image. 343 */ 344 @android.view.RemotableViewMethod 345 public void setImageLevel(int level) { 346 mLevel = level; 347 if (mDrawable != null) { 348 mDrawable.setLevel(level); 349 resizeFromDrawable(); 350 } 351 } 352 353 /** 354 * Options for scaling the bounds of an image to the bounds of this view. 355 */ 356 public enum ScaleType { 357 /** 358 * Scale using the image matrix when drawing. The image matrix can be set using 359 * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax: 360 * <code>android:scaleType="matrix"</code>. 361 */ 362 MATRIX (0), 363 /** 364 * Scale the image using {@link Matrix.ScaleToFit#FILL}. 365 * From XML, use this syntax: <code>android:scaleType="fitXY"</code>. 366 */ 367 FIT_XY (1), 368 /** 369 * Scale the image using {@link Matrix.ScaleToFit#START}. 370 * From XML, use this syntax: <code>android:scaleType="fitStart"</code>. 371 */ 372 FIT_START (2), 373 /** 374 * Scale the image using {@link Matrix.ScaleToFit#CENTER}. 375 * From XML, use this syntax: 376 * <code>android:scaleType="fitCenter"</code>. 377 */ 378 FIT_CENTER (3), 379 /** 380 * Scale the image using {@link Matrix.ScaleToFit#END}. 381 * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>. 382 */ 383 FIT_END (4), 384 /** 385 * Center the image in the view, but perform no scaling. 386 * From XML, use this syntax: <code>android:scaleType="center"</code>. 387 */ 388 CENTER (5), 389 /** 390 * Scale the image uniformly (maintain the image's aspect ratio) so 391 * that both dimensions (width and height) of the image will be equal 392 * to or larger than the corresponding dimension of the view 393 * (minus padding). The image is then centered in the view. 394 * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>. 395 */ 396 CENTER_CROP (6), 397 /** 398 * Scale the image uniformly (maintain the image's aspect ratio) so 399 * that both dimensions (width and height) of the image will be equal 400 * to or less than the corresponding dimension of the view 401 * (minus padding). The image is then centered in the view. 402 * From XML, use this syntax: <code>android:scaleType="centerInside"</code>. 403 */ 404 CENTER_INSIDE (7); 405 406 ScaleType(int ni) { 407 nativeInt = ni; 408 } 409 final int nativeInt; 410 } 411 412 /** 413 * Controls how the image should be resized or moved to match the size 414 * of this ImageView. 415 * 416 * @param scaleType The desired scaling mode. 417 * 418 * @attr ref android.R.styleable#ImageView_scaleType 419 */ 420 public void setScaleType(ScaleType scaleType) { 421 if (scaleType == null) { 422 throw new NullPointerException(); 423 } 424 425 if (mScaleType != scaleType) { 426 mScaleType = scaleType; 427 428 setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); 429 430 requestLayout(); 431 invalidate(); 432 } 433 } 434 435 /** 436 * Return the current scale type in use by this ImageView. 437 * 438 * @see ImageView.ScaleType 439 * 440 * @attr ref android.R.styleable#ImageView_scaleType 441 */ 442 public ScaleType getScaleType() { 443 return mScaleType; 444 } 445 446 /** Return the view's optional matrix. This is applied to the 447 view's drawable when it is drawn. If there is not matrix, 448 this method will return null. 449 Do not change this matrix in place. If you want a different matrix 450 applied to the drawable, be sure to call setImageMatrix(). 451 */ 452 public Matrix getImageMatrix() { 453 return mMatrix; 454 } 455 456 public void setImageMatrix(Matrix matrix) { 457 // collaps null and identity to just null 458 if (matrix != null && matrix.isIdentity()) { 459 matrix = null; 460 } 461 462 // don't invalidate unless we're actually changing our matrix 463 if (matrix == null && !mMatrix.isIdentity() || 464 matrix != null && !mMatrix.equals(matrix)) { 465 mMatrix.set(matrix); 466 configureBounds(); 467 invalidate(); 468 } 469 } 470 471 private void resolveUri() { 472 if (mDrawable != null) { 473 return; 474 } 475 476 Resources rsrc = getResources(); 477 if (rsrc == null) { 478 return; 479 } 480 481 Drawable d = null; 482 483 if (mResource != 0) { 484 try { 485 d = rsrc.getDrawable(mResource); 486 } catch (Exception e) { 487 Log.w("ImageView", "Unable to find resource: " + mResource, e); 488 // Don't try again. 489 mUri = null; 490 } 491 } else if (mUri != null) { 492 if ("content".equals(mUri.getScheme())) { 493 try { 494 d = Drawable.createFromStream( 495 mContext.getContentResolver().openInputStream(mUri), 496 null); 497 } catch (Exception e) { 498 Log.w("ImageView", "Unable to open content: " + mUri, e); 499 } 500 } else { 501 d = Drawable.createFromPath(mUri.toString()); 502 } 503 504 if (d == null) { 505 System.out.println("resolveUri failed on bad bitmap uri: " 506 + mUri); 507 // Don't try again. 508 mUri = null; 509 } 510 } else { 511 return; 512 } 513 514 updateDrawable(d); 515 } 516 517 @Override 518 public int[] onCreateDrawableState(int extraSpace) { 519 if (mState == null) { 520 return super.onCreateDrawableState(extraSpace); 521 } else if (!mMergeState) { 522 return mState; 523 } else { 524 return mergeDrawableStates( 525 super.onCreateDrawableState(extraSpace + mState.length), mState); 526 } 527 } 528 529 private void updateDrawable(Drawable d) { 530 if (mDrawable != null) { 531 mDrawable.setCallback(null); 532 unscheduleDrawable(mDrawable); 533 } 534 mDrawable = d; 535 if (d != null) { 536 d.setCallback(this); 537 if (d.isStateful()) { 538 d.setState(getDrawableState()); 539 } 540 d.setLevel(mLevel); 541 mDrawableWidth = d.getIntrinsicWidth(); 542 mDrawableHeight = d.getIntrinsicHeight(); 543 applyColorMod(); 544 configureBounds(); 545 } 546 } 547 548 private void resizeFromDrawable() { 549 Drawable d = mDrawable; 550 if (d != null) { 551 int w = d.getIntrinsicWidth(); 552 if (w < 0) w = mDrawableWidth; 553 int h = d.getIntrinsicHeight(); 554 if (h < 0) h = mDrawableHeight; 555 if (w != mDrawableWidth || h != mDrawableHeight) { 556 mDrawableWidth = w; 557 mDrawableHeight = h; 558 requestLayout(); 559 } 560 } 561 } 562 563 private static final Matrix.ScaleToFit[] sS2FArray = { 564 Matrix.ScaleToFit.FILL, 565 Matrix.ScaleToFit.START, 566 Matrix.ScaleToFit.CENTER, 567 Matrix.ScaleToFit.END 568 }; 569 570 private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { 571 // ScaleToFit enum to their corresponding Matrix.ScaleToFit values 572 return sS2FArray[st.nativeInt - 1]; 573 } 574 575 @Override 576 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 577 resolveUri(); 578 int w; 579 int h; 580 581 // Desired aspect ratio of the view's contents (not including padding) 582 float desiredAspect = 0.0f; 583 584 // We are allowed to change the view's width 585 boolean resizeWidth = false; 586 587 // We are allowed to change the view's height 588 boolean resizeHeight = false; 589 590 if (mDrawable == null) { 591 // If no drawable, its intrinsic size is 0. 592 mDrawableWidth = -1; 593 mDrawableHeight = -1; 594 w = h = 0; 595 } else { 596 w = mDrawableWidth; 597 h = mDrawableHeight; 598 if (w <= 0) w = 1; 599 if (h <= 0) h = 1; 600 601 // We are supposed to adjust view bounds to match the aspect 602 // ratio of our drawable. See if that is possible. 603 if (mAdjustViewBounds) { 604 605 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 606 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 607 608 resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; 609 resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; 610 611 desiredAspect = (float)w/(float)h; 612 } 613 } 614 615 int pleft = mPaddingLeft; 616 int pright = mPaddingRight; 617 int ptop = mPaddingTop; 618 int pbottom = mPaddingBottom; 619 620 int widthSize; 621 int heightSize; 622 623 if (resizeWidth || resizeHeight) { 624 /* If we get here, it means we want to resize to match the 625 drawables aspect ratio, and we have the freedom to change at 626 least one dimension. 627 */ 628 629 // Get the max possible width given our constraints 630 widthSize = resolveAdjustedSize(w + pleft + pright, 631 mMaxWidth, widthMeasureSpec); 632 633 // Get the max possible height given our constraints 634 heightSize = resolveAdjustedSize(h + ptop + pbottom, 635 mMaxHeight, heightMeasureSpec); 636 637 if (desiredAspect != 0.0f) { 638 // See what our actual aspect ratio is 639 float actualAspect = (float)(widthSize - pleft - pright) / 640 (heightSize - ptop - pbottom); 641 642 if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { 643 644 boolean done = false; 645 646 // Try adjusting width to be proportional to height 647 if (resizeWidth) { 648 int newWidth = (int)(desiredAspect * 649 (heightSize - ptop - pbottom)) 650 + pleft + pright; 651 if (newWidth <= widthSize) { 652 widthSize = newWidth; 653 done = true; 654 } 655 } 656 657 // Try adjusting height to be proportional to width 658 if (!done && resizeHeight) { 659 int newHeight = (int)((widthSize - pleft - pright) 660 / desiredAspect) + ptop + pbottom; 661 if (newHeight <= heightSize) { 662 heightSize = newHeight; 663 } 664 } 665 } 666 } 667 } else { 668 /* We are either don't want to preserve the drawables aspect ratio, 669 or we are not allowed to change view dimensions. Just measure in 670 the normal way. 671 */ 672 w += pleft + pright; 673 h += ptop + pbottom; 674 675 w = Math.max(w, getSuggestedMinimumWidth()); 676 h = Math.max(h, getSuggestedMinimumHeight()); 677 678 widthSize = resolveSize(w, widthMeasureSpec); 679 heightSize = resolveSize(h, heightMeasureSpec); 680 } 681 682 setMeasuredDimension(widthSize, heightSize); 683 } 684 685 private int resolveAdjustedSize(int desiredSize, int maxSize, 686 int measureSpec) { 687 int result = desiredSize; 688 int specMode = MeasureSpec.getMode(measureSpec); 689 int specSize = MeasureSpec.getSize(measureSpec); 690 switch (specMode) { 691 case MeasureSpec.UNSPECIFIED: 692 /* Parent says we can be as big as we want. Just don't be larger 693 than max size imposed on ourselves. 694 */ 695 result = Math.min(desiredSize, maxSize); 696 break; 697 case MeasureSpec.AT_MOST: 698 // Parent says we can be as big as we want, up to specSize. 699 // Don't be larger than specSize, and don't be larger than 700 // the max size imposed on ourselves. 701 result = Math.min(Math.min(desiredSize, specSize), maxSize); 702 break; 703 case MeasureSpec.EXACTLY: 704 // No choice. Do what we are told. 705 result = specSize; 706 break; 707 } 708 return result; 709 } 710 711 @Override 712 protected boolean setFrame(int l, int t, int r, int b) { 713 boolean changed = super.setFrame(l, t, r, b); 714 mHaveFrame = true; 715 configureBounds(); 716 return changed; 717 } 718 719 private void configureBounds() { 720 if (mDrawable == null || !mHaveFrame) { 721 return; 722 } 723 724 int dwidth = mDrawableWidth; 725 int dheight = mDrawableHeight; 726 727 int vwidth = getWidth() - mPaddingLeft - mPaddingRight; 728 int vheight = getHeight() - mPaddingTop - mPaddingBottom; 729 730 boolean fits = (dwidth < 0 || vwidth == dwidth) && 731 (dheight < 0 || vheight == dheight); 732 733 if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { 734 /* If the drawable has no intrinsic size, or we're told to 735 scaletofit, then we just fill our entire view. 736 */ 737 mDrawable.setBounds(0, 0, vwidth, vheight); 738 mDrawMatrix = null; 739 } else { 740 // We need to do the scaling ourself, so have the drawable 741 // use its native size. 742 mDrawable.setBounds(0, 0, dwidth, dheight); 743 744 if (ScaleType.MATRIX == mScaleType) { 745 // Use the specified matrix as-is. 746 if (mMatrix.isIdentity()) { 747 mDrawMatrix = null; 748 } else { 749 mDrawMatrix = mMatrix; 750 } 751 } else if (fits) { 752 // The bitmap fits exactly, no transform needed. 753 mDrawMatrix = null; 754 } else if (ScaleType.CENTER == mScaleType) { 755 // Center bitmap in view, no scaling. 756 mDrawMatrix = mMatrix; 757 mDrawMatrix.setTranslate((vwidth - dwidth) * 0.5f, 758 (vheight - dheight) * 0.5f); 759 } else if (ScaleType.CENTER_CROP == mScaleType) { 760 mDrawMatrix = mMatrix; 761 762 float scale; 763 float dx = 0, dy = 0; 764 765 if (dwidth * vheight > vwidth * dheight) { 766 scale = (float) vheight / (float) dheight; 767 dx = (vwidth - dwidth * scale) * 0.5f; 768 } else { 769 scale = (float) vwidth / (float) dwidth; 770 dy = (vheight - dheight * scale) * 0.5f; 771 } 772 773 mDrawMatrix.setScale(scale, scale); 774 mDrawMatrix.postTranslate(dx, dy); 775 } else if (ScaleType.CENTER_INSIDE == mScaleType) { 776 mDrawMatrix = mMatrix; 777 float scale; 778 float dx; 779 float dy; 780 781 if (dwidth <= vwidth && dheight <= vheight) { 782 scale = 1.0f; 783 } else { 784 scale = Math.min((float) vwidth / (float) dwidth, 785 (float) vheight / (float) dheight); 786 } 787 788 dx = (vwidth - dwidth * scale) * 0.5f; 789 dy = (vheight - dheight * scale) * 0.5f; 790 791 mDrawMatrix.setScale(scale, scale); 792 mDrawMatrix.postTranslate(dx, dy); 793 } else { 794 // Generate the required transform. 795 mTempSrc.set(0, 0, dwidth, dheight); 796 mTempDst.set(0, 0, vwidth, vheight); 797 798 mDrawMatrix = mMatrix; 799 mDrawMatrix.setRectToRect(mTempSrc, mTempDst, 800 scaleTypeToScaleToFit(mScaleType)); 801 } 802 } 803 } 804 805 @Override 806 protected void drawableStateChanged() { 807 super.drawableStateChanged(); 808 Drawable d = mDrawable; 809 if (d != null && d.isStateful()) { 810 d.setState(getDrawableState()); 811 } 812 } 813 814 @Override 815 protected void onDraw(Canvas canvas) { 816 super.onDraw(canvas); 817 818 if (mDrawable == null) { 819 return; // couldn't resolve the URI 820 } 821 822 if (mDrawableWidth == 0 || mDrawableHeight == 0) { 823 return; // nothing to draw (empty bounds) 824 } 825 826 if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { 827 mDrawable.draw(canvas); 828 } else { 829 int saveCount = canvas.getSaveCount(); 830 canvas.save(); 831 832 if (mCropToPadding) { 833 final int scrollX = mScrollX; 834 final int scrollY = mScrollY; 835 canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop, 836 scrollX + mRight - mLeft - mPaddingRight, 837 scrollY + mBottom - mTop - mPaddingBottom); 838 } 839 840 canvas.translate(mPaddingLeft, mPaddingTop); 841 842 if (mDrawMatrix != null) { 843 canvas.concat(mDrawMatrix); 844 } 845 mDrawable.draw(canvas); 846 canvas.restoreToCount(saveCount); 847 } 848 } 849 850 @Override 851 public int getBaseline() { 852 return mBaselineAligned ? getMeasuredHeight() : -1; 853 } 854 855 /** 856 * Set a tinting option for the image. 857 * 858 * @param color Color tint to apply. 859 * @param mode How to apply the color. The standard mode is 860 * {@link PorterDuff.Mode#SRC_ATOP} 861 * 862 * @attr ref android.R.styleable#ImageView_tint 863 */ 864 public final void setColorFilter(int color, PorterDuff.Mode mode) { 865 setColorFilter(new PorterDuffColorFilter(color, mode)); 866 } 867 868 public final void clearColorFilter() { 869 setColorFilter(null); 870 } 871 872 /** 873 * Apply an arbitrary colorfilter to the image. 874 * 875 * @param cf the colorfilter to apply (may be null) 876 */ 877 public void setColorFilter(ColorFilter cf) { 878 if (mColorFilter != cf) { 879 mColorFilter = cf; 880 applyColorMod(); 881 invalidate(); 882 } 883 } 884 885 public void setAlpha(int alpha) { 886 alpha &= 0xFF; // keep it legal 887 if (mAlpha != alpha) { 888 mAlpha = alpha; 889 applyColorMod(); 890 invalidate(); 891 } 892 } 893 894 private void applyColorMod() { 895 if (mDrawable != null) { 896 mDrawable.setColorFilter(mColorFilter); 897 mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); 898 } 899 } 900} 901