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