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