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 17 package android.widget; 18 19 import android.annotation.Nullable; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.res.ColorStateList; 23 import android.content.res.Resources; 24 import android.content.res.TypedArray; 25 import android.graphics.Bitmap; 26 import android.graphics.Canvas; 27 import android.graphics.ColorFilter; 28 import android.graphics.Matrix; 29 import android.graphics.PixelFormat; 30 import android.graphics.PorterDuff; 31 import android.graphics.PorterDuffColorFilter; 32 import android.graphics.Rect; 33 import android.graphics.RectF; 34 import android.graphics.Xfermode; 35 import android.graphics.drawable.BitmapDrawable; 36 import android.graphics.drawable.Drawable; 37 import android.net.Uri; 38 import android.os.Build; 39 import android.text.TextUtils; 40 import android.util.AttributeSet; 41 import android.util.Log; 42 import android.view.RemotableViewMethod; 43 import android.view.View; 44 import android.view.ViewDebug; 45 import android.view.accessibility.AccessibilityEvent; 46 import android.view.accessibility.AccessibilityNodeInfo; 47 import android.widget.RemoteViews.RemoteView; 48 49 import com.android.internal.R; 50 51 import java.io.IOException; 52 import java.io.InputStream; 53 54 /** 55 * Displays an arbitrary image, such as an icon. The ImageView class 56 * can load images from various sources (such as resources or content 57 * providers), takes care of computing its measurement from the image so that 58 * it can be used in any layout manager, and provides various display options 59 * such as scaling and tinting. 60 * 61 * @attr ref android.R.styleable#ImageView_adjustViewBounds 62 * @attr ref android.R.styleable#ImageView_src 63 * @attr ref android.R.styleable#ImageView_maxWidth 64 * @attr ref android.R.styleable#ImageView_maxHeight 65 * @attr ref android.R.styleable#ImageView_tint 66 * @attr ref android.R.styleable#ImageView_scaleType 67 * @attr ref android.R.styleable#ImageView_cropToPadding 68 */ 69 @RemoteView 70 public class ImageView extends View { 71 // settable by the client 72 private Uri mUri; 73 private int mResource = 0; 74 private Matrix mMatrix; 75 private ScaleType mScaleType; 76 private boolean mHaveFrame = false; 77 private boolean mAdjustViewBounds = false; 78 private int mMaxWidth = Integer.MAX_VALUE; 79 private int mMaxHeight = Integer.MAX_VALUE; 80 81 // these are applied to the drawable 82 private ColorFilter mColorFilter = null; 83 private boolean mHasColorFilter = false; 84 private Xfermode mXfermode; 85 private int mAlpha = 255; 86 private int mViewAlphaScale = 256; 87 private boolean mColorMod = false; 88 89 private Drawable mDrawable = null; 90 private ColorStateList mDrawableTintList = null; 91 private PorterDuff.Mode mDrawableTintMode = null; 92 private boolean mHasDrawableTint = false; 93 private boolean mHasDrawableTintMode = false; 94 95 private int[] mState = null; 96 private boolean mMergeState = false; 97 private int mLevel = 0; 98 private int mDrawableWidth; 99 private int mDrawableHeight; 100 private Matrix mDrawMatrix = null; 101 102 // Avoid allocations... 103 private RectF mTempSrc = new RectF(); 104 private RectF mTempDst = new RectF(); 105 106 private boolean mCropToPadding; 107 108 private int mBaseline = -1; 109 private boolean mBaselineAlignBottom = false; 110 111 // AdjustViewBounds behavior will be in compatibility mode for older apps. 112 private boolean mAdjustViewBoundsCompat = false; 113 114 private static final ScaleType[] sScaleTypeArray = { 115 ScaleType.MATRIX, 116 ScaleType.FIT_XY, 117 ScaleType.FIT_START, 118 ScaleType.FIT_CENTER, 119 ScaleType.FIT_END, 120 ScaleType.CENTER, 121 ScaleType.CENTER_CROP, 122 ScaleType.CENTER_INSIDE 123 }; 124 ImageView(Context context)125 public ImageView(Context context) { 126 super(context); 127 initImageView(); 128 } 129 ImageView(Context context, AttributeSet attrs)130 public ImageView(Context context, AttributeSet attrs) { 131 this(context, attrs, 0); 132 } 133 ImageView(Context context, AttributeSet attrs, int defStyleAttr)134 public ImageView(Context context, AttributeSet attrs, int defStyleAttr) { 135 this(context, attrs, defStyleAttr, 0); 136 } 137 ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)138 public ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 139 super(context, attrs, defStyleAttr, defStyleRes); 140 141 initImageView(); 142 143 final TypedArray a = context.obtainStyledAttributes( 144 attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); 145 146 Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); 147 if (d != null) { 148 setImageDrawable(d); 149 } 150 151 mBaselineAlignBottom = a.getBoolean( 152 com.android.internal.R.styleable.ImageView_baselineAlignBottom, false); 153 154 mBaseline = a.getDimensionPixelSize( 155 com.android.internal.R.styleable.ImageView_baseline, -1); 156 157 setAdjustViewBounds( 158 a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds, 159 false)); 160 161 setMaxWidth(a.getDimensionPixelSize( 162 com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE)); 163 164 setMaxHeight(a.getDimensionPixelSize( 165 com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE)); 166 167 final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1); 168 if (index >= 0) { 169 setScaleType(sScaleTypeArray[index]); 170 } 171 172 if (a.hasValue(R.styleable.ImageView_tint)) { 173 mDrawableTintList = a.getColorStateList(R.styleable.ImageView_tint); 174 mHasDrawableTint = true; 175 176 // Prior to L, this attribute would always set a color filter with 177 // blending mode SRC_ATOP. Preserve that default behavior. 178 mDrawableTintMode = PorterDuff.Mode.SRC_ATOP; 179 mHasDrawableTintMode = true; 180 } 181 182 if (a.hasValue(R.styleable.ImageView_tintMode)) { 183 mDrawableTintMode = Drawable.parseTintMode(a.getInt( 184 R.styleable.ImageView_tintMode, -1), mDrawableTintMode); 185 mHasDrawableTintMode = true; 186 } 187 188 applyImageTint(); 189 190 final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255); 191 if (alpha != 255) { 192 setAlpha(alpha); 193 } 194 195 mCropToPadding = a.getBoolean( 196 com.android.internal.R.styleable.ImageView_cropToPadding, false); 197 198 a.recycle(); 199 200 //need inflate syntax/reader for matrix 201 } 202 initImageView()203 private void initImageView() { 204 mMatrix = new Matrix(); 205 mScaleType = ScaleType.FIT_CENTER; 206 mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <= 207 Build.VERSION_CODES.JELLY_BEAN_MR1; 208 } 209 210 @Override verifyDrawable(Drawable dr)211 protected boolean verifyDrawable(Drawable dr) { 212 return mDrawable == dr || super.verifyDrawable(dr); 213 } 214 215 @Override jumpDrawablesToCurrentState()216 public void jumpDrawablesToCurrentState() { 217 super.jumpDrawablesToCurrentState(); 218 if (mDrawable != null) mDrawable.jumpToCurrentState(); 219 } 220 221 @Override invalidateDrawable(Drawable dr)222 public void invalidateDrawable(Drawable dr) { 223 if (dr == mDrawable) { 224 /* we invalidate the whole view in this case because it's very 225 * hard to know where the drawable actually is. This is made 226 * complicated because of the offsets and transformations that 227 * can be applied. In theory we could get the drawable's bounds 228 * and run them through the transformation and offsets, but this 229 * is probably not worth the effort. 230 */ 231 invalidate(); 232 } else { 233 super.invalidateDrawable(dr); 234 } 235 } 236 237 @Override hasOverlappingRendering()238 public boolean hasOverlappingRendering() { 239 return (getBackground() != null && getBackground().getCurrent() != null); 240 } 241 242 @Override onPopulateAccessibilityEvent(AccessibilityEvent event)243 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 244 super.onPopulateAccessibilityEvent(event); 245 CharSequence contentDescription = getContentDescription(); 246 if (!TextUtils.isEmpty(contentDescription)) { 247 event.getText().add(contentDescription); 248 } 249 } 250 251 /** 252 * True when ImageView is adjusting its bounds 253 * to preserve the aspect ratio of its drawable 254 * 255 * @return whether to adjust the bounds of this view 256 * to presrve the original aspect ratio of the drawable 257 * 258 * @see #setAdjustViewBounds(boolean) 259 * 260 * @attr ref android.R.styleable#ImageView_adjustViewBounds 261 */ getAdjustViewBounds()262 public boolean getAdjustViewBounds() { 263 return mAdjustViewBounds; 264 } 265 266 /** 267 * Set this to true if you want the ImageView to adjust its bounds 268 * to preserve the aspect ratio of its drawable. 269 * 270 * <p><strong>Note:</strong> If the application targets API level 17 or lower, 271 * adjustViewBounds will allow the drawable to shrink the view bounds, but not grow 272 * to fill available measured space in all cases. This is for compatibility with 273 * legacy {@link android.view.View.MeasureSpec MeasureSpec} and 274 * {@link android.widget.RelativeLayout RelativeLayout} behavior.</p> 275 * 276 * @param adjustViewBounds Whether to adjust the bounds of this view 277 * to preserve the original aspect ratio of the drawable. 278 * 279 * @see #getAdjustViewBounds() 280 * 281 * @attr ref android.R.styleable#ImageView_adjustViewBounds 282 */ 283 @android.view.RemotableViewMethod setAdjustViewBounds(boolean adjustViewBounds)284 public void setAdjustViewBounds(boolean adjustViewBounds) { 285 mAdjustViewBounds = adjustViewBounds; 286 if (adjustViewBounds) { 287 setScaleType(ScaleType.FIT_CENTER); 288 } 289 } 290 291 /** 292 * The maximum width of this view. 293 * 294 * @return The maximum width of this view 295 * 296 * @see #setMaxWidth(int) 297 * 298 * @attr ref android.R.styleable#ImageView_maxWidth 299 */ getMaxWidth()300 public int getMaxWidth() { 301 return mMaxWidth; 302 } 303 304 /** 305 * An optional argument to supply a maximum width for this view. Only valid if 306 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum 307 * of 100 x 100 while preserving the original aspect ratio, do the following: 1) set 308 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width 309 * layout params to WRAP_CONTENT. 310 * 311 * <p> 312 * Note that this view could be still smaller than 100 x 100 using this approach if the original 313 * image is small. To set an image to a fixed size, specify that size in the layout params and 314 * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit 315 * the image within the bounds. 316 * </p> 317 * 318 * @param maxWidth maximum width for this view 319 * 320 * @see #getMaxWidth() 321 * 322 * @attr ref android.R.styleable#ImageView_maxWidth 323 */ 324 @android.view.RemotableViewMethod setMaxWidth(int maxWidth)325 public void setMaxWidth(int maxWidth) { 326 mMaxWidth = maxWidth; 327 } 328 329 /** 330 * The maximum height of this view. 331 * 332 * @return The maximum height of this view 333 * 334 * @see #setMaxHeight(int) 335 * 336 * @attr ref android.R.styleable#ImageView_maxHeight 337 */ getMaxHeight()338 public int getMaxHeight() { 339 return mMaxHeight; 340 } 341 342 /** 343 * An optional argument to supply a maximum height for this view. Only valid if 344 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a 345 * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set 346 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width 347 * layout params to WRAP_CONTENT. 348 * 349 * <p> 350 * Note that this view could be still smaller than 100 x 100 using this approach if the original 351 * image is small. To set an image to a fixed size, specify that size in the layout params and 352 * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit 353 * the image within the bounds. 354 * </p> 355 * 356 * @param maxHeight maximum height for this view 357 * 358 * @see #getMaxHeight() 359 * 360 * @attr ref android.R.styleable#ImageView_maxHeight 361 */ 362 @android.view.RemotableViewMethod setMaxHeight(int maxHeight)363 public void setMaxHeight(int maxHeight) { 364 mMaxHeight = maxHeight; 365 } 366 367 /** Return the view's drawable, or null if no drawable has been 368 assigned. 369 */ getDrawable()370 public Drawable getDrawable() { 371 return mDrawable; 372 } 373 374 /** 375 * Sets a drawable as the content of this ImageView. 376 * 377 * <p class="note">This does Bitmap reading and decoding on the UI 378 * thread, which can cause a latency hiccup. If that's a concern, 379 * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or 380 * {@link #setImageBitmap(android.graphics.Bitmap)} and 381 * {@link android.graphics.BitmapFactory} instead.</p> 382 * 383 * @param resId the resource identifier of the drawable 384 * 385 * @attr ref android.R.styleable#ImageView_src 386 */ 387 @android.view.RemotableViewMethod setImageResource(int resId)388 public void setImageResource(int resId) { 389 // The resource configuration may have changed, so we should always 390 // try to load the resource even if the resId hasn't changed. 391 final int oldWidth = mDrawableWidth; 392 final int oldHeight = mDrawableHeight; 393 394 updateDrawable(null); 395 mResource = resId; 396 mUri = null; 397 398 resolveUri(); 399 400 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { 401 requestLayout(); 402 } 403 invalidate(); 404 } 405 406 /** 407 * Sets the content of this ImageView to the specified Uri. 408 * 409 * <p class="note">This does Bitmap reading and decoding on the UI 410 * thread, which can cause a latency hiccup. If that's a concern, 411 * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or 412 * {@link #setImageBitmap(android.graphics.Bitmap)} and 413 * {@link android.graphics.BitmapFactory} instead.</p> 414 * 415 * @param uri The Uri of an image 416 */ 417 @android.view.RemotableViewMethod setImageURI(Uri uri)418 public void setImageURI(Uri uri) { 419 if (mResource != 0 || 420 (mUri != uri && 421 (uri == null || mUri == null || !uri.equals(mUri)))) { 422 updateDrawable(null); 423 mResource = 0; 424 mUri = uri; 425 426 final int oldWidth = mDrawableWidth; 427 final int oldHeight = mDrawableHeight; 428 429 resolveUri(); 430 431 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { 432 requestLayout(); 433 } 434 invalidate(); 435 } 436 } 437 438 /** 439 * Sets a drawable as the content of this ImageView. 440 * 441 * @param drawable The drawable to set 442 */ setImageDrawable(Drawable drawable)443 public void setImageDrawable(Drawable drawable) { 444 if (mDrawable != drawable) { 445 mResource = 0; 446 mUri = null; 447 448 final int oldWidth = mDrawableWidth; 449 final int oldHeight = mDrawableHeight; 450 451 updateDrawable(drawable); 452 453 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { 454 requestLayout(); 455 } 456 invalidate(); 457 } 458 } 459 460 /** 461 * Applies a tint to the image drawable. Does not modify the current tint 462 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 463 * <p> 464 * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically 465 * mutate the drawable and apply the specified tint and tint mode using 466 * {@link Drawable#setTintList(ColorStateList)}. 467 * 468 * @param tint the tint to apply, may be {@code null} to clear tint 469 * 470 * @attr ref android.R.styleable#ImageView_tint 471 * @see #getImageTintList() 472 * @see Drawable#setTintList(ColorStateList) 473 */ setImageTintList(@ullable ColorStateList tint)474 public void setImageTintList(@Nullable ColorStateList tint) { 475 mDrawableTintList = tint; 476 mHasDrawableTint = true; 477 478 applyImageTint(); 479 } 480 481 /** 482 * @return the tint applied to the image drawable 483 * @attr ref android.R.styleable#ImageView_tint 484 * @see #setImageTintList(ColorStateList) 485 */ 486 @Nullable getImageTintList()487 public ColorStateList getImageTintList() { 488 return mDrawableTintList; 489 } 490 491 /** 492 * Specifies the blending mode used to apply the tint specified by 493 * {@link #setImageTintList(ColorStateList)}} to the image drawable. The default 494 * mode is {@link PorterDuff.Mode#SRC_IN}. 495 * 496 * @param tintMode the blending mode used to apply the tint, may be 497 * {@code null} to clear tint 498 * @attr ref android.R.styleable#ImageView_tintMode 499 * @see #getImageTintMode() 500 * @see Drawable#setTintMode(PorterDuff.Mode) 501 */ setImageTintMode(@ullable PorterDuff.Mode tintMode)502 public void setImageTintMode(@Nullable PorterDuff.Mode tintMode) { 503 mDrawableTintMode = tintMode; 504 mHasDrawableTintMode = true; 505 506 applyImageTint(); 507 } 508 509 /** 510 * @return the blending mode used to apply the tint to the image drawable 511 * @attr ref android.R.styleable#ImageView_tintMode 512 * @see #setImageTintMode(PorterDuff.Mode) 513 */ 514 @Nullable getImageTintMode()515 public PorterDuff.Mode getImageTintMode() { 516 return mDrawableTintMode; 517 } 518 applyImageTint()519 private void applyImageTint() { 520 if (mDrawable != null && (mHasDrawableTint || mHasDrawableTintMode)) { 521 mDrawable = mDrawable.mutate(); 522 523 if (mHasDrawableTint) { 524 mDrawable.setTintList(mDrawableTintList); 525 } 526 527 if (mHasDrawableTintMode) { 528 mDrawable.setTintMode(mDrawableTintMode); 529 } 530 531 // The drawable (or one of its children) may not have been 532 // stateful before applying the tint, so let's try again. 533 if (mDrawable.isStateful()) { 534 mDrawable.setState(getDrawableState()); 535 } 536 } 537 } 538 539 /** 540 * Sets a Bitmap as the content of this ImageView. 541 * 542 * @param bm The bitmap to set 543 */ 544 @android.view.RemotableViewMethod setImageBitmap(Bitmap bm)545 public void setImageBitmap(Bitmap bm) { 546 // if this is used frequently, may handle bitmaps explicitly 547 // to reduce the intermediate drawable object 548 setImageDrawable(new BitmapDrawable(mContext.getResources(), bm)); 549 } 550 setImageState(int[] state, boolean merge)551 public void setImageState(int[] state, boolean merge) { 552 mState = state; 553 mMergeState = merge; 554 if (mDrawable != null) { 555 refreshDrawableState(); 556 resizeFromDrawable(); 557 } 558 } 559 560 @Override setSelected(boolean selected)561 public void setSelected(boolean selected) { 562 super.setSelected(selected); 563 resizeFromDrawable(); 564 } 565 566 /** 567 * Sets the image level, when it is constructed from a 568 * {@link android.graphics.drawable.LevelListDrawable}. 569 * 570 * @param level The new level for the image. 571 */ 572 @android.view.RemotableViewMethod setImageLevel(int level)573 public void setImageLevel(int level) { 574 mLevel = level; 575 if (mDrawable != null) { 576 mDrawable.setLevel(level); 577 resizeFromDrawable(); 578 } 579 } 580 581 /** 582 * Options for scaling the bounds of an image to the bounds of this view. 583 */ 584 public enum ScaleType { 585 /** 586 * Scale using the image matrix when drawing. The image matrix can be set using 587 * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax: 588 * <code>android:scaleType="matrix"</code>. 589 */ 590 MATRIX (0), 591 /** 592 * Scale the image using {@link Matrix.ScaleToFit#FILL}. 593 * From XML, use this syntax: <code>android:scaleType="fitXY"</code>. 594 */ 595 FIT_XY (1), 596 /** 597 * Scale the image using {@link Matrix.ScaleToFit#START}. 598 * From XML, use this syntax: <code>android:scaleType="fitStart"</code>. 599 */ 600 FIT_START (2), 601 /** 602 * Scale the image using {@link Matrix.ScaleToFit#CENTER}. 603 * From XML, use this syntax: 604 * <code>android:scaleType="fitCenter"</code>. 605 */ 606 FIT_CENTER (3), 607 /** 608 * Scale the image using {@link Matrix.ScaleToFit#END}. 609 * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>. 610 */ 611 FIT_END (4), 612 /** 613 * Center the image in the view, but perform no scaling. 614 * From XML, use this syntax: <code>android:scaleType="center"</code>. 615 */ 616 CENTER (5), 617 /** 618 * Scale the image uniformly (maintain the image's aspect ratio) so 619 * that both dimensions (width and height) of the image will be equal 620 * to or larger than the corresponding dimension of the view 621 * (minus padding). The image is then centered in the view. 622 * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>. 623 */ 624 CENTER_CROP (6), 625 /** 626 * Scale the image uniformly (maintain the image's aspect ratio) so 627 * that both dimensions (width and height) of the image will be equal 628 * to or less than the corresponding dimension of the view 629 * (minus padding). The image is then centered in the view. 630 * From XML, use this syntax: <code>android:scaleType="centerInside"</code>. 631 */ 632 CENTER_INSIDE (7); 633 ScaleType(int ni)634 ScaleType(int ni) { 635 nativeInt = ni; 636 } 637 final int nativeInt; 638 } 639 640 /** 641 * Controls how the image should be resized or moved to match the size 642 * of this ImageView. 643 * 644 * @param scaleType The desired scaling mode. 645 * 646 * @attr ref android.R.styleable#ImageView_scaleType 647 */ setScaleType(ScaleType scaleType)648 public void setScaleType(ScaleType scaleType) { 649 if (scaleType == null) { 650 throw new NullPointerException(); 651 } 652 653 if (mScaleType != scaleType) { 654 mScaleType = scaleType; 655 656 setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); 657 658 requestLayout(); 659 invalidate(); 660 } 661 } 662 663 /** 664 * Return the current scale type in use by this ImageView. 665 * 666 * @see ImageView.ScaleType 667 * 668 * @attr ref android.R.styleable#ImageView_scaleType 669 */ getScaleType()670 public ScaleType getScaleType() { 671 return mScaleType; 672 } 673 674 /** Return the view's optional matrix. This is applied to the 675 view's drawable when it is drawn. If there is no matrix, 676 this method will return an identity matrix. 677 Do not change this matrix in place but make a copy. 678 If you want a different matrix applied to the drawable, 679 be sure to call setImageMatrix(). 680 */ getImageMatrix()681 public Matrix getImageMatrix() { 682 if (mDrawMatrix == null) { 683 return new Matrix(Matrix.IDENTITY_MATRIX); 684 } 685 return mDrawMatrix; 686 } 687 setImageMatrix(Matrix matrix)688 public void setImageMatrix(Matrix matrix) { 689 // collaps null and identity to just null 690 if (matrix != null && matrix.isIdentity()) { 691 matrix = null; 692 } 693 694 // don't invalidate unless we're actually changing our matrix 695 if (matrix == null && !mMatrix.isIdentity() || 696 matrix != null && !mMatrix.equals(matrix)) { 697 mMatrix.set(matrix); 698 configureBounds(); 699 invalidate(); 700 } 701 } 702 703 /** 704 * Return whether this ImageView crops to padding. 705 * 706 * @return whether this ImageView crops to padding 707 * 708 * @see #setCropToPadding(boolean) 709 * 710 * @attr ref android.R.styleable#ImageView_cropToPadding 711 */ getCropToPadding()712 public boolean getCropToPadding() { 713 return mCropToPadding; 714 } 715 716 /** 717 * Sets whether this ImageView will crop to padding. 718 * 719 * @param cropToPadding whether this ImageView will crop to padding 720 * 721 * @see #getCropToPadding() 722 * 723 * @attr ref android.R.styleable#ImageView_cropToPadding 724 */ setCropToPadding(boolean cropToPadding)725 public void setCropToPadding(boolean cropToPadding) { 726 if (mCropToPadding != cropToPadding) { 727 mCropToPadding = cropToPadding; 728 requestLayout(); 729 invalidate(); 730 } 731 } 732 resolveUri()733 private void resolveUri() { 734 if (mDrawable != null) { 735 return; 736 } 737 738 Resources rsrc = getResources(); 739 if (rsrc == null) { 740 return; 741 } 742 743 Drawable d = null; 744 745 if (mResource != 0) { 746 try { 747 d = mContext.getDrawable(mResource); 748 } catch (Exception e) { 749 Log.w("ImageView", "Unable to find resource: " + mResource, e); 750 // Don't try again. 751 mUri = null; 752 } 753 } else if (mUri != null) { 754 String scheme = mUri.getScheme(); 755 if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { 756 try { 757 // Load drawable through Resources, to get the source density information 758 ContentResolver.OpenResourceIdResult r = 759 mContext.getContentResolver().getResourceId(mUri); 760 d = r.r.getDrawable(r.id, mContext.getTheme()); 761 } catch (Exception e) { 762 Log.w("ImageView", "Unable to open content: " + mUri, e); 763 } 764 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) 765 || ContentResolver.SCHEME_FILE.equals(scheme)) { 766 InputStream stream = null; 767 try { 768 stream = mContext.getContentResolver().openInputStream(mUri); 769 d = Drawable.createFromStream(stream, null); 770 } catch (Exception e) { 771 Log.w("ImageView", "Unable to open content: " + mUri, e); 772 } finally { 773 if (stream != null) { 774 try { 775 stream.close(); 776 } catch (IOException e) { 777 Log.w("ImageView", "Unable to close content: " + mUri, e); 778 } 779 } 780 } 781 } else { 782 d = Drawable.createFromPath(mUri.toString()); 783 } 784 785 if (d == null) { 786 System.out.println("resolveUri failed on bad bitmap uri: " + mUri); 787 // Don't try again. 788 mUri = null; 789 } 790 } else { 791 return; 792 } 793 794 updateDrawable(d); 795 } 796 797 @Override onCreateDrawableState(int extraSpace)798 public int[] onCreateDrawableState(int extraSpace) { 799 if (mState == null) { 800 return super.onCreateDrawableState(extraSpace); 801 } else if (!mMergeState) { 802 return mState; 803 } else { 804 return mergeDrawableStates( 805 super.onCreateDrawableState(extraSpace + mState.length), mState); 806 } 807 } 808 updateDrawable(Drawable d)809 private void updateDrawable(Drawable d) { 810 if (mDrawable != null) { 811 mDrawable.setCallback(null); 812 unscheduleDrawable(mDrawable); 813 } 814 815 mDrawable = d; 816 817 if (d != null) { 818 d.setCallback(this); 819 d.setLayoutDirection(getLayoutDirection()); 820 if (d.isStateful()) { 821 d.setState(getDrawableState()); 822 } 823 d.setVisible(getVisibility() == VISIBLE, true); 824 d.setLevel(mLevel); 825 mDrawableWidth = d.getIntrinsicWidth(); 826 mDrawableHeight = d.getIntrinsicHeight(); 827 applyImageTint(); 828 applyColorMod(); 829 830 configureBounds(); 831 } else { 832 mDrawableWidth = mDrawableHeight = -1; 833 } 834 } 835 resizeFromDrawable()836 private void resizeFromDrawable() { 837 Drawable d = mDrawable; 838 if (d != null) { 839 int w = d.getIntrinsicWidth(); 840 if (w < 0) w = mDrawableWidth; 841 int h = d.getIntrinsicHeight(); 842 if (h < 0) h = mDrawableHeight; 843 if (w != mDrawableWidth || h != mDrawableHeight) { 844 mDrawableWidth = w; 845 mDrawableHeight = h; 846 requestLayout(); 847 } 848 } 849 } 850 851 @Override onRtlPropertiesChanged(int layoutDirection)852 public void onRtlPropertiesChanged(int layoutDirection) { 853 super.onRtlPropertiesChanged(layoutDirection); 854 855 if (mDrawable != null) { 856 mDrawable.setLayoutDirection(layoutDirection); 857 } 858 } 859 860 private static final Matrix.ScaleToFit[] sS2FArray = { 861 Matrix.ScaleToFit.FILL, 862 Matrix.ScaleToFit.START, 863 Matrix.ScaleToFit.CENTER, 864 Matrix.ScaleToFit.END 865 }; 866 scaleTypeToScaleToFit(ScaleType st)867 private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { 868 // ScaleToFit enum to their corresponding Matrix.ScaleToFit values 869 return sS2FArray[st.nativeInt - 1]; 870 } 871 872 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)873 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 874 resolveUri(); 875 int w; 876 int h; 877 878 // Desired aspect ratio of the view's contents (not including padding) 879 float desiredAspect = 0.0f; 880 881 // We are allowed to change the view's width 882 boolean resizeWidth = false; 883 884 // We are allowed to change the view's height 885 boolean resizeHeight = false; 886 887 final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 888 final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 889 890 if (mDrawable == null) { 891 // If no drawable, its intrinsic size is 0. 892 mDrawableWidth = -1; 893 mDrawableHeight = -1; 894 w = h = 0; 895 } else { 896 w = mDrawableWidth; 897 h = mDrawableHeight; 898 if (w <= 0) w = 1; 899 if (h <= 0) h = 1; 900 901 // We are supposed to adjust view bounds to match the aspect 902 // ratio of our drawable. See if that is possible. 903 if (mAdjustViewBounds) { 904 resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; 905 resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; 906 907 desiredAspect = (float) w / (float) h; 908 } 909 } 910 911 int pleft = mPaddingLeft; 912 int pright = mPaddingRight; 913 int ptop = mPaddingTop; 914 int pbottom = mPaddingBottom; 915 916 int widthSize; 917 int heightSize; 918 919 if (resizeWidth || resizeHeight) { 920 /* If we get here, it means we want to resize to match the 921 drawables aspect ratio, and we have the freedom to change at 922 least one dimension. 923 */ 924 925 // Get the max possible width given our constraints 926 widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); 927 928 // Get the max possible height given our constraints 929 heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); 930 931 if (desiredAspect != 0.0f) { 932 // See what our actual aspect ratio is 933 float actualAspect = (float)(widthSize - pleft - pright) / 934 (heightSize - ptop - pbottom); 935 936 if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { 937 938 boolean done = false; 939 940 // Try adjusting width to be proportional to height 941 if (resizeWidth) { 942 int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + 943 pleft + pright; 944 945 // Allow the width to outgrow its original estimate if height is fixed. 946 if (!resizeHeight && !mAdjustViewBoundsCompat) { 947 widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); 948 } 949 950 if (newWidth <= widthSize) { 951 widthSize = newWidth; 952 done = true; 953 } 954 } 955 956 // Try adjusting height to be proportional to width 957 if (!done && resizeHeight) { 958 int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + 959 ptop + pbottom; 960 961 // Allow the height to outgrow its original estimate if width is fixed. 962 if (!resizeWidth && !mAdjustViewBoundsCompat) { 963 heightSize = resolveAdjustedSize(newHeight, mMaxHeight, 964 heightMeasureSpec); 965 } 966 967 if (newHeight <= heightSize) { 968 heightSize = newHeight; 969 } 970 } 971 } 972 } 973 } else { 974 /* We are either don't want to preserve the drawables aspect ratio, 975 or we are not allowed to change view dimensions. Just measure in 976 the normal way. 977 */ 978 w += pleft + pright; 979 h += ptop + pbottom; 980 981 w = Math.max(w, getSuggestedMinimumWidth()); 982 h = Math.max(h, getSuggestedMinimumHeight()); 983 984 widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); 985 heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); 986 } 987 988 setMeasuredDimension(widthSize, heightSize); 989 } 990 resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec)991 private int resolveAdjustedSize(int desiredSize, int maxSize, 992 int measureSpec) { 993 int result = desiredSize; 994 int specMode = MeasureSpec.getMode(measureSpec); 995 int specSize = MeasureSpec.getSize(measureSpec); 996 switch (specMode) { 997 case MeasureSpec.UNSPECIFIED: 998 /* Parent says we can be as big as we want. Just don't be larger 999 than max size imposed on ourselves. 1000 */ 1001 result = Math.min(desiredSize, maxSize); 1002 break; 1003 case MeasureSpec.AT_MOST: 1004 // Parent says we can be as big as we want, up to specSize. 1005 // Don't be larger than specSize, and don't be larger than 1006 // the max size imposed on ourselves. 1007 result = Math.min(Math.min(desiredSize, specSize), maxSize); 1008 break; 1009 case MeasureSpec.EXACTLY: 1010 // No choice. Do what we are told. 1011 result = specSize; 1012 break; 1013 } 1014 return result; 1015 } 1016 1017 @Override setFrame(int l, int t, int r, int b)1018 protected boolean setFrame(int l, int t, int r, int b) { 1019 boolean changed = super.setFrame(l, t, r, b); 1020 mHaveFrame = true; 1021 configureBounds(); 1022 return changed; 1023 } 1024 configureBounds()1025 private void configureBounds() { 1026 if (mDrawable == null || !mHaveFrame) { 1027 return; 1028 } 1029 1030 int dwidth = mDrawableWidth; 1031 int dheight = mDrawableHeight; 1032 1033 int vwidth = getWidth() - mPaddingLeft - mPaddingRight; 1034 int vheight = getHeight() - mPaddingTop - mPaddingBottom; 1035 1036 boolean fits = (dwidth < 0 || vwidth == dwidth) && 1037 (dheight < 0 || vheight == dheight); 1038 1039 if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { 1040 /* If the drawable has no intrinsic size, or we're told to 1041 scaletofit, then we just fill our entire view. 1042 */ 1043 mDrawable.setBounds(0, 0, vwidth, vheight); 1044 mDrawMatrix = null; 1045 } else { 1046 // We need to do the scaling ourself, so have the drawable 1047 // use its native size. 1048 mDrawable.setBounds(0, 0, dwidth, dheight); 1049 1050 if (ScaleType.MATRIX == mScaleType) { 1051 // Use the specified matrix as-is. 1052 if (mMatrix.isIdentity()) { 1053 mDrawMatrix = null; 1054 } else { 1055 mDrawMatrix = mMatrix; 1056 } 1057 } else if (fits) { 1058 // The bitmap fits exactly, no transform needed. 1059 mDrawMatrix = null; 1060 } else if (ScaleType.CENTER == mScaleType) { 1061 // Center bitmap in view, no scaling. 1062 mDrawMatrix = mMatrix; 1063 mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f), 1064 (int) ((vheight - dheight) * 0.5f + 0.5f)); 1065 } else if (ScaleType.CENTER_CROP == mScaleType) { 1066 mDrawMatrix = mMatrix; 1067 1068 float scale; 1069 float dx = 0, dy = 0; 1070 1071 if (dwidth * vheight > vwidth * dheight) { 1072 scale = (float) vheight / (float) dheight; 1073 dx = (vwidth - dwidth * scale) * 0.5f; 1074 } else { 1075 scale = (float) vwidth / (float) dwidth; 1076 dy = (vheight - dheight * scale) * 0.5f; 1077 } 1078 1079 mDrawMatrix.setScale(scale, scale); 1080 mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); 1081 } else if (ScaleType.CENTER_INSIDE == mScaleType) { 1082 mDrawMatrix = mMatrix; 1083 float scale; 1084 float dx; 1085 float dy; 1086 1087 if (dwidth <= vwidth && dheight <= vheight) { 1088 scale = 1.0f; 1089 } else { 1090 scale = Math.min((float) vwidth / (float) dwidth, 1091 (float) vheight / (float) dheight); 1092 } 1093 1094 dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); 1095 dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); 1096 1097 mDrawMatrix.setScale(scale, scale); 1098 mDrawMatrix.postTranslate(dx, dy); 1099 } else { 1100 // Generate the required transform. 1101 mTempSrc.set(0, 0, dwidth, dheight); 1102 mTempDst.set(0, 0, vwidth, vheight); 1103 1104 mDrawMatrix = mMatrix; 1105 mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType)); 1106 } 1107 } 1108 } 1109 1110 @Override drawableStateChanged()1111 protected void drawableStateChanged() { 1112 super.drawableStateChanged(); 1113 Drawable d = mDrawable; 1114 if (d != null && d.isStateful()) { 1115 d.setState(getDrawableState()); 1116 } 1117 } 1118 1119 @Override drawableHotspotChanged(float x, float y)1120 public void drawableHotspotChanged(float x, float y) { 1121 super.drawableHotspotChanged(x, y); 1122 1123 if (mDrawable != null) { 1124 mDrawable.setHotspot(x, y); 1125 } 1126 } 1127 1128 /** @hide */ animateTransform(Matrix matrix)1129 public void animateTransform(Matrix matrix) { 1130 if (mDrawable == null) { 1131 return; 1132 } 1133 if (matrix == null) { 1134 mDrawable.setBounds(0, 0, getWidth(), getHeight()); 1135 } else { 1136 mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight); 1137 if (mDrawMatrix == null) { 1138 mDrawMatrix = new Matrix(); 1139 } 1140 mDrawMatrix.set(matrix); 1141 } 1142 invalidate(); 1143 } 1144 1145 @Override onDraw(Canvas canvas)1146 protected void onDraw(Canvas canvas) { 1147 super.onDraw(canvas); 1148 1149 if (mDrawable == null) { 1150 return; // couldn't resolve the URI 1151 } 1152 1153 if (mDrawableWidth == 0 || mDrawableHeight == 0) { 1154 return; // nothing to draw (empty bounds) 1155 } 1156 1157 if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { 1158 mDrawable.draw(canvas); 1159 } else { 1160 int saveCount = canvas.getSaveCount(); 1161 canvas.save(); 1162 1163 if (mCropToPadding) { 1164 final int scrollX = mScrollX; 1165 final int scrollY = mScrollY; 1166 canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop, 1167 scrollX + mRight - mLeft - mPaddingRight, 1168 scrollY + mBottom - mTop - mPaddingBottom); 1169 } 1170 1171 canvas.translate(mPaddingLeft, mPaddingTop); 1172 1173 if (mDrawMatrix != null) { 1174 canvas.concat(mDrawMatrix); 1175 } 1176 mDrawable.draw(canvas); 1177 canvas.restoreToCount(saveCount); 1178 } 1179 } 1180 1181 /** 1182 * <p>Return the offset of the widget's text baseline from the widget's top 1183 * boundary. </p> 1184 * 1185 * @return the offset of the baseline within the widget's bounds or -1 1186 * if baseline alignment is not supported. 1187 */ 1188 @Override 1189 @ViewDebug.ExportedProperty(category = "layout") getBaseline()1190 public int getBaseline() { 1191 if (mBaselineAlignBottom) { 1192 return getMeasuredHeight(); 1193 } else { 1194 return mBaseline; 1195 } 1196 } 1197 1198 /** 1199 * <p>Set the offset of the widget's text baseline from the widget's top 1200 * boundary. This value is overridden by the {@link #setBaselineAlignBottom(boolean)} 1201 * property.</p> 1202 * 1203 * @param baseline The baseline to use, or -1 if none is to be provided. 1204 * 1205 * @see #setBaseline(int) 1206 * @attr ref android.R.styleable#ImageView_baseline 1207 */ setBaseline(int baseline)1208 public void setBaseline(int baseline) { 1209 if (mBaseline != baseline) { 1210 mBaseline = baseline; 1211 requestLayout(); 1212 } 1213 } 1214 1215 /** 1216 * Set whether to set the baseline of this view to the bottom of the view. 1217 * Setting this value overrides any calls to setBaseline. 1218 * 1219 * @param aligned If true, the image view will be baseline aligned with 1220 * based on its bottom edge. 1221 * 1222 * @attr ref android.R.styleable#ImageView_baselineAlignBottom 1223 */ setBaselineAlignBottom(boolean aligned)1224 public void setBaselineAlignBottom(boolean aligned) { 1225 if (mBaselineAlignBottom != aligned) { 1226 mBaselineAlignBottom = aligned; 1227 requestLayout(); 1228 } 1229 } 1230 1231 /** 1232 * Return whether this view's baseline will be considered the bottom of the view. 1233 * 1234 * @see #setBaselineAlignBottom(boolean) 1235 */ getBaselineAlignBottom()1236 public boolean getBaselineAlignBottom() { 1237 return mBaselineAlignBottom; 1238 } 1239 1240 /** 1241 * Set a tinting option for the image. 1242 * 1243 * @param color Color tint to apply. 1244 * @param mode How to apply the color. The standard mode is 1245 * {@link PorterDuff.Mode#SRC_ATOP} 1246 * 1247 * @attr ref android.R.styleable#ImageView_tint 1248 */ setColorFilter(int color, PorterDuff.Mode mode)1249 public final void setColorFilter(int color, PorterDuff.Mode mode) { 1250 setColorFilter(new PorterDuffColorFilter(color, mode)); 1251 } 1252 1253 /** 1254 * Set a tinting option for the image. Assumes 1255 * {@link PorterDuff.Mode#SRC_ATOP} blending mode. 1256 * 1257 * @param color Color tint to apply. 1258 * @attr ref android.R.styleable#ImageView_tint 1259 */ 1260 @RemotableViewMethod setColorFilter(int color)1261 public final void setColorFilter(int color) { 1262 setColorFilter(color, PorterDuff.Mode.SRC_ATOP); 1263 } 1264 clearColorFilter()1265 public final void clearColorFilter() { 1266 setColorFilter(null); 1267 } 1268 1269 /** 1270 * @hide Candidate for future API inclusion 1271 */ setXfermode(Xfermode mode)1272 public final void setXfermode(Xfermode mode) { 1273 if (mXfermode != mode) { 1274 mXfermode = mode; 1275 mColorMod = true; 1276 applyColorMod(); 1277 invalidate(); 1278 } 1279 } 1280 1281 /** 1282 * Returns the active color filter for this ImageView. 1283 * 1284 * @return the active color filter for this ImageView 1285 * 1286 * @see #setColorFilter(android.graphics.ColorFilter) 1287 */ getColorFilter()1288 public ColorFilter getColorFilter() { 1289 return mColorFilter; 1290 } 1291 1292 /** 1293 * Apply an arbitrary colorfilter to the image. 1294 * 1295 * @param cf the colorfilter to apply (may be null) 1296 * 1297 * @see #getColorFilter() 1298 */ setColorFilter(ColorFilter cf)1299 public void setColorFilter(ColorFilter cf) { 1300 if (mColorFilter != cf) { 1301 mColorFilter = cf; 1302 mHasColorFilter = true; 1303 mColorMod = true; 1304 applyColorMod(); 1305 invalidate(); 1306 } 1307 } 1308 1309 /** 1310 * Returns the alpha that will be applied to the drawable of this ImageView. 1311 * 1312 * @return the alpha that will be applied to the drawable of this ImageView 1313 * 1314 * @see #setImageAlpha(int) 1315 */ getImageAlpha()1316 public int getImageAlpha() { 1317 return mAlpha; 1318 } 1319 1320 /** 1321 * Sets the alpha value that should be applied to the image. 1322 * 1323 * @param alpha the alpha value that should be applied to the image 1324 * 1325 * @see #getImageAlpha() 1326 */ 1327 @RemotableViewMethod setImageAlpha(int alpha)1328 public void setImageAlpha(int alpha) { 1329 setAlpha(alpha); 1330 } 1331 1332 /** 1333 * Sets the alpha value that should be applied to the image. 1334 * 1335 * @param alpha the alpha value that should be applied to the image 1336 * 1337 * @deprecated use #setImageAlpha(int) instead 1338 */ 1339 @Deprecated 1340 @RemotableViewMethod setAlpha(int alpha)1341 public void setAlpha(int alpha) { 1342 alpha &= 0xFF; // keep it legal 1343 if (mAlpha != alpha) { 1344 mAlpha = alpha; 1345 mColorMod = true; 1346 applyColorMod(); 1347 invalidate(); 1348 } 1349 } 1350 applyColorMod()1351 private void applyColorMod() { 1352 // Only mutate and apply when modifications have occurred. This should 1353 // not reset the mColorMod flag, since these filters need to be 1354 // re-applied if the Drawable is changed. 1355 if (mDrawable != null && mColorMod) { 1356 mDrawable = mDrawable.mutate(); 1357 if (mHasColorFilter) { 1358 mDrawable.setColorFilter(mColorFilter); 1359 } 1360 mDrawable.setXfermode(mXfermode); 1361 mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); 1362 } 1363 } 1364 1365 @Override isOpaque()1366 public boolean isOpaque() { 1367 return super.isOpaque() || mDrawable != null && mXfermode == null 1368 && mDrawable.getOpacity() == PixelFormat.OPAQUE 1369 && mAlpha * mViewAlphaScale >> 8 == 255 1370 && isFilledByImage(); 1371 } 1372 isFilledByImage()1373 private boolean isFilledByImage() { 1374 if (mDrawable == null) { 1375 return false; 1376 } 1377 1378 final Rect bounds = mDrawable.getBounds(); 1379 final Matrix matrix = mDrawMatrix; 1380 if (matrix == null) { 1381 return bounds.left <= 0 && bounds.top <= 0 && bounds.right >= getWidth() 1382 && bounds.bottom >= getHeight(); 1383 } else if (matrix.rectStaysRect()) { 1384 final RectF boundsSrc = mTempSrc; 1385 final RectF boundsDst = mTempDst; 1386 boundsSrc.set(bounds); 1387 matrix.mapRect(boundsDst, boundsSrc); 1388 return boundsDst.left <= 0 && boundsDst.top <= 0 && boundsDst.right >= getWidth() 1389 && boundsDst.bottom >= getHeight(); 1390 } else { 1391 // If the matrix doesn't map to a rectangle, assume the worst. 1392 return false; 1393 } 1394 } 1395 1396 @RemotableViewMethod 1397 @Override setVisibility(int visibility)1398 public void setVisibility(int visibility) { 1399 super.setVisibility(visibility); 1400 if (mDrawable != null) { 1401 mDrawable.setVisible(visibility == VISIBLE, false); 1402 } 1403 } 1404 1405 @Override onAttachedToWindow()1406 protected void onAttachedToWindow() { 1407 super.onAttachedToWindow(); 1408 if (mDrawable != null) { 1409 mDrawable.setVisible(getVisibility() == VISIBLE, false); 1410 } 1411 } 1412 1413 @Override onDetachedFromWindow()1414 protected void onDetachedFromWindow() { 1415 super.onDetachedFromWindow(); 1416 if (mDrawable != null) { 1417 mDrawable.setVisible(false, false); 1418 } 1419 } 1420 1421 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)1422 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1423 super.onInitializeAccessibilityEvent(event); 1424 event.setClassName(ImageView.class.getName()); 1425 } 1426 1427 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1428 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1429 super.onInitializeAccessibilityNodeInfo(info); 1430 info.setClassName(ImageView.class.getName()); 1431 } 1432 } 1433