blob: 67b7100ab9baaa60668c4055abbafc87b130fc6f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.widget;
18
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.DrawableRes;
Siva Velusamy94a6d152015-05-05 15:07:00 -070020import android.annotation.NonNull;
Alan Viverette91174362014-06-17 14:51:45 -070021import android.annotation.Nullable;
Jiaquan He1dd48d02017-05-01 14:18:39 -070022import android.annotation.TestApi;
Bjorn Bringert0f8555b2009-11-27 12:42:28 +000023import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Alan Viverette91174362014-06-17 14:51:45 -070025import android.content.res.ColorStateList;
Leon Scroggins III046a99e2018-01-31 14:59:29 -050026import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.res.TypedArray;
28import android.graphics.Bitmap;
29import android.graphics.Canvas;
30import android.graphics.ColorFilter;
Leon Scroggins III046a99e2018-01-31 14:59:29 -050031import android.graphics.ImageDecoder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.graphics.Matrix;
Alan Viverettee10dd532013-12-12 19:19:51 -080033import android.graphics.PixelFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.graphics.PorterDuff;
35import android.graphics.PorterDuffColorFilter;
Alan Viverettee10dd532013-12-12 19:19:51 -080036import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.graphics.RectF;
Adam Powell31049d72013-10-07 12:58:42 -070038import android.graphics.Xfermode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.graphics.drawable.BitmapDrawable;
40import android.graphics.drawable.Drawable;
Dan Sandlera22a3802015-05-13 00:12:47 -040041import android.graphics.drawable.Icon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.net.Uri;
Adam Powell7da4b732012-12-07 15:28:33 -080043import android.os.Build;
Dan Sandlera22a3802015-05-13 00:12:47 -040044import android.os.Handler;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070045import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.AttributeSet;
47import android.util.Log;
Jeff Sharkey2b95c242010-02-08 17:40:30 -080048import android.view.RemotableViewMethod;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.view.View;
Joe Onoratofd52b182010-11-10 18:00:52 -080050import android.view.ViewDebug;
Siva Velusamy94a6d152015-05-05 15:07:00 -070051import android.view.ViewHierarchyEncoder;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070052import android.view.accessibility.AccessibilityEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.widget.RemoteViews.RemoteView;
54
Alan Viverette91174362014-06-17 14:51:45 -070055import com.android.internal.R;
56
Romain Guy36143942012-12-03 10:10:22 -080057import java.io.IOException;
Romain Guy36143942012-12-03 10:10:22 -080058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059/**
Joe Fernandez704d43b2017-04-18 10:35:38 -070060 * Displays image resources, for example {@link android.graphics.Bitmap}
61 * or {@link android.graphics.drawable.Drawable} resources.
62 * ImageView is also commonly used to {@link #setImageTintMode(PorterDuff.Mode)
63 * apply tints to an image} and handle {@link #setScaleType(ScaleType) image scaling}.
64 *
65 * <p>
66 * The following XML snippet is a common example of using an ImageView to display an image resource:
67 * </p>
68 * <pre>
69 * &lt;LinearLayout
70 * xmlns:android="http://schemas.android.com/apk/res/android"
71 * android:layout_width="match_parent"
72 * android:layout_height="match_parent"&gt;
73 * &lt;ImageView
74 * android:layout_width="wrap_content"
75 * android:layout_height="wrap_content"
76 * android:src="@mipmap/ic_launcher"
77 * /&gt;
78 * &lt;/LinearLayout&gt;
79 * </pre>
80 *
81 * <p>
82 * To learn more about Drawables, see: <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
Laura Davis31756962018-06-18 16:09:32 -070083 * To learn more about working with Bitmaps, see: <a href="{@docRoot}topic/performance/graphics/index.html">Handling Bitmaps</a>.
Joe Fernandez704d43b2017-04-18 10:35:38 -070084 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 *
86 * @attr ref android.R.styleable#ImageView_adjustViewBounds
87 * @attr ref android.R.styleable#ImageView_src
88 * @attr ref android.R.styleable#ImageView_maxWidth
89 * @attr ref android.R.styleable#ImageView_maxHeight
90 * @attr ref android.R.styleable#ImageView_tint
91 * @attr ref android.R.styleable#ImageView_scaleType
92 * @attr ref android.R.styleable#ImageView_cropToPadding
93 */
94@RemoteView
95public class ImageView extends View {
Alan Viverette6aa92d12015-08-25 13:19:25 -040096 private static final String LOG_TAG = "ImageView";
97
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 // settable by the client
99 private Uri mUri;
100 private int mResource = 0;
101 private Matrix mMatrix;
102 private ScaleType mScaleType;
103 private boolean mHaveFrame = false;
104 private boolean mAdjustViewBounds = false;
105 private int mMaxWidth = Integer.MAX_VALUE;
106 private int mMaxHeight = Integer.MAX_VALUE;
107
108 // these are applied to the drawable
Alan Viverette91174362014-06-17 14:51:45 -0700109 private ColorFilter mColorFilter = null;
110 private boolean mHasColorFilter = false;
Adam Powell31049d72013-10-07 12:58:42 -0700111 private Xfermode mXfermode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private int mAlpha = 255;
Alan Viverette6aa92d12015-08-25 13:19:25 -0400113 private final int mViewAlphaScale = 256;
Jeff Sharkey2b95c242010-02-08 17:40:30 -0800114 private boolean mColorMod = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
116 private Drawable mDrawable = null;
Chris Craik82af13e2015-07-14 13:36:27 -0700117 private BitmapDrawable mRecycleableBitmapDrawable = null;
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700118 private ColorStateList mDrawableTintList = null;
119 private PorterDuff.Mode mDrawableTintMode = null;
Alan Viverette91174362014-06-17 14:51:45 -0700120 private boolean mHasDrawableTint = false;
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700121 private boolean mHasDrawableTintMode = false;
Alan Viverette91174362014-06-17 14:51:45 -0700122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 private int[] mState = null;
124 private boolean mMergeState = false;
125 private int mLevel = 0;
126 private int mDrawableWidth;
127 private int mDrawableHeight;
128 private Matrix mDrawMatrix = null;
129
130 // Avoid allocations...
Alan Viverette6aa92d12015-08-25 13:19:25 -0400131 private final RectF mTempSrc = new RectF();
132 private final RectF mTempDst = new RectF();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
134 private boolean mCropToPadding;
135
Joe Onoratofd52b182010-11-10 18:00:52 -0800136 private int mBaseline = -1;
137 private boolean mBaselineAlignBottom = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
Adam Powell06f9eb82016-08-24 17:09:01 -0700139 /** Compatibility modes dependent on targetSdkVersion of the app. */
140 private static boolean sCompatDone;
141
142 /** AdjustViewBounds behavior will be in compatibility mode for older apps. */
143 private static boolean sCompatAdjustViewBounds;
Adam Powell7da4b732012-12-07 15:28:33 -0800144
Alan Viverette270a3422015-08-25 13:25:16 -0400145 /** Whether to pass Resources when creating the source from a stream. */
Adam Powell06f9eb82016-08-24 17:09:01 -0700146 private static boolean sCompatUseCorrectStreamDensity;
147
148 /** Whether to use pre-Nougat drawable visibility dispatching conditions. */
149 private static boolean sCompatDrawableVisibilityDispatch;
Alan Viverette270a3422015-08-25 13:25:16 -0400150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private static final ScaleType[] sScaleTypeArray = {
152 ScaleType.MATRIX,
153 ScaleType.FIT_XY,
154 ScaleType.FIT_START,
155 ScaleType.FIT_CENTER,
156 ScaleType.FIT_END,
157 ScaleType.CENTER,
158 ScaleType.CENTER_CROP,
159 ScaleType.CENTER_INSIDE
160 };
161
162 public ImageView(Context context) {
163 super(context);
164 initImageView();
165 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700166
Scott Kennedy76c382e2015-02-10 23:15:39 -0800167 public ImageView(Context context, @Nullable AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 this(context, attrs, 0);
169 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700170
Scott Kennedy76c382e2015-02-10 23:15:39 -0800171 public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
Alan Viverette617feb92013-09-09 18:09:13 -0700172 this(context, attrs, defStyleAttr, 0);
173 }
174
Scott Kennedy76c382e2015-02-10 23:15:39 -0800175 public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
176 int defStyleRes) {
Alan Viverette617feb92013-09-09 18:09:13 -0700177 super(context, attrs, defStyleAttr, defStyleRes);
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 initImageView();
180
Felipe Lemed04a6972017-03-02 12:56:18 -0800181 // ImageView is not important by default, unless app developer overrode attribute.
182 if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
183 setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
184 }
185
Alan Viverette617feb92013-09-09 18:09:13 -0700186 final TypedArray a = context.obtainStyledAttributes(
Alan Viverette6aa92d12015-08-25 13:19:25 -0400187 attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188
Alan Viverette6aa92d12015-08-25 13:19:25 -0400189 final Drawable d = a.getDrawable(R.styleable.ImageView_src);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 if (d != null) {
191 setImageDrawable(d);
192 }
193
Alan Viverette6aa92d12015-08-25 13:19:25 -0400194 mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false);
195 mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1);
Joe Onoratofd52b182010-11-10 18:00:52 -0800196
Alan Viverette6aa92d12015-08-25 13:19:25 -0400197 setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
198 setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
199 setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
Joe Onoratofd52b182010-11-10 18:00:52 -0800200
Alan Viverette6aa92d12015-08-25 13:19:25 -0400201 final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 if (index >= 0) {
203 setScaleType(sScaleTypeArray[index]);
204 }
205
Alan Viverette91174362014-06-17 14:51:45 -0700206 if (a.hasValue(R.styleable.ImageView_tint)) {
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700207 mDrawableTintList = a.getColorStateList(R.styleable.ImageView_tint);
Alan Viverette91174362014-06-17 14:51:45 -0700208 mHasDrawableTint = true;
209
Alan Viverette38f93bc2014-09-15 16:25:09 -0700210 // Prior to L, this attribute would always set a color filter with
211 // blending mode SRC_ATOP. Preserve that default behavior.
212 mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
213 mHasDrawableTintMode = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
Alan Viverette91174362014-06-17 14:51:45 -0700215
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700216 if (a.hasValue(R.styleable.ImageView_tintMode)) {
217 mDrawableTintMode = Drawable.parseTintMode(a.getInt(
218 R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
219 mHasDrawableTintMode = true;
220 }
221
222 applyImageTint();
223
Alan Viverette6aa92d12015-08-25 13:19:25 -0400224 final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255);
Chet Haaseba1fe8e2011-10-15 07:35:51 -0700225 if (alpha != 255) {
Alan Viverette6aa92d12015-08-25 13:19:25 -0400226 setImageAlpha(alpha);
Chet Haaseba1fe8e2011-10-15 07:35:51 -0700227 }
228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 mCropToPadding = a.getBoolean(
Alan Viverette6aa92d12015-08-25 13:19:25 -0400230 R.styleable.ImageView_cropToPadding, false);
231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 a.recycle();
233
234 //need inflate syntax/reader for matrix
235 }
236
237 private void initImageView() {
Alan Viverette6aa92d12015-08-25 13:19:25 -0400238 mMatrix = new Matrix();
239 mScaleType = ScaleType.FIT_CENTER;
Alan Viverette270a3422015-08-25 13:25:16 -0400240
Adam Powell06f9eb82016-08-24 17:09:01 -0700241 if (!sCompatDone) {
242 final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
243 sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
244 sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
245 sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N;
246 sCompatDone = true;
247 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 }
249
250 @Override
Alan Viverettef6d87ec2016-03-11 10:09:14 -0500251 protected boolean verifyDrawable(@NonNull Drawable dr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 return mDrawable == dr || super.verifyDrawable(dr);
253 }
Chet Haase8473f5a2015-06-17 14:43:30 -0700254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 @Override
Dianne Hackborne2136772010-11-04 15:08:59 -0700256 public void jumpDrawablesToCurrentState() {
257 super.jumpDrawablesToCurrentState();
258 if (mDrawable != null) mDrawable.jumpToCurrentState();
259 }
260
261 @Override
Alan Viverettef6d87ec2016-03-11 10:09:14 -0500262 public void invalidateDrawable(@NonNull Drawable dr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 if (dr == mDrawable) {
Chet Haase8473f5a2015-06-17 14:43:30 -0700264 if (dr != null) {
265 // update cached drawable dimensions if they've changed
266 final int w = dr.getIntrinsicWidth();
267 final int h = dr.getIntrinsicHeight();
268 if (w != mDrawableWidth || h != mDrawableHeight) {
269 mDrawableWidth = w;
270 mDrawableHeight = h;
Alan Viverette01320de2015-10-20 09:48:58 -0400271 // updates the matrix, which is dependent on the bounds
272 configureBounds();
Chet Haase8473f5a2015-06-17 14:43:30 -0700273 }
274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 /* we invalidate the whole view in this case because it's very
276 * hard to know where the drawable actually is. This is made
277 * complicated because of the offsets and transformations that
278 * can be applied. In theory we could get the drawable's bounds
279 * and run them through the transformation and offsets, but this
280 * is probably not worth the effort.
281 */
282 invalidate();
283 } else {
284 super.invalidateDrawable(dr);
285 }
286 }
Joe Onoratofd52b182010-11-10 18:00:52 -0800287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 @Override
Chet Haasedb8c9a62012-03-21 18:54:18 -0700289 public boolean hasOverlappingRendering() {
Michael Jurka0931a852013-03-21 16:07:45 +0100290 return (getBackground() != null && getBackground().getCurrent() != null);
Chet Haasedb8c9a62012-03-21 18:54:18 -0700291 }
292
Alan Viverettea54956a2015-01-07 16:05:02 -0800293 /** @hide */
Chet Haasedb8c9a62012-03-21 18:54:18 -0700294 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800295 public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
296 super.onPopulateAccessibilityEventInternal(event);
Alan Viverette6aa92d12015-08-25 13:19:25 -0400297
298 final CharSequence contentDescription = getContentDescription();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700299 if (!TextUtils.isEmpty(contentDescription)) {
300 event.getText().add(contentDescription);
301 }
302 }
303
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 /**
Philip Milneaac722a2012-03-26 13:30:26 -0700305 * True when ImageView is adjusting its bounds
306 * to preserve the aspect ratio of its drawable
307 *
308 * @return whether to adjust the bounds of this view
Alan Viverette6aa92d12015-08-25 13:19:25 -0400309 * to preserve the original aspect ratio of the drawable
Philip Milneaac722a2012-03-26 13:30:26 -0700310 *
311 * @see #setAdjustViewBounds(boolean)
312 *
313 * @attr ref android.R.styleable#ImageView_adjustViewBounds
314 */
315 public boolean getAdjustViewBounds() {
316 return mAdjustViewBounds;
317 }
318
319 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 * Set this to true if you want the ImageView to adjust its bounds
321 * to preserve the aspect ratio of its drawable.
Adam Powell2c8cc972012-12-07 18:04:51 -0800322 *
323 * <p><strong>Note:</strong> If the application targets API level 17 or lower,
324 * adjustViewBounds will allow the drawable to shrink the view bounds, but not grow
325 * to fill available measured space in all cases. This is for compatibility with
326 * legacy {@link android.view.View.MeasureSpec MeasureSpec} and
327 * {@link android.widget.RelativeLayout RelativeLayout} behavior.</p>
328 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 * @param adjustViewBounds Whether to adjust the bounds of this view
Adam Powell2c8cc972012-12-07 18:04:51 -0800330 * to preserve the original aspect ratio of the drawable.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400331 *
Philip Milneaac722a2012-03-26 13:30:26 -0700332 * @see #getAdjustViewBounds()
333 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * @attr ref android.R.styleable#ImageView_adjustViewBounds
335 */
336 @android.view.RemotableViewMethod
337 public void setAdjustViewBounds(boolean adjustViewBounds) {
338 mAdjustViewBounds = adjustViewBounds;
339 if (adjustViewBounds) {
340 setScaleType(ScaleType.FIT_CENTER);
341 }
342 }
Philip Milneaac722a2012-03-26 13:30:26 -0700343
344 /**
345 * The maximum width of this view.
346 *
347 * @return The maximum width of this view
348 *
349 * @see #setMaxWidth(int)
350 *
351 * @attr ref android.R.styleable#ImageView_maxWidth
352 */
353 public int getMaxWidth() {
354 return mMaxWidth;
355 }
356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 /**
358 * An optional argument to supply a maximum width for this view. Only valid if
Romain Guy9fc27812011-04-27 14:21:41 -0700359 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum
360 * of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
361 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
362 * layout params to WRAP_CONTENT.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400363 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 * <p>
365 * Note that this view could be still smaller than 100 x 100 using this approach if the original
366 * image is small. To set an image to a fixed size, specify that size in the layout params and
Romain Guy9fc27812011-04-27 14:21:41 -0700367 * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
368 * the image within the bounds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 * </p>
Alan Viverette6aa92d12015-08-25 13:19:25 -0400370 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 * @param maxWidth maximum width for this view
Philip Milneaac722a2012-03-26 13:30:26 -0700372 *
373 * @see #getMaxWidth()
374 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 * @attr ref android.R.styleable#ImageView_maxWidth
376 */
377 @android.view.RemotableViewMethod
378 public void setMaxWidth(int maxWidth) {
379 mMaxWidth = maxWidth;
380 }
Philip Milneaac722a2012-03-26 13:30:26 -0700381
382 /**
383 * The maximum height of this view.
384 *
385 * @return The maximum height of this view
386 *
387 * @see #setMaxHeight(int)
388 *
389 * @attr ref android.R.styleable#ImageView_maxHeight
390 */
391 public int getMaxHeight() {
392 return mMaxHeight;
393 }
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 /**
396 * An optional argument to supply a maximum height for this view. Only valid if
Romain Guy9fc27812011-04-27 14:21:41 -0700397 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
398 * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
399 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
400 * layout params to WRAP_CONTENT.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400401 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 * <p>
403 * Note that this view could be still smaller than 100 x 100 using this approach if the original
404 * image is small. To set an image to a fixed size, specify that size in the layout params and
Romain Guy9fc27812011-04-27 14:21:41 -0700405 * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
406 * the image within the bounds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 * </p>
Alan Viverette6aa92d12015-08-25 13:19:25 -0400408 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 * @param maxHeight maximum height for this view
Philip Milneaac722a2012-03-26 13:30:26 -0700410 *
411 * @see #getMaxHeight()
412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 * @attr ref android.R.styleable#ImageView_maxHeight
414 */
415 @android.view.RemotableViewMethod
416 public void setMaxHeight(int maxHeight) {
417 mMaxHeight = maxHeight;
418 }
419
Joe Fernandez704d43b2017-04-18 10:35:38 -0700420 /**
421 * Gets the current Drawable, or null if no Drawable has been
422 * assigned.
423 *
424 * @return the view's drawable, or null if no drawable has been
425 * assigned.
426 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 public Drawable getDrawable() {
Chet Haasebfa11e42015-08-05 21:44:42 -0700428 if (mDrawable == mRecycleableBitmapDrawable) {
429 // Consider our cached version dirty since app code now has a reference to it
430 mRecycleableBitmapDrawable = null;
431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 return mDrawable;
433 }
434
Sunny Goyaldd292f42015-12-02 14:29:27 -0800435 private class ImageDrawableCallback implements Runnable {
436
437 private final Drawable drawable;
438 private final Uri uri;
439 private final int resource;
440
441 ImageDrawableCallback(Drawable drawable, Uri uri, int resource) {
442 this.drawable = drawable;
443 this.uri = uri;
444 this.resource = resource;
445 }
446
447 @Override
448 public void run() {
449 setImageDrawable(drawable);
450 mUri = uri;
451 mResource = resource;
452 }
453 }
454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 /**
456 * Sets a drawable as the content of this ImageView.
Brad Fitzpatrickc6b0b772010-08-27 11:43:56 -0700457 * <p class="note">This does Bitmap reading and decoding on the UI
458 * thread, which can cause a latency hiccup. If that's a concern,
Romain Guy9fc27812011-04-27 14:21:41 -0700459 * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or
460 * {@link #setImageBitmap(android.graphics.Bitmap)} and
Brad Fitzpatrickc6b0b772010-08-27 11:43:56 -0700461 * {@link android.graphics.BitmapFactory} instead.</p>
462 *
Chet Haase430742f2013-04-12 11:18:36 -0700463 * @param resId the resource identifier of the drawable
Brad Fitzpatrickc6b0b772010-08-27 11:43:56 -0700464 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 * @attr ref android.R.styleable#ImageView_src
466 */
Sunny Goyaldd292f42015-12-02 14:29:27 -0800467 @android.view.RemotableViewMethod(asyncImpl="setImageResourceAsync")
Tor Norbye7b9c9122013-05-30 16:48:33 -0700468 public void setImageResource(@DrawableRes int resId) {
Alan Viveretted739d7b2014-10-29 17:26:37 -0700469 // The resource configuration may have changed, so we should always
470 // try to load the resource even if the resId hasn't changed.
471 final int oldWidth = mDrawableWidth;
472 final int oldHeight = mDrawableHeight;
Alan Viverette4803bc12014-02-03 14:32:07 -0800473
Alan Viveretted739d7b2014-10-29 17:26:37 -0700474 updateDrawable(null);
475 mResource = resId;
476 mUri = null;
Adam Powellf96ce022012-08-09 15:08:33 -0700477
Alan Viveretted739d7b2014-10-29 17:26:37 -0700478 resolveUri();
Adam Powellf96ce022012-08-09 15:08:33 -0700479
Alan Viveretted739d7b2014-10-29 17:26:37 -0700480 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
481 requestLayout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
Alan Viveretted739d7b2014-10-29 17:26:37 -0700483 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485
Sunny Goyaldd292f42015-12-02 14:29:27 -0800486 /** @hide **/
487 public Runnable setImageResourceAsync(@DrawableRes int resId) {
Sunny Goyal5c022632016-02-17 16:30:41 -0800488 Drawable d = null;
489 if (resId != 0) {
490 try {
491 d = getContext().getDrawable(resId);
492 } catch (Exception e) {
493 Log.w(LOG_TAG, "Unable to find resource: " + resId, e);
494 resId = 0;
495 }
496 }
497 return new ImageDrawableCallback(d, null, resId);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800498 }
499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 /**
501 * Sets the content of this ImageView to the specified Uri.
Joe Fernandez704d43b2017-04-18 10:35:38 -0700502 * Note that you use this method to load images from a local Uri only.
503 * <p/>
504 * To learn how to display images from a remote Uri see: <a href="https://developer.android.com/topic/performance/graphics/index.html">Handling Bitmaps</a>
505 * <p/>
Brad Fitzpatrickc6b0b772010-08-27 11:43:56 -0700506 * <p class="note">This does Bitmap reading and decoding on the UI
507 * thread, which can cause a latency hiccup. If that's a concern,
Alan Viverette6ef129e2015-06-30 13:32:27 -0700508 * consider using {@link #setImageDrawable(Drawable)} or
Romain Guy9fc27812011-04-27 14:21:41 -0700509 * {@link #setImageBitmap(android.graphics.Bitmap)} and
Brad Fitzpatrickc6b0b772010-08-27 11:43:56 -0700510 * {@link android.graphics.BitmapFactory} instead.</p>
511 *
Alan Viveretteac21e562016-10-11 17:42:21 -0400512 * <p class="note">On devices running SDK < 24, this method will fail to
513 * apply correct density scaling to images loaded from
514 * {@link ContentResolver#SCHEME_CONTENT content} and
515 * {@link ContentResolver#SCHEME_FILE file} schemes. Applications running
516 * on devices with SDK >= 24 <strong>MUST</strong> specify the
517 * {@code targetSdkVersion} in their manifest as 24 or above for density
518 * scaling to be applied to images loaded from these schemes.</p>
519 *
Alan Viverette6ef129e2015-06-30 13:32:27 -0700520 * @param uri the Uri of an image, or {@code null} to clear the content
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 */
Sunny Goyaldd292f42015-12-02 14:29:27 -0800522 @android.view.RemotableViewMethod(asyncImpl="setImageURIAsync")
Alan Viverette6ef129e2015-06-30 13:32:27 -0700523 public void setImageURI(@Nullable Uri uri) {
Alan Viverette6aa92d12015-08-25 13:19:25 -0400524 if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 updateDrawable(null);
526 mResource = 0;
527 mUri = uri;
Adam Powellf96ce022012-08-09 15:08:33 -0700528
529 final int oldWidth = mDrawableWidth;
530 final int oldHeight = mDrawableHeight;
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 resolveUri();
Adam Powellf96ce022012-08-09 15:08:33 -0700533
534 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
535 requestLayout();
536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 invalidate();
538 }
539 }
540
Sunny Goyaldd292f42015-12-02 14:29:27 -0800541 /** @hide **/
542 public Runnable setImageURIAsync(@Nullable Uri uri) {
543 if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
544 Drawable d = uri == null ? null : getDrawableFromUri(uri);
545 if (d == null) {
546 // Do not set the URI if the drawable couldn't be loaded.
547 uri = null;
548 }
549 return new ImageDrawableCallback(d, uri, 0);
550 }
551 return null;
552 }
553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 /**
555 * Sets a drawable as the content of this ImageView.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400556 *
Alan Viverette6ef129e2015-06-30 13:32:27 -0700557 * @param drawable the Drawable to set, or {@code null} to clear the
558 * content
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 */
Alan Viverette6ef129e2015-06-30 13:32:27 -0700560 public void setImageDrawable(@Nullable Drawable drawable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 if (mDrawable != drawable) {
562 mResource = 0;
563 mUri = null;
Romain Guy9e648c42011-08-17 20:04:27 -0700564
Adam Powellf96ce022012-08-09 15:08:33 -0700565 final int oldWidth = mDrawableWidth;
566 final int oldHeight = mDrawableHeight;
Romain Guy9e648c42011-08-17 20:04:27 -0700567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 updateDrawable(drawable);
Adam Powell2a0e99d2011-08-04 10:55:03 -0700569
Romain Guy9e648c42011-08-17 20:04:27 -0700570 if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
Adam Powell2a0e99d2011-08-04 10:55:03 -0700571 requestLayout();
572 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 invalidate();
574 }
575 }
576
577 /**
Dan Sandlera22a3802015-05-13 00:12:47 -0400578 * Sets the content of this ImageView to the specified Icon.
579 *
Alan Viverette6ef129e2015-06-30 13:32:27 -0700580 * <p class="note">Depending on the Icon type, this may do Bitmap reading
581 * and decoding on the UI thread, which can cause UI jank. If that's a
582 * concern, consider using
Chris Banese695bb82015-05-14 10:12:20 +0100583 * {@link Icon#loadDrawableAsync(Context, Icon.OnDrawableLoadedListener, Handler)}
Alan Viverette6ef129e2015-06-30 13:32:27 -0700584 * and then {@link #setImageDrawable(android.graphics.drawable.Drawable)}
585 * instead.</p>
Dan Sandlera22a3802015-05-13 00:12:47 -0400586 *
Alan Viverette6ef129e2015-06-30 13:32:27 -0700587 * @param icon an Icon holding the desired image, or {@code null} to clear
588 * the content
Dan Sandlera22a3802015-05-13 00:12:47 -0400589 */
Sunny Goyaldd292f42015-12-02 14:29:27 -0800590 @android.view.RemotableViewMethod(asyncImpl="setImageIconAsync")
Alan Viverette6ef129e2015-06-30 13:32:27 -0700591 public void setImageIcon(@Nullable Icon icon) {
592 setImageDrawable(icon == null ? null : icon.loadDrawable(mContext));
Dan Sandlera22a3802015-05-13 00:12:47 -0400593 }
594
Sunny Goyaldd292f42015-12-02 14:29:27 -0800595 /** @hide **/
596 public Runnable setImageIconAsync(@Nullable Icon icon) {
597 return new ImageDrawableCallback(icon == null ? null : icon.loadDrawable(mContext), null, 0);
598 }
599
Dan Sandlera22a3802015-05-13 00:12:47 -0400600 /**
Alan Viverette91174362014-06-17 14:51:45 -0700601 * Applies a tint to the image drawable. Does not modify the current tint
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700602 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
Alan Viverette91174362014-06-17 14:51:45 -0700603 * <p>
604 * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
605 * mutate the drawable and apply the specified tint and tint mode using
Alan Viverettea4264452014-07-28 16:02:55 -0700606 * {@link Drawable#setTintList(ColorStateList)}.
Alan Viverette2b4e14c2016-07-21 16:25:13 -0400607 * <p>
608 * <em>Note:</em> The default tint mode used by this setter is NOT
609 * consistent with the default tint mode used by the
610 * {@link android.R.styleable#ImageView_tint android:tint}
611 * attribute. If the {@code android:tint} attribute is specified, the
612 * default tint mode will be set to {@link PorterDuff.Mode#SRC_ATOP} to
613 * ensure consistency with earlier versions of the platform.
Alan Viverette91174362014-06-17 14:51:45 -0700614 *
615 * @param tint the tint to apply, may be {@code null} to clear tint
616 *
617 * @attr ref android.R.styleable#ImageView_tint
Alan Viverettea4264452014-07-28 16:02:55 -0700618 * @see #getImageTintList()
619 * @see Drawable#setTintList(ColorStateList)
Alan Viverette91174362014-06-17 14:51:45 -0700620 */
Alan Viverettea4264452014-07-28 16:02:55 -0700621 public void setImageTintList(@Nullable ColorStateList tint) {
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700622 mDrawableTintList = tint;
Alan Viverette4f64c042014-07-21 17:49:13 -0700623 mHasDrawableTint = true;
624
Alan Viverettea4264452014-07-28 16:02:55 -0700625 applyImageTint();
Alan Viverette91174362014-06-17 14:51:45 -0700626 }
627
628 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -0700629 * Get the current {@link android.content.res.ColorStateList} used to tint the image Drawable,
630 * or null if no tint is applied.
631 *
Alan Viverette91174362014-06-17 14:51:45 -0700632 * @return the tint applied to the image drawable
633 * @attr ref android.R.styleable#ImageView_tint
Alan Viverettea4264452014-07-28 16:02:55 -0700634 * @see #setImageTintList(ColorStateList)
Alan Viverette91174362014-06-17 14:51:45 -0700635 */
636 @Nullable
Alan Viverettea4264452014-07-28 16:02:55 -0700637 public ColorStateList getImageTintList() {
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700638 return mDrawableTintList;
Alan Viverette91174362014-06-17 14:51:45 -0700639 }
640
641 /**
642 * Specifies the blending mode used to apply the tint specified by
Alan Viverettea4264452014-07-28 16:02:55 -0700643 * {@link #setImageTintList(ColorStateList)}} to the image drawable. The default
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700644 * mode is {@link PorterDuff.Mode#SRC_IN}.
Alan Viverette91174362014-06-17 14:51:45 -0700645 *
646 * @param tintMode the blending mode used to apply the tint, may be
647 * {@code null} to clear tint
648 * @attr ref android.R.styleable#ImageView_tintMode
Alan Viverettea4264452014-07-28 16:02:55 -0700649 * @see #getImageTintMode()
650 * @see Drawable#setTintMode(PorterDuff.Mode)
Alan Viverette91174362014-06-17 14:51:45 -0700651 */
Alan Viverettea4264452014-07-28 16:02:55 -0700652 public void setImageTintMode(@Nullable PorterDuff.Mode tintMode) {
Alan Viverette4f64c042014-07-21 17:49:13 -0700653 mDrawableTintMode = tintMode;
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700654 mHasDrawableTintMode = true;
Alan Viverette4f64c042014-07-21 17:49:13 -0700655
Alan Viverettea4264452014-07-28 16:02:55 -0700656 applyImageTint();
Alan Viverette91174362014-06-17 14:51:45 -0700657 }
658
659 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -0700660 * Gets the blending mode used to apply the tint to the image Drawable
661 * @return the blending mode used to apply the tint to the image Drawable
Alan Viverette91174362014-06-17 14:51:45 -0700662 * @attr ref android.R.styleable#ImageView_tintMode
Alan Viverettea4264452014-07-28 16:02:55 -0700663 * @see #setImageTintMode(PorterDuff.Mode)
Alan Viverette91174362014-06-17 14:51:45 -0700664 */
665 @Nullable
Alan Viverettea4264452014-07-28 16:02:55 -0700666 public PorterDuff.Mode getImageTintMode() {
Alan Viverette91174362014-06-17 14:51:45 -0700667 return mDrawableTintMode;
668 }
669
Alan Viverettea4264452014-07-28 16:02:55 -0700670 private void applyImageTint() {
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700671 if (mDrawable != null && (mHasDrawableTint || mHasDrawableTintMode)) {
Alan Viverette91174362014-06-17 14:51:45 -0700672 mDrawable = mDrawable.mutate();
Alan Viveretteb56f5d22014-09-14 15:48:50 -0700673
674 if (mHasDrawableTint) {
675 mDrawable.setTintList(mDrawableTintList);
676 }
677
678 if (mHasDrawableTintMode) {
679 mDrawable.setTintMode(mDrawableTintMode);
680 }
Alan Viveretted5133792014-10-28 14:41:36 -0700681
682 // The drawable (or one of its children) may not have been
683 // stateful before applying the tint, so let's try again.
684 if (mDrawable.isStateful()) {
685 mDrawable.setState(getDrawableState());
686 }
Alan Viverette91174362014-06-17 14:51:45 -0700687 }
688 }
689
690 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 * Sets a Bitmap as the content of this ImageView.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400692 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 * @param bm The bitmap to set
694 */
695 @android.view.RemotableViewMethod
696 public void setImageBitmap(Bitmap bm) {
John Reck5a135692015-07-10 10:02:58 -0700697 // Hacky fix to force setImageDrawable to do a full setImageDrawable
698 // instead of doing an object reference comparison
699 mDrawable = null;
700 if (mRecycleableBitmapDrawable == null) {
Chris Craik82af13e2015-07-14 13:36:27 -0700701 mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);
John Reckb7ba1222015-07-09 17:37:34 -0700702 } else {
John Reck5a135692015-07-10 10:02:58 -0700703 mRecycleableBitmapDrawable.setBitmap(bm);
John Reckb7ba1222015-07-09 17:37:34 -0700704 }
John Reck5a135692015-07-10 10:02:58 -0700705 setImageDrawable(mRecycleableBitmapDrawable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 }
707
Joe Fernandez704d43b2017-04-18 10:35:38 -0700708 /**
709 * Set the state of the current {@link android.graphics.drawable.StateListDrawable}.
710 * For more information about State List Drawables, see: <a href="https://developer.android.com/guide/topics/resources/drawable-resource.html#StateList">the Drawable Resource Guide</a>.
711 *
712 * @param state the state to set for the StateListDrawable
713 * @param merge if true, merges the state values for the state you specify into the current state
714 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 public void setImageState(int[] state, boolean merge) {
716 mState = state;
717 mMergeState = merge;
718 if (mDrawable != null) {
719 refreshDrawableState();
720 resizeFromDrawable();
721 }
722 }
723
724 @Override
725 public void setSelected(boolean selected) {
726 super.setSelected(selected);
727 resizeFromDrawable();
728 }
729
The Android Open Source Project4df24232009-03-05 14:34:35 -0800730 /**
Alan Viverette6aa92d12015-08-25 13:19:25 -0400731 * Sets the image level, when it is constructed from a
The Android Open Source Project4df24232009-03-05 14:34:35 -0800732 * {@link android.graphics.drawable.LevelListDrawable}.
733 *
734 * @param level The new level for the image.
735 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 @android.view.RemotableViewMethod
737 public void setImageLevel(int level) {
738 mLevel = level;
739 if (mDrawable != null) {
740 mDrawable.setLevel(level);
741 resizeFromDrawable();
742 }
743 }
744
745 /**
746 * Options for scaling the bounds of an image to the bounds of this view.
747 */
748 public enum ScaleType {
749 /**
750 * Scale using the image matrix when drawing. The image matrix can be set using
751 * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax:
752 * <code>android:scaleType="matrix"</code>.
753 */
754 MATRIX (0),
755 /**
756 * Scale the image using {@link Matrix.ScaleToFit#FILL}.
757 * From XML, use this syntax: <code>android:scaleType="fitXY"</code>.
758 */
759 FIT_XY (1),
760 /**
761 * Scale the image using {@link Matrix.ScaleToFit#START}.
762 * From XML, use this syntax: <code>android:scaleType="fitStart"</code>.
763 */
764 FIT_START (2),
765 /**
766 * Scale the image using {@link Matrix.ScaleToFit#CENTER}.
767 * From XML, use this syntax:
768 * <code>android:scaleType="fitCenter"</code>.
769 */
770 FIT_CENTER (3),
771 /**
772 * Scale the image using {@link Matrix.ScaleToFit#END}.
773 * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>.
774 */
775 FIT_END (4),
776 /**
777 * Center the image in the view, but perform no scaling.
778 * From XML, use this syntax: <code>android:scaleType="center"</code>.
779 */
780 CENTER (5),
781 /**
782 * Scale the image uniformly (maintain the image's aspect ratio) so
783 * that both dimensions (width and height) of the image will be equal
784 * to or larger than the corresponding dimension of the view
785 * (minus padding). The image is then centered in the view.
786 * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>.
787 */
788 CENTER_CROP (6),
789 /**
790 * Scale the image uniformly (maintain the image's aspect ratio) so
791 * that both dimensions (width and height) of the image will be equal
792 * to or less than the corresponding dimension of the view
793 * (minus padding). The image is then centered in the view.
794 * From XML, use this syntax: <code>android:scaleType="centerInside"</code>.
795 */
796 CENTER_INSIDE (7);
Alan Viverette6aa92d12015-08-25 13:19:25 -0400797
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 ScaleType(int ni) {
799 nativeInt = ni;
800 }
801 final int nativeInt;
802 }
803
804 /**
805 * Controls how the image should be resized or moved to match the size
806 * of this ImageView.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400807 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 * @param scaleType The desired scaling mode.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400809 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 * @attr ref android.R.styleable#ImageView_scaleType
811 */
812 public void setScaleType(ScaleType scaleType) {
813 if (scaleType == null) {
814 throw new NullPointerException();
815 }
816
817 if (mScaleType != scaleType) {
818 mScaleType = scaleType;
819
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 requestLayout();
821 invalidate();
822 }
823 }
Alan Viverette6aa92d12015-08-25 13:19:25 -0400824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -0700826 * Returns the current ScaleType that is used to scale the bounds of an image to the bounds of the ImageView.
827 * @return The ScaleType used to scale the image.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 * @see ImageView.ScaleType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 * @attr ref android.R.styleable#ImageView_scaleType
830 */
831 public ScaleType getScaleType() {
832 return mScaleType;
833 }
834
Joe Fernandez704d43b2017-04-18 10:35:38 -0700835 /** Returns the view's optional matrix. This is applied to the
John Spurlock0196e562014-02-03 09:00:35 -0500836 view's drawable when it is drawn. If there is no matrix,
Romain Guy53704052013-01-30 16:25:50 -0800837 this method will return an identity matrix.
838 Do not change this matrix in place but make a copy.
839 If you want a different matrix applied to the drawable,
840 be sure to call setImageMatrix().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 */
842 public Matrix getImageMatrix() {
Dake Gu1ee60172013-01-03 15:11:43 -0800843 if (mDrawMatrix == null) {
Romain Guy53704052013-01-30 16:25:50 -0800844 return new Matrix(Matrix.IDENTITY_MATRIX);
Dake Gu1ee60172013-01-03 15:11:43 -0800845 }
846 return mDrawMatrix;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 }
848
Joseph Cooperd96fdbd2015-04-03 14:00:31 -0700849 /**
850 * Adds a transformation {@link Matrix} that is applied
851 * to the view's drawable when it is drawn. Allows custom scaling,
852 * translation, and perspective distortion.
Alan Viverette6aa92d12015-08-25 13:19:25 -0400853 *
Joe Fernandez704d43b2017-04-18 10:35:38 -0700854 * @param matrix The transformation parameters in matrix form.
Joseph Cooperd96fdbd2015-04-03 14:00:31 -0700855 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 public void setImageMatrix(Matrix matrix) {
Joseph Cooperd96fdbd2015-04-03 14:00:31 -0700857 // collapse null and identity to just null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 if (matrix != null && matrix.isIdentity()) {
859 matrix = null;
860 }
Joseph Cooperd96fdbd2015-04-03 14:00:31 -0700861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 // don't invalidate unless we're actually changing our matrix
863 if (matrix == null && !mMatrix.isIdentity() ||
864 matrix != null && !mMatrix.equals(matrix)) {
865 mMatrix.set(matrix);
Chih-Chung Changf8887262009-07-30 15:36:13 +0800866 configureBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 invalidate();
868 }
869 }
Philip Milneaac722a2012-03-26 13:30:26 -0700870
871 /**
872 * Return whether this ImageView crops to padding.
873 *
874 * @return whether this ImageView crops to padding
875 *
876 * @see #setCropToPadding(boolean)
877 *
878 * @attr ref android.R.styleable#ImageView_cropToPadding
879 */
880 public boolean getCropToPadding() {
881 return mCropToPadding;
882 }
883
884 /**
885 * Sets whether this ImageView will crop to padding.
886 *
887 * @param cropToPadding whether this ImageView will crop to padding
888 *
889 * @see #getCropToPadding()
890 *
891 * @attr ref android.R.styleable#ImageView_cropToPadding
892 */
893 public void setCropToPadding(boolean cropToPadding) {
894 if (mCropToPadding != cropToPadding) {
895 mCropToPadding = cropToPadding;
896 requestLayout();
897 invalidate();
898 }
899 }
900
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 private void resolveUri() {
902 if (mDrawable != null) {
903 return;
904 }
905
Sunny Goyaldd292f42015-12-02 14:29:27 -0800906 if (getResources() == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 return;
908 }
909
910 Drawable d = null;
911
912 if (mResource != 0) {
913 try {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800914 d = mContext.getDrawable(mResource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 } catch (Exception e) {
Alan Viverette6aa92d12015-08-25 13:19:25 -0400916 Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 // Don't try again.
Sunny Goyal5c022632016-02-17 16:30:41 -0800918 mResource = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
920 } else if (mUri != null) {
Sunny Goyaldd292f42015-12-02 14:29:27 -0800921 d = getDrawableFromUri(mUri);
Alan Viverette6aa92d12015-08-25 13:19:25 -0400922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 if (d == null) {
Alan Viverette6aa92d12015-08-25 13:19:25 -0400924 Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 // Don't try again.
926 mUri = null;
927 }
928 } else {
929 return;
930 }
931
932 updateDrawable(d);
933 }
934
Sunny Goyaldd292f42015-12-02 14:29:27 -0800935 private Drawable getDrawableFromUri(Uri uri) {
936 final String scheme = uri.getScheme();
937 if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
938 try {
939 // Load drawable through Resources, to get the source density information
940 ContentResolver.OpenResourceIdResult r =
941 mContext.getContentResolver().getResourceId(uri);
942 return r.r.getDrawable(r.id, mContext.getTheme());
943 } catch (Exception e) {
944 Log.w(LOG_TAG, "Unable to open content: " + uri, e);
945 }
946 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
947 || ContentResolver.SCHEME_FILE.equals(scheme)) {
Sunny Goyaldd292f42015-12-02 14:29:27 -0800948 try {
Leon Scroggins III046a99e2018-01-31 14:59:29 -0500949 Resources res = sCompatUseCorrectStreamDensity ? getResources() : null;
950 ImageDecoder.Source src = ImageDecoder.createSource(mContext.getContentResolver(),
951 uri, res);
952 return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
953 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
954 });
955 } catch (IOException e) {
Sunny Goyaldd292f42015-12-02 14:29:27 -0800956 Log.w(LOG_TAG, "Unable to open content: " + uri, e);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800957 }
958 } else {
959 return Drawable.createFromPath(uri.toString());
960 }
961 return null;
962 }
963
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 @Override
965 public int[] onCreateDrawableState(int extraSpace) {
966 if (mState == null) {
967 return super.onCreateDrawableState(extraSpace);
968 } else if (!mMergeState) {
969 return mState;
970 } else {
971 return mergeDrawableStates(
972 super.onCreateDrawableState(extraSpace + mState.length), mState);
973 }
974 }
975
976 private void updateDrawable(Drawable d) {
John Reck5a135692015-07-10 10:02:58 -0700977 if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
978 mRecycleableBitmapDrawable.setBitmap(null);
979 }
980
Adam Powell06f9eb82016-08-24 17:09:01 -0700981 boolean sameDrawable = false;
982
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 if (mDrawable != null) {
Adam Powell06f9eb82016-08-24 17:09:01 -0700984 sameDrawable = mDrawable == d;
Adam Powell35e2ea02016-03-22 10:40:29 -0700985 mDrawable.setCallback(null);
986 unscheduleDrawable(mDrawable);
Adam Powell06f9eb82016-08-24 17:09:01 -0700987 if (!sCompatDrawableVisibilityDispatch && !sameDrawable && isAttachedToWindow()) {
Adam Powell4b2e12c2016-03-31 15:35:35 -0700988 mDrawable.setVisible(false, false);
989 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 }
Alan Viverette91174362014-06-17 14:51:45 -0700991
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 mDrawable = d;
Alan Viverette91174362014-06-17 14:51:45 -0700993
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 if (d != null) {
995 d.setCallback(this);
Alan Viverette91174362014-06-17 14:51:45 -0700996 d.setLayoutDirection(getLayoutDirection());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 if (d.isStateful()) {
998 d.setState(getDrawableState());
999 }
Adam Powell06f9eb82016-08-24 17:09:01 -07001000 if (!sameDrawable || sCompatDrawableVisibilityDispatch) {
1001 final boolean visible = sCompatDrawableVisibilityDispatch
1002 ? getVisibility() == VISIBLE
1003 : isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
1004 d.setVisible(visible, true);
1005 }
Alan Viverette91174362014-06-17 14:51:45 -07001006 d.setLevel(mLevel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 mDrawableWidth = d.getIntrinsicWidth();
1008 mDrawableHeight = d.getIntrinsicHeight();
Alan Viverettea4264452014-07-28 16:02:55 -07001009 applyImageTint();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 applyColorMod();
Alan Viveretted5133792014-10-28 14:41:36 -07001011
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 configureBounds();
Romain Guy9e648c42011-08-17 20:04:27 -07001013 } else {
1014 mDrawableWidth = mDrawableHeight = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
1016 }
1017
1018 private void resizeFromDrawable() {
Alan Viverette6aa92d12015-08-25 13:19:25 -04001019 final Drawable d = mDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 if (d != null) {
1021 int w = d.getIntrinsicWidth();
1022 if (w < 0) w = mDrawableWidth;
1023 int h = d.getIntrinsicHeight();
1024 if (h < 0) h = mDrawableHeight;
1025 if (w != mDrawableWidth || h != mDrawableHeight) {
1026 mDrawableWidth = w;
1027 mDrawableHeight = h;
1028 requestLayout();
1029 }
1030 }
1031 }
1032
Fabrice Di Meglio3f5a90b2013-06-24 19:22:25 -07001033 @Override
1034 public void onRtlPropertiesChanged(int layoutDirection) {
1035 super.onRtlPropertiesChanged(layoutDirection);
1036
1037 if (mDrawable != null) {
1038 mDrawable.setLayoutDirection(layoutDirection);
1039 }
1040 }
1041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 private static final Matrix.ScaleToFit[] sS2FArray = {
1043 Matrix.ScaleToFit.FILL,
1044 Matrix.ScaleToFit.START,
1045 Matrix.ScaleToFit.CENTER,
1046 Matrix.ScaleToFit.END
1047 };
1048
1049 private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) {
1050 // ScaleToFit enum to their corresponding Matrix.ScaleToFit values
1051 return sS2FArray[st.nativeInt - 1];
Alan Viverette6aa92d12015-08-25 13:19:25 -04001052 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053
1054 @Override
1055 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1056 resolveUri();
1057 int w;
1058 int h;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001059
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 // Desired aspect ratio of the view's contents (not including padding)
1061 float desiredAspect = 0.0f;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001062
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 // We are allowed to change the view's width
1064 boolean resizeWidth = false;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 // We are allowed to change the view's height
1067 boolean resizeHeight = false;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001068
Adam Powell2a0e99d2011-08-04 10:55:03 -07001069 final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
1070 final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
1071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 if (mDrawable == null) {
1073 // If no drawable, its intrinsic size is 0.
1074 mDrawableWidth = -1;
1075 mDrawableHeight = -1;
1076 w = h = 0;
1077 } else {
1078 w = mDrawableWidth;
1079 h = mDrawableHeight;
1080 if (w <= 0) w = 1;
1081 if (h <= 0) h = 1;
Romain Guy9e648c42011-08-17 20:04:27 -07001082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 // We are supposed to adjust view bounds to match the aspect
1084 // ratio of our drawable. See if that is possible.
1085 if (mAdjustViewBounds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
1087 resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001088
Romain Guy9e648c42011-08-17 20:04:27 -07001089 desiredAspect = (float) w / (float) h;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
1091 }
Alan Viverette6aa92d12015-08-25 13:19:25 -04001092
1093 final int pleft = mPaddingLeft;
1094 final int pright = mPaddingRight;
1095 final int ptop = mPaddingTop;
1096 final int pbottom = mPaddingBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097
1098 int widthSize;
1099 int heightSize;
1100
1101 if (resizeWidth || resizeHeight) {
1102 /* If we get here, it means we want to resize to match the
1103 drawables aspect ratio, and we have the freedom to change at
Alan Viverette6aa92d12015-08-25 13:19:25 -04001104 least one dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 */
1106
1107 // Get the max possible width given our constraints
Romain Guy9e648c42011-08-17 20:04:27 -07001108 widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);
1109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 // Get the max possible height given our constraints
Romain Guy9e648c42011-08-17 20:04:27 -07001111 heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
1112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 if (desiredAspect != 0.0f) {
1114 // See what our actual aspect ratio is
Alan Viverette6aa92d12015-08-25 13:19:25 -04001115 final float actualAspect = (float)(widthSize - pleft - pright) /
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 (heightSize - ptop - pbottom);
Alan Viverette6aa92d12015-08-25 13:19:25 -04001117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
Alan Viverette6aa92d12015-08-25 13:19:25 -04001119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 boolean done = false;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 // Try adjusting width to be proportional to height
1123 if (resizeWidth) {
Romain Guy9e648c42011-08-17 20:04:27 -07001124 int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
1125 pleft + pright;
Adam Powelld5edc772012-09-26 15:21:39 -07001126
1127 // Allow the width to outgrow its original estimate if height is fixed.
Adam Powell06f9eb82016-08-24 17:09:01 -07001128 if (!resizeHeight && !sCompatAdjustViewBounds) {
Adam Powelld5edc772012-09-26 15:21:39 -07001129 widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
1130 }
1131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 if (newWidth <= widthSize) {
1133 widthSize = newWidth;
1134 done = true;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001135 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 }
Alan Viverette6aa92d12015-08-25 13:19:25 -04001137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 // Try adjusting height to be proportional to width
1139 if (!done && resizeHeight) {
Romain Guy9e648c42011-08-17 20:04:27 -07001140 int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
1141 ptop + pbottom;
Adam Powelld5edc772012-09-26 15:21:39 -07001142
1143 // Allow the height to outgrow its original estimate if width is fixed.
Adam Powell06f9eb82016-08-24 17:09:01 -07001144 if (!resizeWidth && !sCompatAdjustViewBounds) {
Adam Powelld5edc772012-09-26 15:21:39 -07001145 heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
1146 heightMeasureSpec);
1147 }
1148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 if (newHeight <= heightSize) {
1150 heightSize = newHeight;
Dianne Hackborn189ee182010-12-02 21:48:53 -08001151 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 }
1153 }
1154 }
1155 } else {
1156 /* We are either don't want to preserve the drawables aspect ratio,
1157 or we are not allowed to change view dimensions. Just measure in
1158 the normal way.
1159 */
1160 w += pleft + pright;
1161 h += ptop + pbottom;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 w = Math.max(w, getSuggestedMinimumWidth());
1164 h = Math.max(h, getSuggestedMinimumHeight());
1165
Dianne Hackborn189ee182010-12-02 21:48:53 -08001166 widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
1167 heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 }
1169
1170 setMeasuredDimension(widthSize, heightSize);
1171 }
1172
1173 private int resolveAdjustedSize(int desiredSize, int maxSize,
1174 int measureSpec) {
1175 int result = desiredSize;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001176 final int specMode = MeasureSpec.getMode(measureSpec);
1177 final int specSize = MeasureSpec.getSize(measureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 switch (specMode) {
1179 case MeasureSpec.UNSPECIFIED:
1180 /* Parent says we can be as big as we want. Just don't be larger
1181 than max size imposed on ourselves.
1182 */
1183 result = Math.min(desiredSize, maxSize);
1184 break;
1185 case MeasureSpec.AT_MOST:
Alan Viverette6aa92d12015-08-25 13:19:25 -04001186 // Parent says we can be as big as we want, up to specSize.
1187 // Don't be larger than specSize, and don't be larger than
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 // the max size imposed on ourselves.
1189 result = Math.min(Math.min(desiredSize, specSize), maxSize);
1190 break;
1191 case MeasureSpec.EXACTLY:
1192 // No choice. Do what we are told.
1193 result = specSize;
1194 break;
1195 }
1196 return result;
1197 }
1198
1199 @Override
1200 protected boolean setFrame(int l, int t, int r, int b) {
Alan Viverette6aa92d12015-08-25 13:19:25 -04001201 final boolean changed = super.setFrame(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 mHaveFrame = true;
1203 configureBounds();
1204 return changed;
1205 }
1206
1207 private void configureBounds() {
1208 if (mDrawable == null || !mHaveFrame) {
1209 return;
1210 }
1211
Alan Viverette6aa92d12015-08-25 13:19:25 -04001212 final int dwidth = mDrawableWidth;
1213 final int dheight = mDrawableHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214
Alan Viverette6aa92d12015-08-25 13:19:25 -04001215 final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
1216 final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217
Alan Viverette6aa92d12015-08-25 13:19:25 -04001218 final boolean fits = (dwidth < 0 || vwidth == dwidth)
1219 && (dheight < 0 || vheight == dheight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220
1221 if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
1222 /* If the drawable has no intrinsic size, or we're told to
1223 scaletofit, then we just fill our entire view.
1224 */
1225 mDrawable.setBounds(0, 0, vwidth, vheight);
1226 mDrawMatrix = null;
1227 } else {
1228 // We need to do the scaling ourself, so have the drawable
1229 // use its native size.
1230 mDrawable.setBounds(0, 0, dwidth, dheight);
1231
1232 if (ScaleType.MATRIX == mScaleType) {
1233 // Use the specified matrix as-is.
1234 if (mMatrix.isIdentity()) {
1235 mDrawMatrix = null;
1236 } else {
1237 mDrawMatrix = mMatrix;
1238 }
1239 } else if (fits) {
1240 // The bitmap fits exactly, no transform needed.
1241 mDrawMatrix = null;
1242 } else if (ScaleType.CENTER == mScaleType) {
1243 // Center bitmap in view, no scaling.
1244 mDrawMatrix = mMatrix;
Adam Powell14d1f1382015-06-04 12:56:00 -07001245 mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
1246 Math.round((vheight - dheight) * 0.5f));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 } else if (ScaleType.CENTER_CROP == mScaleType) {
1248 mDrawMatrix = mMatrix;
1249
1250 float scale;
1251 float dx = 0, dy = 0;
1252
1253 if (dwidth * vheight > vwidth * dheight) {
Alan Viverette6aa92d12015-08-25 13:19:25 -04001254 scale = (float) vheight / (float) dheight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 dx = (vwidth - dwidth * scale) * 0.5f;
1256 } else {
1257 scale = (float) vwidth / (float) dwidth;
1258 dy = (vheight - dheight * scale) * 0.5f;
1259 }
1260
1261 mDrawMatrix.setScale(scale, scale);
Adam Powell14d1f1382015-06-04 12:56:00 -07001262 mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 } else if (ScaleType.CENTER_INSIDE == mScaleType) {
1264 mDrawMatrix = mMatrix;
1265 float scale;
1266 float dx;
1267 float dy;
Alan Viverette6aa92d12015-08-25 13:19:25 -04001268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 if (dwidth <= vwidth && dheight <= vheight) {
1270 scale = 1.0f;
1271 } else {
Romain Guy9e648c42011-08-17 20:04:27 -07001272 scale = Math.min((float) vwidth / (float) dwidth,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 (float) vheight / (float) dheight);
1274 }
Alan Viverette6aa92d12015-08-25 13:19:25 -04001275
Adam Powell14d1f1382015-06-04 12:56:00 -07001276 dx = Math.round((vwidth - dwidth * scale) * 0.5f);
1277 dy = Math.round((vheight - dheight * scale) * 0.5f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278
1279 mDrawMatrix.setScale(scale, scale);
1280 mDrawMatrix.postTranslate(dx, dy);
1281 } else {
1282 // Generate the required transform.
1283 mTempSrc.set(0, 0, dwidth, dheight);
1284 mTempDst.set(0, 0, vwidth, vheight);
Alan Viverette6aa92d12015-08-25 13:19:25 -04001285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 mDrawMatrix = mMatrix;
Romain Guy9e648c42011-08-17 20:04:27 -07001287 mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 }
1289 }
1290 }
1291
1292 @Override
1293 protected void drawableStateChanged() {
1294 super.drawableStateChanged();
Alan Viverette6aa92d12015-08-25 13:19:25 -04001295
Alan Viverettead0020f2015-09-04 10:10:42 -04001296 final Drawable drawable = mDrawable;
1297 if (drawable != null && drawable.isStateful()
1298 && drawable.setState(getDrawableState())) {
1299 invalidateDrawable(drawable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 }
1301 }
1302
Alan Viverettecebc6ba2014-06-13 15:52:13 -07001303 @Override
Alan Viverette8de14942014-06-18 18:05:15 -07001304 public void drawableHotspotChanged(float x, float y) {
1305 super.drawableHotspotChanged(x, y);
Alan Viverettecebc6ba2014-06-13 15:52:13 -07001306
1307 if (mDrawable != null) {
1308 mDrawable.setHotspot(x, y);
1309 }
1310 }
1311
George Mount990205e2014-06-24 09:36:18 -07001312 /** @hide */
1313 public void animateTransform(Matrix matrix) {
George Mountf6c763d2014-09-25 15:13:15 -07001314 if (mDrawable == null) {
1315 return;
1316 }
George Mount990205e2014-06-24 09:36:18 -07001317 if (matrix == null) {
1318 mDrawable.setBounds(0, 0, getWidth(), getHeight());
1319 } else {
1320 mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
1321 if (mDrawMatrix == null) {
1322 mDrawMatrix = new Matrix();
1323 }
1324 mDrawMatrix.set(matrix);
1325 }
1326 invalidate();
1327 }
1328
Alan Viverettecebc6ba2014-06-13 15:52:13 -07001329 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 protected void onDraw(Canvas canvas) {
1331 super.onDraw(canvas);
1332
1333 if (mDrawable == null) {
1334 return; // couldn't resolve the URI
1335 }
1336
1337 if (mDrawableWidth == 0 || mDrawableHeight == 0) {
1338 return; // nothing to draw (empty bounds)
1339 }
1340
1341 if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
1342 mDrawable.draw(canvas);
1343 } else {
Alan Viverette6aa92d12015-08-25 13:19:25 -04001344 final int saveCount = canvas.getSaveCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 canvas.save();
Alan Viverette6aa92d12015-08-25 13:19:25 -04001346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 if (mCropToPadding) {
1348 final int scrollX = mScrollX;
1349 final int scrollY = mScrollY;
1350 canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
1351 scrollX + mRight - mLeft - mPaddingRight,
1352 scrollY + mBottom - mTop - mPaddingBottom);
1353 }
Alan Viverette6aa92d12015-08-25 13:19:25 -04001354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 canvas.translate(mPaddingLeft, mPaddingTop);
1356
1357 if (mDrawMatrix != null) {
1358 canvas.concat(mDrawMatrix);
1359 }
1360 mDrawable.draw(canvas);
1361 canvas.restoreToCount(saveCount);
1362 }
1363 }
1364
Joe Onoratofd52b182010-11-10 18:00:52 -08001365 /**
1366 * <p>Return the offset of the widget's text baseline from the widget's top
1367 * boundary. </p>
1368 *
1369 * @return the offset of the baseline within the widget's bounds or -1
1370 * if baseline alignment is not supported.
1371 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 @Override
Joe Onoratofd52b182010-11-10 18:00:52 -08001373 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 public int getBaseline() {
Joe Onoratofd52b182010-11-10 18:00:52 -08001375 if (mBaselineAlignBottom) {
1376 return getMeasuredHeight();
1377 } else {
1378 return mBaseline;
1379 }
1380 }
1381
1382 /**
1383 * <p>Set the offset of the widget's text baseline from the widget's top
Romain Guy9fc27812011-04-27 14:21:41 -07001384 * boundary. This value is overridden by the {@link #setBaselineAlignBottom(boolean)}
Joe Onoratofd52b182010-11-10 18:00:52 -08001385 * property.</p>
1386 *
1387 * @param baseline The baseline to use, or -1 if none is to be provided.
1388 *
Alan Viverette6aa92d12015-08-25 13:19:25 -04001389 * @see #setBaseline(int)
Joe Onoratofd52b182010-11-10 18:00:52 -08001390 * @attr ref android.R.styleable#ImageView_baseline
1391 */
1392 public void setBaseline(int baseline) {
1393 if (mBaseline != baseline) {
1394 mBaseline = baseline;
1395 requestLayout();
1396 }
1397 }
1398
1399 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -07001400 * Sets whether the baseline of this view to the bottom of the view.
Joe Onoratofd52b182010-11-10 18:00:52 -08001401 * Setting this value overrides any calls to setBaseline.
1402 *
Joe Fernandez704d43b2017-04-18 10:35:38 -07001403 * @param aligned If true, the image view will be baseline aligned by its bottom edge.
Joe Onoratofd52b182010-11-10 18:00:52 -08001404 *
1405 * @attr ref android.R.styleable#ImageView_baselineAlignBottom
1406 */
1407 public void setBaselineAlignBottom(boolean aligned) {
1408 if (mBaselineAlignBottom != aligned) {
1409 mBaselineAlignBottom = aligned;
1410 requestLayout();
1411 }
1412 }
1413
1414 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -07001415 * Checks whether this view's baseline is considered the bottom of the view.
Joe Onoratofd52b182010-11-10 18:00:52 -08001416 *
Joe Fernandez704d43b2017-04-18 10:35:38 -07001417 * @return True if the ImageView's baseline is considered the bottom of the view, false if otherwise.
Joe Onoratofd52b182010-11-10 18:00:52 -08001418 * @see #setBaselineAlignBottom(boolean)
1419 */
1420 public boolean getBaselineAlignBottom() {
1421 return mBaselineAlignBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 }
svetoslavganov75986cf2009-05-14 22:28:01 -07001423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 /**
Joe Fernandez704d43b2017-04-18 10:35:38 -07001425 * Sets a tinting option for the image.
Alan Viverette6aa92d12015-08-25 13:19:25 -04001426 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 * @param color Color tint to apply.
1428 * @param mode How to apply the color. The standard mode is
1429 * {@link PorterDuff.Mode#SRC_ATOP}
Alan Viverette6aa92d12015-08-25 13:19:25 -04001430 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 * @attr ref android.R.styleable#ImageView_tint
1432 */
1433 public final void setColorFilter(int color, PorterDuff.Mode mode) {
1434 setColorFilter(new PorterDuffColorFilter(color, mode));
1435 }
1436
Jeff Sharkey2b95c242010-02-08 17:40:30 -08001437 /**
1438 * Set a tinting option for the image. Assumes
1439 * {@link PorterDuff.Mode#SRC_ATOP} blending mode.
1440 *
1441 * @param color Color tint to apply.
1442 * @attr ref android.R.styleable#ImageView_tint
1443 */
1444 @RemotableViewMethod
1445 public final void setColorFilter(int color) {
1446 setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
1447 }
1448
Joe Fernandez704d43b2017-04-18 10:35:38 -07001449 /**
1450 * Removes the image's {@link android.graphics.ColorFilter}.
1451 *
1452 * @see #setColorFilter(int)
1453 * @see #getColorFilter()
1454 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 public final void clearColorFilter() {
1456 setColorFilter(null);
1457 }
Philip Milneaac722a2012-03-26 13:30:26 -07001458
1459 /**
Adam Powell31049d72013-10-07 12:58:42 -07001460 * @hide Candidate for future API inclusion
1461 */
1462 public final void setXfermode(Xfermode mode) {
1463 if (mXfermode != mode) {
1464 mXfermode = mode;
1465 mColorMod = true;
1466 applyColorMod();
1467 invalidate();
1468 }
1469 }
1470
1471 /**
Philip Milneaac722a2012-03-26 13:30:26 -07001472 * Returns the active color filter for this ImageView.
1473 *
1474 * @return the active color filter for this ImageView
1475 *
1476 * @see #setColorFilter(android.graphics.ColorFilter)
1477 */
1478 public ColorFilter getColorFilter() {
1479 return mColorFilter;
1480 }
1481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 /**
1483 * Apply an arbitrary colorfilter to the image.
1484 *
1485 * @param cf the colorfilter to apply (may be null)
Philip Milneaac722a2012-03-26 13:30:26 -07001486 *
1487 * @see #getColorFilter()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 */
1489 public void setColorFilter(ColorFilter cf) {
1490 if (mColorFilter != cf) {
1491 mColorFilter = cf;
Alan Viverette91174362014-06-17 14:51:45 -07001492 mHasColorFilter = true;
Jeff Sharkey2b95c242010-02-08 17:40:30 -08001493 mColorMod = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 applyColorMod();
1495 invalidate();
1496 }
1497 }
svetoslavganov75986cf2009-05-14 22:28:01 -07001498
Philip Milneaac722a2012-03-26 13:30:26 -07001499 /**
1500 * Returns the alpha that will be applied to the drawable of this ImageView.
1501 *
Kevin Hufnaglebdb308a2016-09-23 15:54:42 -07001502 * @return the alpha value that will be applied to the drawable of this
1503 * ImageView (between 0 and 255 inclusive, with 0 being transparent and
1504 * 255 being opaque)
Philip Milneaac722a2012-03-26 13:30:26 -07001505 *
1506 * @see #setImageAlpha(int)
1507 */
1508 public int getImageAlpha() {
1509 return mAlpha;
1510 }
1511
1512 /**
1513 * Sets the alpha value that should be applied to the image.
1514 *
Kevin Hufnaglebdb308a2016-09-23 15:54:42 -07001515 * @param alpha the alpha value that should be applied to the image (between
1516 * 0 and 255 inclusive, with 0 being transparent and 255 being opaque)
Philip Milneaac722a2012-03-26 13:30:26 -07001517 *
1518 * @see #getImageAlpha()
1519 */
1520 @RemotableViewMethod
1521 public void setImageAlpha(int alpha) {
1522 setAlpha(alpha);
1523 }
1524
1525 /**
1526 * Sets the alpha value that should be applied to the image.
1527 *
1528 * @param alpha the alpha value that should be applied to the image
1529 *
1530 * @deprecated use #setImageAlpha(int) instead
1531 */
1532 @Deprecated
Jeff Sharkey2b95c242010-02-08 17:40:30 -08001533 @RemotableViewMethod
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 public void setAlpha(int alpha) {
1535 alpha &= 0xFF; // keep it legal
1536 if (mAlpha != alpha) {
1537 mAlpha = alpha;
Jeff Sharkey2b95c242010-02-08 17:40:30 -08001538 mColorMod = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 applyColorMod();
1540 invalidate();
1541 }
1542 }
1543
1544 private void applyColorMod() {
Jeff Sharkey2b95c242010-02-08 17:40:30 -08001545 // Only mutate and apply when modifications have occurred. This should
1546 // not reset the mColorMod flag, since these filters need to be
1547 // re-applied if the Drawable is changed.
1548 if (mDrawable != null && mColorMod) {
1549 mDrawable = mDrawable.mutate();
Alan Viverette91174362014-06-17 14:51:45 -07001550 if (mHasColorFilter) {
1551 mDrawable.setColorFilter(mColorFilter);
1552 }
Adam Powell31049d72013-10-07 12:58:42 -07001553 mDrawable.setXfermode(mXfermode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
1555 }
1556 }
Adam Powell37419d72011-11-10 11:32:09 -08001557
Alan Viverettee10dd532013-12-12 19:19:51 -08001558 @Override
1559 public boolean isOpaque() {
1560 return super.isOpaque() || mDrawable != null && mXfermode == null
1561 && mDrawable.getOpacity() == PixelFormat.OPAQUE
1562 && mAlpha * mViewAlphaScale >> 8 == 255
1563 && isFilledByImage();
1564 }
1565
1566 private boolean isFilledByImage() {
1567 if (mDrawable == null) {
1568 return false;
1569 }
1570
1571 final Rect bounds = mDrawable.getBounds();
1572 final Matrix matrix = mDrawMatrix;
1573 if (matrix == null) {
1574 return bounds.left <= 0 && bounds.top <= 0 && bounds.right >= getWidth()
1575 && bounds.bottom >= getHeight();
1576 } else if (matrix.rectStaysRect()) {
1577 final RectF boundsSrc = mTempSrc;
1578 final RectF boundsDst = mTempDst;
1579 boundsSrc.set(bounds);
1580 matrix.mapRect(boundsDst, boundsSrc);
1581 return boundsDst.left <= 0 && boundsDst.top <= 0 && boundsDst.right >= getWidth()
1582 && boundsDst.bottom >= getHeight();
1583 } else {
1584 // If the matrix doesn't map to a rectangle, assume the worst.
1585 return false;
1586 }
1587 }
1588
Adam Powell37419d72011-11-10 11:32:09 -08001589 @Override
Adam Powell9c146bf2016-03-15 17:35:00 -07001590 public void onVisibilityAggregated(boolean isVisible) {
1591 super.onVisibilityAggregated(isVisible);
Adam Powell06f9eb82016-08-24 17:09:01 -07001592 // Only do this for new apps post-Nougat
1593 if (mDrawable != null && !sCompatDrawableVisibilityDispatch) {
Adam Powell9c146bf2016-03-15 17:35:00 -07001594 mDrawable.setVisible(isVisible, false);
Adam Powell37419d72011-11-10 11:32:09 -08001595 }
1596 }
1597
Adam Powell06f9eb82016-08-24 17:09:01 -07001598 @RemotableViewMethod
1599 @Override
1600 public void setVisibility(int visibility) {
1601 super.setVisibility(visibility);
1602 // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
1603 if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
1604 mDrawable.setVisible(visibility == VISIBLE, false);
1605 }
1606 }
1607
1608 @Override
1609 protected void onAttachedToWindow() {
1610 super.onAttachedToWindow();
1611 // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
1612 if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
1613 mDrawable.setVisible(getVisibility() == VISIBLE, false);
1614 }
1615 }
1616
1617 @Override
1618 protected void onDetachedFromWindow() {
1619 super.onDetachedFromWindow();
1620 // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated
1621 if (mDrawable != null && sCompatDrawableVisibilityDispatch) {
1622 mDrawable.setVisible(false, false);
1623 }
1624 }
1625
Adam Powell37419d72011-11-10 11:32:09 -08001626 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001627 public CharSequence getAccessibilityClassName() {
1628 return ImageView.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001629 }
Siva Velusamy94a6d152015-05-05 15:07:00 -07001630
1631 /** @hide */
1632 @Override
1633 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
1634 super.encodeProperties(stream);
1635 stream.addProperty("layout:baseline", getBaseline());
1636 }
Jiaquan He1dd48d02017-05-01 14:18:39 -07001637
1638 /** @hide */
1639 @Override
1640 @TestApi
1641 public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) {
1642 final boolean lackFocusState = mDrawable == null || !mDrawable.isStateful()
1643 || !mDrawable.hasFocusStateSpecified();
1644 return super.isDefaultFocusHighlightNeeded(background, foreground) && lackFocusState;
1645 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646}