• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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