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