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 if (mUri != null || mResource != resId) { 390 final int oldWidth = mDrawableWidth; 391 final int oldHeight = mDrawableHeight; 392 393 updateDrawable(null); 394 mResource = resId; 395 mUri = null; 396 397 resolveUri(); 398 399 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { 400 requestLayout(); 401 } 402 invalidate(); 403 } 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 } 532 533 /** 534 * Sets a Bitmap as the content of this ImageView. 535 * 536 * @param bm The bitmap to set 537 */ 538 @android.view.RemotableViewMethod setImageBitmap(Bitmap bm)539 public void setImageBitmap(Bitmap bm) { 540 // if this is used frequently, may handle bitmaps explicitly 541 // to reduce the intermediate drawable object 542 setImageDrawable(new BitmapDrawable(mContext.getResources(), bm)); 543 } 544 setImageState(int[] state, boolean merge)545 public void setImageState(int[] state, boolean merge) { 546 mState = state; 547 mMergeState = merge; 548 if (mDrawable != null) { 549 refreshDrawableState(); 550 resizeFromDrawable(); 551 } 552 } 553 554 @Override setSelected(boolean selected)555 public void setSelected(boolean selected) { 556 super.setSelected(selected); 557 resizeFromDrawable(); 558 } 559 560 /** 561 * Sets the image level, when it is constructed from a 562 * {@link android.graphics.drawable.LevelListDrawable}. 563 * 564 * @param level The new level for the image. 565 */ 566 @android.view.RemotableViewMethod setImageLevel(int level)567 public void setImageLevel(int level) { 568 mLevel = level; 569 if (mDrawable != null) { 570 mDrawable.setLevel(level); 571 resizeFromDrawable(); 572 } 573 } 574 575 /** 576 * Options for scaling the bounds of an image to the bounds of this view. 577 */ 578 public enum ScaleType { 579 /** 580 * Scale using the image matrix when drawing. The image matrix can be set using 581 * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax: 582 * <code>android:scaleType="matrix"</code>. 583 */ 584 MATRIX (0), 585 /** 586 * Scale the image using {@link Matrix.ScaleToFit#FILL}. 587 * From XML, use this syntax: <code>android:scaleType="fitXY"</code>. 588 */ 589 FIT_XY (1), 590 /** 591 * Scale the image using {@link Matrix.ScaleToFit#START}. 592 * From XML, use this syntax: <code>android:scaleType="fitStart"</code>. 593 */ 594 FIT_START (2), 595 /** 596 * Scale the image using {@link Matrix.ScaleToFit#CENTER}. 597 * From XML, use this syntax: 598 * <code>android:scaleType="fitCenter"</code>. 599 */ 600 FIT_CENTER (3), 601 /** 602 * Scale the image using {@link Matrix.ScaleToFit#END}. 603 * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>. 604 */ 605 FIT_END (4), 606 /** 607 * Center the image in the view, but perform no scaling. 608 * From XML, use this syntax: <code>android:scaleType="center"</code>. 609 */ 610 CENTER (5), 611 /** 612 * Scale the image uniformly (maintain the image's aspect ratio) so 613 * that both dimensions (width and height) of the image will be equal 614 * to or larger than the corresponding dimension of the view 615 * (minus padding). The image is then centered in the view. 616 * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>. 617 */ 618 CENTER_CROP (6), 619 /** 620 * Scale the image uniformly (maintain the image's aspect ratio) so 621 * that both dimensions (width and height) of the image will be equal 622 * to or less than the corresponding dimension of the view 623 * (minus padding). The image is then centered in the view. 624 * From XML, use this syntax: <code>android:scaleType="centerInside"</code>. 625 */ 626 CENTER_INSIDE (7); 627 ScaleType(int ni)628 ScaleType(int ni) { 629 nativeInt = ni; 630 } 631 final int nativeInt; 632 } 633 634 /** 635 * Controls how the image should be resized or moved to match the size 636 * of this ImageView. 637 * 638 * @param scaleType The desired scaling mode. 639 * 640 * @attr ref android.R.styleable#ImageView_scaleType 641 */ setScaleType(ScaleType scaleType)642 public void setScaleType(ScaleType scaleType) { 643 if (scaleType == null) { 644 throw new NullPointerException(); 645 } 646 647 if (mScaleType != scaleType) { 648 mScaleType = scaleType; 649 650 setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); 651 652 requestLayout(); 653 invalidate(); 654 } 655 } 656 657 /** 658 * Return the current scale type in use by this ImageView. 659 * 660 * @see ImageView.ScaleType 661 * 662 * @attr ref android.R.styleable#ImageView_scaleType 663 */ getScaleType()664 public ScaleType getScaleType() { 665 return mScaleType; 666 } 667 668 /** Return the view's optional matrix. This is applied to the 669 view's drawable when it is drawn. If there is no matrix, 670 this method will return an identity matrix. 671 Do not change this matrix in place but make a copy. 672 If you want a different matrix applied to the drawable, 673 be sure to call setImageMatrix(). 674 */ getImageMatrix()675 public Matrix getImageMatrix() { 676 if (mDrawMatrix == null) { 677 return new Matrix(Matrix.IDENTITY_MATRIX); 678 } 679 return mDrawMatrix; 680 } 681 setImageMatrix(Matrix matrix)682 public void setImageMatrix(Matrix matrix) { 683 // collaps null and identity to just null 684 if (matrix != null && matrix.isIdentity()) { 685 matrix = null; 686 } 687 688 // don't invalidate unless we're actually changing our matrix 689 if (matrix == null && !mMatrix.isIdentity() || 690 matrix != null && !mMatrix.equals(matrix)) { 691 mMatrix.set(matrix); 692 configureBounds(); 693 invalidate(); 694 } 695 } 696 697 /** 698 * Return whether this ImageView crops to padding. 699 * 700 * @return whether this ImageView crops to padding 701 * 702 * @see #setCropToPadding(boolean) 703 * 704 * @attr ref android.R.styleable#ImageView_cropToPadding 705 */ getCropToPadding()706 public boolean getCropToPadding() { 707 return mCropToPadding; 708 } 709 710 /** 711 * Sets whether this ImageView will crop to padding. 712 * 713 * @param cropToPadding whether this ImageView will crop to padding 714 * 715 * @see #getCropToPadding() 716 * 717 * @attr ref android.R.styleable#ImageView_cropToPadding 718 */ setCropToPadding(boolean cropToPadding)719 public void setCropToPadding(boolean cropToPadding) { 720 if (mCropToPadding != cropToPadding) { 721 mCropToPadding = cropToPadding; 722 requestLayout(); 723 invalidate(); 724 } 725 } 726 resolveUri()727 private void resolveUri() { 728 if (mDrawable != null) { 729 return; 730 } 731 732 Resources rsrc = getResources(); 733 if (rsrc == null) { 734 return; 735 } 736 737 Drawable d = null; 738 739 if (mResource != 0) { 740 try { 741 d = mContext.getDrawable(mResource); 742 } catch (Exception e) { 743 Log.w("ImageView", "Unable to find resource: " + mResource, e); 744 // Don't try again. 745 mUri = null; 746 } 747 } else if (mUri != null) { 748 String scheme = mUri.getScheme(); 749 if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { 750 try { 751 // Load drawable through Resources, to get the source density information 752 ContentResolver.OpenResourceIdResult r = 753 mContext.getContentResolver().getResourceId(mUri); 754 d = r.r.getDrawable(r.id, mContext.getTheme()); 755 } catch (Exception e) { 756 Log.w("ImageView", "Unable to open content: " + mUri, e); 757 } 758 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) 759 || ContentResolver.SCHEME_FILE.equals(scheme)) { 760 InputStream stream = null; 761 try { 762 stream = mContext.getContentResolver().openInputStream(mUri); 763 d = Drawable.createFromStream(stream, null); 764 } catch (Exception e) { 765 Log.w("ImageView", "Unable to open content: " + mUri, e); 766 } finally { 767 if (stream != null) { 768 try { 769 stream.close(); 770 } catch (IOException e) { 771 Log.w("ImageView", "Unable to close content: " + mUri, e); 772 } 773 } 774 } 775 } else { 776 d = Drawable.createFromPath(mUri.toString()); 777 } 778 779 if (d == null) { 780 System.out.println("resolveUri failed on bad bitmap uri: " + mUri); 781 // Don't try again. 782 mUri = null; 783 } 784 } else { 785 return; 786 } 787 788 updateDrawable(d); 789 } 790 791 @Override onCreateDrawableState(int extraSpace)792 public int[] onCreateDrawableState(int extraSpace) { 793 if (mState == null) { 794 return super.onCreateDrawableState(extraSpace); 795 } else if (!mMergeState) { 796 return mState; 797 } else { 798 return mergeDrawableStates( 799 super.onCreateDrawableState(extraSpace + mState.length), mState); 800 } 801 } 802 updateDrawable(Drawable d)803 private void updateDrawable(Drawable d) { 804 if (mDrawable != null) { 805 mDrawable.setCallback(null); 806 unscheduleDrawable(mDrawable); 807 } 808 809 mDrawable = d; 810 811 if (d != null) { 812 d.setCallback(this); 813 d.setLayoutDirection(getLayoutDirection()); 814 if (d.isStateful()) { 815 d.setState(getDrawableState()); 816 } 817 d.setVisible(getVisibility() == VISIBLE, true); 818 d.setLevel(mLevel); 819 mDrawableWidth = d.getIntrinsicWidth(); 820 mDrawableHeight = d.getIntrinsicHeight(); 821 applyImageTint(); 822 applyColorMod(); 823 configureBounds(); 824 } else { 825 mDrawableWidth = mDrawableHeight = -1; 826 } 827 } 828 resizeFromDrawable()829 private void resizeFromDrawable() { 830 Drawable d = mDrawable; 831 if (d != null) { 832 int w = d.getIntrinsicWidth(); 833 if (w < 0) w = mDrawableWidth; 834 int h = d.getIntrinsicHeight(); 835 if (h < 0) h = mDrawableHeight; 836 if (w != mDrawableWidth || h != mDrawableHeight) { 837 mDrawableWidth = w; 838 mDrawableHeight = h; 839 requestLayout(); 840 } 841 } 842 } 843 844 @Override onRtlPropertiesChanged(int layoutDirection)845 public void onRtlPropertiesChanged(int layoutDirection) { 846 super.onRtlPropertiesChanged(layoutDirection); 847 848 if (mDrawable != null) { 849 mDrawable.setLayoutDirection(layoutDirection); 850 } 851 } 852 853 private static final Matrix.ScaleToFit[] sS2FArray = { 854 Matrix.ScaleToFit.FILL, 855 Matrix.ScaleToFit.START, 856 Matrix.ScaleToFit.CENTER, 857 Matrix.ScaleToFit.END 858 }; 859 scaleTypeToScaleToFit(ScaleType st)860 private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { 861 // ScaleToFit enum to their corresponding Matrix.ScaleToFit values 862 return sS2FArray[st.nativeInt - 1]; 863 } 864 865 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)866 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 867 resolveUri(); 868 int w; 869 int h; 870 871 // Desired aspect ratio of the view's contents (not including padding) 872 float desiredAspect = 0.0f; 873 874 // We are allowed to change the view's width 875 boolean resizeWidth = false; 876 877 // We are allowed to change the view's height 878 boolean resizeHeight = false; 879 880 final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 881 final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 882 883 if (mDrawable == null) { 884 // If no drawable, its intrinsic size is 0. 885 mDrawableWidth = -1; 886 mDrawableHeight = -1; 887 w = h = 0; 888 } else { 889 w = mDrawableWidth; 890 h = mDrawableHeight; 891 if (w <= 0) w = 1; 892 if (h <= 0) h = 1; 893 894 // We are supposed to adjust view bounds to match the aspect 895 // ratio of our drawable. See if that is possible. 896 if (mAdjustViewBounds) { 897 resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; 898 resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; 899 900 desiredAspect = (float) w / (float) h; 901 } 902 } 903 904 int pleft = mPaddingLeft; 905 int pright = mPaddingRight; 906 int ptop = mPaddingTop; 907 int pbottom = mPaddingBottom; 908 909 int widthSize; 910 int heightSize; 911 912 if (resizeWidth || resizeHeight) { 913 /* If we get here, it means we want to resize to match the 914 drawables aspect ratio, and we have the freedom to change at 915 least one dimension. 916 */ 917 918 // Get the max possible width given our constraints 919 widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); 920 921 // Get the max possible height given our constraints 922 heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); 923 924 if (desiredAspect != 0.0f) { 925 // See what our actual aspect ratio is 926 float actualAspect = (float)(widthSize - pleft - pright) / 927 (heightSize - ptop - pbottom); 928 929 if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { 930 931 boolean done = false; 932 933 // Try adjusting width to be proportional to height 934 if (resizeWidth) { 935 int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + 936 pleft + pright; 937 938 // Allow the width to outgrow its original estimate if height is fixed. 939 if (!resizeHeight && !mAdjustViewBoundsCompat) { 940 widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); 941 } 942 943 if (newWidth <= widthSize) { 944 widthSize = newWidth; 945 done = true; 946 } 947 } 948 949 // Try adjusting height to be proportional to width 950 if (!done && resizeHeight) { 951 int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + 952 ptop + pbottom; 953 954 // Allow the height to outgrow its original estimate if width is fixed. 955 if (!resizeWidth && !mAdjustViewBoundsCompat) { 956 heightSize = resolveAdjustedSize(newHeight, mMaxHeight, 957 heightMeasureSpec); 958 } 959 960 if (newHeight <= heightSize) { 961 heightSize = newHeight; 962 } 963 } 964 } 965 } 966 } else { 967 /* We are either don't want to preserve the drawables aspect ratio, 968 or we are not allowed to change view dimensions. Just measure in 969 the normal way. 970 */ 971 w += pleft + pright; 972 h += ptop + pbottom; 973 974 w = Math.max(w, getSuggestedMinimumWidth()); 975 h = Math.max(h, getSuggestedMinimumHeight()); 976 977 widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); 978 heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); 979 } 980 981 setMeasuredDimension(widthSize, heightSize); 982 } 983 resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec)984 private int resolveAdjustedSize(int desiredSize, int maxSize, 985 int measureSpec) { 986 int result = desiredSize; 987 int specMode = MeasureSpec.getMode(measureSpec); 988 int specSize = MeasureSpec.getSize(measureSpec); 989 switch (specMode) { 990 case MeasureSpec.UNSPECIFIED: 991 /* Parent says we can be as big as we want. Just don't be larger 992 than max size imposed on ourselves. 993 */ 994 result = Math.min(desiredSize, maxSize); 995 break; 996 case MeasureSpec.AT_MOST: 997 // Parent says we can be as big as we want, up to specSize. 998 // Don't be larger than specSize, and don't be larger than 999 // the max size imposed on ourselves. 1000 result = Math.min(Math.min(desiredSize, specSize), maxSize); 1001 break; 1002 case MeasureSpec.EXACTLY: 1003 // No choice. Do what we are told. 1004 result = specSize; 1005 break; 1006 } 1007 return result; 1008 } 1009 1010 @Override setFrame(int l, int t, int r, int b)1011 protected boolean setFrame(int l, int t, int r, int b) { 1012 boolean changed = super.setFrame(l, t, r, b); 1013 mHaveFrame = true; 1014 configureBounds(); 1015 return changed; 1016 } 1017 configureBounds()1018 private void configureBounds() { 1019 if (mDrawable == null || !mHaveFrame) { 1020 return; 1021 } 1022 1023 int dwidth = mDrawableWidth; 1024 int dheight = mDrawableHeight; 1025 1026 int vwidth = getWidth() - mPaddingLeft - mPaddingRight; 1027 int vheight = getHeight() - mPaddingTop - mPaddingBottom; 1028 1029 boolean fits = (dwidth < 0 || vwidth == dwidth) && 1030 (dheight < 0 || vheight == dheight); 1031 1032 if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { 1033 /* If the drawable has no intrinsic size, or we're told to 1034 scaletofit, then we just fill our entire view. 1035 */ 1036 mDrawable.setBounds(0, 0, vwidth, vheight); 1037 mDrawMatrix = null; 1038 } else { 1039 // We need to do the scaling ourself, so have the drawable 1040 // use its native size. 1041 mDrawable.setBounds(0, 0, dwidth, dheight); 1042 1043 if (ScaleType.MATRIX == mScaleType) { 1044 // Use the specified matrix as-is. 1045 if (mMatrix.isIdentity()) { 1046 mDrawMatrix = null; 1047 } else { 1048 mDrawMatrix = mMatrix; 1049 } 1050 } else if (fits) { 1051 // The bitmap fits exactly, no transform needed. 1052 mDrawMatrix = null; 1053 } else if (ScaleType.CENTER == mScaleType) { 1054 // Center bitmap in view, no scaling. 1055 mDrawMatrix = mMatrix; 1056 mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f), 1057 (int) ((vheight - dheight) * 0.5f + 0.5f)); 1058 } else if (ScaleType.CENTER_CROP == mScaleType) { 1059 mDrawMatrix = mMatrix; 1060 1061 float scale; 1062 float dx = 0, dy = 0; 1063 1064 if (dwidth * vheight > vwidth * dheight) { 1065 scale = (float) vheight / (float) dheight; 1066 dx = (vwidth - dwidth * scale) * 0.5f; 1067 } else { 1068 scale = (float) vwidth / (float) dwidth; 1069 dy = (vheight - dheight * scale) * 0.5f; 1070 } 1071 1072 mDrawMatrix.setScale(scale, scale); 1073 mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); 1074 } else if (ScaleType.CENTER_INSIDE == mScaleType) { 1075 mDrawMatrix = mMatrix; 1076 float scale; 1077 float dx; 1078 float dy; 1079 1080 if (dwidth <= vwidth && dheight <= vheight) { 1081 scale = 1.0f; 1082 } else { 1083 scale = Math.min((float) vwidth / (float) dwidth, 1084 (float) vheight / (float) dheight); 1085 } 1086 1087 dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); 1088 dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); 1089 1090 mDrawMatrix.setScale(scale, scale); 1091 mDrawMatrix.postTranslate(dx, dy); 1092 } else { 1093 // Generate the required transform. 1094 mTempSrc.set(0, 0, dwidth, dheight); 1095 mTempDst.set(0, 0, vwidth, vheight); 1096 1097 mDrawMatrix = mMatrix; 1098 mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType)); 1099 } 1100 } 1101 } 1102 1103 @Override drawableStateChanged()1104 protected void drawableStateChanged() { 1105 super.drawableStateChanged(); 1106 Drawable d = mDrawable; 1107 if (d != null && d.isStateful()) { 1108 d.setState(getDrawableState()); 1109 } 1110 } 1111 1112 @Override drawableHotspotChanged(float x, float y)1113 public void drawableHotspotChanged(float x, float y) { 1114 super.drawableHotspotChanged(x, y); 1115 1116 if (mDrawable != null) { 1117 mDrawable.setHotspot(x, y); 1118 } 1119 } 1120 1121 /** @hide */ animateTransform(Matrix matrix)1122 public void animateTransform(Matrix matrix) { 1123 if (matrix == null) { 1124 mDrawable.setBounds(0, 0, getWidth(), getHeight()); 1125 } else { 1126 mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight); 1127 if (mDrawMatrix == null) { 1128 mDrawMatrix = new Matrix(); 1129 } 1130 mDrawMatrix.set(matrix); 1131 } 1132 invalidate(); 1133 } 1134 1135 @Override onDraw(Canvas canvas)1136 protected void onDraw(Canvas canvas) { 1137 super.onDraw(canvas); 1138 1139 if (mDrawable == null) { 1140 return; // couldn't resolve the URI 1141 } 1142 1143 if (mDrawableWidth == 0 || mDrawableHeight == 0) { 1144 return; // nothing to draw (empty bounds) 1145 } 1146 1147 if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { 1148 mDrawable.draw(canvas); 1149 } else { 1150 int saveCount = canvas.getSaveCount(); 1151 canvas.save(); 1152 1153 if (mCropToPadding) { 1154 final int scrollX = mScrollX; 1155 final int scrollY = mScrollY; 1156 canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop, 1157 scrollX + mRight - mLeft - mPaddingRight, 1158 scrollY + mBottom - mTop - mPaddingBottom); 1159 } 1160 1161 canvas.translate(mPaddingLeft, mPaddingTop); 1162 1163 if (mDrawMatrix != null) { 1164 canvas.concat(mDrawMatrix); 1165 } 1166 mDrawable.draw(canvas); 1167 canvas.restoreToCount(saveCount); 1168 } 1169 } 1170 1171 /** 1172 * <p>Return the offset of the widget's text baseline from the widget's top 1173 * boundary. </p> 1174 * 1175 * @return the offset of the baseline within the widget's bounds or -1 1176 * if baseline alignment is not supported. 1177 */ 1178 @Override 1179 @ViewDebug.ExportedProperty(category = "layout") getBaseline()1180 public int getBaseline() { 1181 if (mBaselineAlignBottom) { 1182 return getMeasuredHeight(); 1183 } else { 1184 return mBaseline; 1185 } 1186 } 1187 1188 /** 1189 * <p>Set the offset of the widget's text baseline from the widget's top 1190 * boundary. This value is overridden by the {@link #setBaselineAlignBottom(boolean)} 1191 * property.</p> 1192 * 1193 * @param baseline The baseline to use, or -1 if none is to be provided. 1194 * 1195 * @see #setBaseline(int) 1196 * @attr ref android.R.styleable#ImageView_baseline 1197 */ setBaseline(int baseline)1198 public void setBaseline(int baseline) { 1199 if (mBaseline != baseline) { 1200 mBaseline = baseline; 1201 requestLayout(); 1202 } 1203 } 1204 1205 /** 1206 * Set whether to set the baseline of this view to the bottom of the view. 1207 * Setting this value overrides any calls to setBaseline. 1208 * 1209 * @param aligned If true, the image view will be baseline aligned with 1210 * based on its bottom edge. 1211 * 1212 * @attr ref android.R.styleable#ImageView_baselineAlignBottom 1213 */ setBaselineAlignBottom(boolean aligned)1214 public void setBaselineAlignBottom(boolean aligned) { 1215 if (mBaselineAlignBottom != aligned) { 1216 mBaselineAlignBottom = aligned; 1217 requestLayout(); 1218 } 1219 } 1220 1221 /** 1222 * Return whether this view's baseline will be considered the bottom of the view. 1223 * 1224 * @see #setBaselineAlignBottom(boolean) 1225 */ getBaselineAlignBottom()1226 public boolean getBaselineAlignBottom() { 1227 return mBaselineAlignBottom; 1228 } 1229 1230 /** 1231 * Set a tinting option for the image. 1232 * 1233 * @param color Color tint to apply. 1234 * @param mode How to apply the color. The standard mode is 1235 * {@link PorterDuff.Mode#SRC_ATOP} 1236 * 1237 * @attr ref android.R.styleable#ImageView_tint 1238 */ setColorFilter(int color, PorterDuff.Mode mode)1239 public final void setColorFilter(int color, PorterDuff.Mode mode) { 1240 setColorFilter(new PorterDuffColorFilter(color, mode)); 1241 } 1242 1243 /** 1244 * Set a tinting option for the image. Assumes 1245 * {@link PorterDuff.Mode#SRC_ATOP} blending mode. 1246 * 1247 * @param color Color tint to apply. 1248 * @attr ref android.R.styleable#ImageView_tint 1249 */ 1250 @RemotableViewMethod setColorFilter(int color)1251 public final void setColorFilter(int color) { 1252 setColorFilter(color, PorterDuff.Mode.SRC_ATOP); 1253 } 1254 clearColorFilter()1255 public final void clearColorFilter() { 1256 setColorFilter(null); 1257 } 1258 1259 /** 1260 * @hide Candidate for future API inclusion 1261 */ setXfermode(Xfermode mode)1262 public final void setXfermode(Xfermode mode) { 1263 if (mXfermode != mode) { 1264 mXfermode = mode; 1265 mColorMod = true; 1266 applyColorMod(); 1267 invalidate(); 1268 } 1269 } 1270 1271 /** 1272 * Returns the active color filter for this ImageView. 1273 * 1274 * @return the active color filter for this ImageView 1275 * 1276 * @see #setColorFilter(android.graphics.ColorFilter) 1277 */ getColorFilter()1278 public ColorFilter getColorFilter() { 1279 return mColorFilter; 1280 } 1281 1282 /** 1283 * Apply an arbitrary colorfilter to the image. 1284 * 1285 * @param cf the colorfilter to apply (may be null) 1286 * 1287 * @see #getColorFilter() 1288 */ setColorFilter(ColorFilter cf)1289 public void setColorFilter(ColorFilter cf) { 1290 if (mColorFilter != cf) { 1291 mColorFilter = cf; 1292 mHasColorFilter = true; 1293 mColorMod = true; 1294 applyColorMod(); 1295 invalidate(); 1296 } 1297 } 1298 1299 /** 1300 * Returns the alpha that will be applied to the drawable of this ImageView. 1301 * 1302 * @return the alpha that will be applied to the drawable of this ImageView 1303 * 1304 * @see #setImageAlpha(int) 1305 */ getImageAlpha()1306 public int getImageAlpha() { 1307 return mAlpha; 1308 } 1309 1310 /** 1311 * Sets the alpha value that should be applied to the image. 1312 * 1313 * @param alpha the alpha value that should be applied to the image 1314 * 1315 * @see #getImageAlpha() 1316 */ 1317 @RemotableViewMethod setImageAlpha(int alpha)1318 public void setImageAlpha(int alpha) { 1319 setAlpha(alpha); 1320 } 1321 1322 /** 1323 * Sets the alpha value that should be applied to the image. 1324 * 1325 * @param alpha the alpha value that should be applied to the image 1326 * 1327 * @deprecated use #setImageAlpha(int) instead 1328 */ 1329 @Deprecated 1330 @RemotableViewMethod setAlpha(int alpha)1331 public void setAlpha(int alpha) { 1332 alpha &= 0xFF; // keep it legal 1333 if (mAlpha != alpha) { 1334 mAlpha = alpha; 1335 mColorMod = true; 1336 applyColorMod(); 1337 invalidate(); 1338 } 1339 } 1340 applyColorMod()1341 private void applyColorMod() { 1342 // Only mutate and apply when modifications have occurred. This should 1343 // not reset the mColorMod flag, since these filters need to be 1344 // re-applied if the Drawable is changed. 1345 if (mDrawable != null && mColorMod) { 1346 mDrawable = mDrawable.mutate(); 1347 if (mHasColorFilter) { 1348 mDrawable.setColorFilter(mColorFilter); 1349 } 1350 mDrawable.setXfermode(mXfermode); 1351 mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); 1352 } 1353 } 1354 1355 @Override isOpaque()1356 public boolean isOpaque() { 1357 return super.isOpaque() || mDrawable != null && mXfermode == null 1358 && mDrawable.getOpacity() == PixelFormat.OPAQUE 1359 && mAlpha * mViewAlphaScale >> 8 == 255 1360 && isFilledByImage(); 1361 } 1362 isFilledByImage()1363 private boolean isFilledByImage() { 1364 if (mDrawable == null) { 1365 return false; 1366 } 1367 1368 final Rect bounds = mDrawable.getBounds(); 1369 final Matrix matrix = mDrawMatrix; 1370 if (matrix == null) { 1371 return bounds.left <= 0 && bounds.top <= 0 && bounds.right >= getWidth() 1372 && bounds.bottom >= getHeight(); 1373 } else if (matrix.rectStaysRect()) { 1374 final RectF boundsSrc = mTempSrc; 1375 final RectF boundsDst = mTempDst; 1376 boundsSrc.set(bounds); 1377 matrix.mapRect(boundsDst, boundsSrc); 1378 return boundsDst.left <= 0 && boundsDst.top <= 0 && boundsDst.right >= getWidth() 1379 && boundsDst.bottom >= getHeight(); 1380 } else { 1381 // If the matrix doesn't map to a rectangle, assume the worst. 1382 return false; 1383 } 1384 } 1385 1386 @RemotableViewMethod 1387 @Override setVisibility(int visibility)1388 public void setVisibility(int visibility) { 1389 super.setVisibility(visibility); 1390 if (mDrawable != null) { 1391 mDrawable.setVisible(visibility == VISIBLE, false); 1392 } 1393 } 1394 1395 @Override onAttachedToWindow()1396 protected void onAttachedToWindow() { 1397 super.onAttachedToWindow(); 1398 if (mDrawable != null) { 1399 mDrawable.setVisible(getVisibility() == VISIBLE, false); 1400 } 1401 } 1402 1403 @Override onDetachedFromWindow()1404 protected void onDetachedFromWindow() { 1405 super.onDetachedFromWindow(); 1406 if (mDrawable != null) { 1407 mDrawable.setVisible(false, false); 1408 } 1409 } 1410 1411 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)1412 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1413 super.onInitializeAccessibilityEvent(event); 1414 event.setClassName(ImageView.class.getName()); 1415 } 1416 1417 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1418 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1419 super.onInitializeAccessibilityNodeInfo(info); 1420 info.setClassName(ImageView.class.getName()); 1421 } 1422 } 1423