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