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