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