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