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