ImageView (一) ——从源码的角度分析ScaleType (缩放模式)

作者: 林子木

博客地址:http://blog.csdn.net/wolinxuebin 


文章目的

    最近在看源码,发现网上有一些对ImageView的缩放模式有介绍的地方,但是对个别缩放模式说明还是存在一些问题,其次,就是缺乏一个总体的对比,也就是从他们的例子中无法分辨某几个模式的区别,尤其是几个Center模式。所以在看源码的时候,顺便写下这篇文章很大家分享。

文章主要内容

    第一章直接进行一个各个模式比较的表格。第二章将具体将每个例子进行编写demo并且截图处理。第三章将从源码的角度分析每种模式的原理。

第一章 各个模式总结表

表1-1 ImageView 各个ScaleType说明表           

缩放模式

裁剪  

按比例 

放大  

缩小  

描述

MATIRX

未知

未知

未知

未知

通过设定setImageMatrix函数相应的矩阵,来完成。

FIT_XY

直接将图片铺满整个view

CENTER

1)          如果图片较小,直接居中全部显示

2)          如果图片较大,裁剪后居中显示

CENTER_CROP

按比例缩放,按照缩放比例大的进行缩放,然后居中显示(除非图片和view的形状一样,

否则一定存在裁剪,所以有的描述为图片的长宽≥View的长宽)

CENTER_INSIDE

1) 如果图片较小,直接放入view,不进行放大处理,居中显示

2) 将图片按比例进行缩放,使得图片完全展示,并且长或者宽等于view的长和宽

(所以有的描述为图片的长宽≤View的长宽)

FIT_START

对图片的处理和FIT_CENTER,区别为:

1)          view的横向有空,居左边

2)          view的纵向有空,居上边

FIT_CENTER(默认)

将图片按比例进行缩放,使得图片完全展示,并且长或者宽等于view的长和宽

(所以有的描述为图片的长宽≤View的长宽),最后居中显示

FIT_END

对图片的处理和FIT_CENTER,区别为:

1)  view的横向有空,居右边

2)  view的纵向有空,居下边


通过以上的表格大家应该能对各个模式有一个大致的了解,但是大家肯定有一个疑惑,就是对CENTER、CENTER_CROP、CENTER_INSIDE以及FIT_CENTER有点分布清的感觉,那么让我们再来阅读下面一个表格,来更好的认清。

表1-2 各个Center缩放模式的比较      

缩放模式

图片比View小

dH <= vH && dW <= vW

图片比View大

dH > vH || dW > vW

是否裁剪

CENTER

直接居中显示

直接截取

图片比View大的时候存在裁剪

CENTER_CROP

按比例缩放,最后

dH >= vH 或 dw >= vW

同左

始终存在(除非图片和view的形状一样)

CENTER_INSIDE

直接居中显示

按比例缩放,最后

dH <= vH 或 dw <= vW

不裁剪

FIT_CENTER

始终按比例缩放,最后

dH <= vH 或 dw <= vW

同左

不裁剪

注:1)dH dw 代表图片的高和宽,vH vW代表View的高和宽

2)ImageView的模式缩放模式是Fit_Center             


第二章、各个缩放模式的示例

原图如下:


图 2.1 示例原图(中间的十字叉)

2.1 MATIRX

这个需要设置相应的矩阵,所以什么情况都有,所以先不做相应的示例。

2.2 FIT_XY

 直接将图片不按比例铺满View,所以就可能图片就扭曲了,如下图:


2.2 FIT_XY 模式示例图

2.3 CENTER模式

该模式,在图片较小的情况下,直接居中显示(如图2.3 左);如果图片比view大,那么直接裁剪后居中显示(如图2.3 右),如下图:


图2.3 Center模式, 左边为图片比View小, 右边为图片比View大

2.4 CENTER_CROP

该模式一律将图片放大,并且使得图片的宽高≥View的宽高,所以如果图片和View的形状不一样(也就是宽和高的缩放比例不一致),将存在图片的裁剪,如下图:


图2.4 CENTER_CROP模式,

取View宽/图宽,View高/图高大的比例对宽高进行缩放,出现了截断的效果。

2.5 CENTER_INSIDE

如果图片小的时候,不进行缩放,直接居中显示(如图2.5 左);如果图片较大,那么将进行按比例进行缩小处理,最后图片的宽高≤View的宽高(如图2.5 右)。


图2.5 Center_Inside模式图,左图为图片较View小,右图为图片较View大(横向)

2.6 FIT_CENTER

始终进行缩放,最后使得图片的宽高≤View的宽高,如图2.6所示。


图 2.6 FIT_CENTER 模式, 左图为按纵向缩放,右图为按横向进行缩放

2.7 FIT_START

缩放模式和FIT_CENTER一样,唯一的却别是图片居于View的位置。原则是,缩放有,有空白的方向的左边(如图2.7 左)或上边(如图2.7 右),因为按照其中一个方向进行缩放,那么该方向上图片将和View一致,另一个方向上留有空白。注,这里是LTR模式,如果是RTL将左边和右边相反。


图 2.7 FIT_START模式,左边为按照纵向缩放,右边为按照横向缩放。

2.8 FIT_END

该模式和FIT_START一样,唯一的区别是,为居右(如图 2.8 左)和 居下(如图 2.8 右)。


图 2.8 FIT_END 模式,左边为按照纵向缩放,右边为按照横向缩放。

第三章 从源码角度看各个缩放模式

该源码是在ImageView的configureBounds()函数中体现,具体如下:

首先获取相应图片和View的宽高

	int dwidth = mDrawableWidth;
        int dheight = mDrawableHeight;

        //[lxb #] 获取当前view的宽高
        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        int vheight = getHeight() - mPaddingTop - mPaddingBottom;

接着进行fits变量标记,这个fits为true则使用FIT_XY模式:

	//[lxb #] dwidth < 0 表示,继承了Drawable的view没有重写getTrinsicWidth则返回-1,表示view没有原始大小的说明。
        boolean fits = (dwidth < 0 || vwidth == dwidth) &&
                       (dheight < 0 || vheight == dheight);

2.1 FIT_XY

	if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            /* If the drawable has no intrinsic size, or we're told to
                scaletofit, then we just fill our entire view.
            */
            //[lxb #] 采用当前view的宽高
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } 

2.2 MATRIX 

	    //[lxb #] MATRIX模式,是采用设置矩阵的方式进行变换
            if (ScaleType.MATRIX == mScaleType) {
                // Use the specified matrix as-is.
                //[lxb #] 是不是单位矩阵,单位矩阵的话,就不需要进行变换,就是原始图
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            }

2.3 CENTER

	   else if (ScaleType.CENTER == mScaleType) {
                // Center bitmap in view, no scaling.
                //[lxb #] CENTER 模式
                //[lxb #] mMatrix 如果没有调用setImageMatrix函数,mMatrix初始化为一个单位矩阵
                //[lxb #] CENTER 就是将drawable进行平移,将drawble的中心点和view的中心点对齐
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
                                         Math.round((vheight - dheight) * 0.5f));
            }

2.4 CENTER_CROP

	   else if (ScaleType.CENTER_CROP == mScaleType) {
                mDrawMatrix = mMatrix;

                float scale;
                float dx = 0, dy = 0;

                //[lxb #] 化简如下 vh/dh > vw/dw,也就是要将drawable放入到view中,按照需要进行大缩放的为准
                //[lxb #] 说人话就是,将drawable按比例缩放到view的大小,使得长宽都大于等于view的长宽(其中一个相等),并居中显示
                //[lxb #] 所以如果drawable和view的长宽比不一样的话,就会被切割
                if (dwidth * vheight > vwidth * dheight) {
                    scale = (float) vheight / (float) dheight; 
                    dx = (vwidth - dwidth * scale) * 0.5f;
                } else {
                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * 0.5f;
                }

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
            }

2.5 CENTER_INSIDE

	   else if (ScaleType.CENTER_INSIDE == mScaleType) {
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;
                
                //[lxb #] 简单的理解,如果drawble能放到view中,那么直接塞进去,否则按比例缩小放进去
                //[lxb #] 和fitCenter的区别是,如果当前的图片比view小,就直接放进行,不缩放
                //[lxb #] 而fitCentter则是进行缩放,使得宽或者长等于view的宽或者长
                if (dwidth <= vwidth && dheight <= vheight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) vwidth / (float) dwidth,
                            (float) vheight / (float) dheight);
                }
                
                //[lxb #] 按比例缩小并居中对齐
                dx = Math.round((vwidth - dwidth * scale) * 0.5f);
                dy = Math.round((vheight - dheight * scale) * 0.5f);

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(dx, dy);
            } 

2.7 FIT_START, FIT_CENTER, FIT_END

先执行如下代码:

	   else {
                // Generate the required transform.
                //[lxb #] FIT_START(2) FIT_END(4) FIT_CENTER(3)
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);
                
                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }

最后走Matrix_Delegate.java 的 native_setRectToRect函数,如下:

@LayoutlibDelegate
    /*package*/ static boolean native_setRectToRect(long native_object, RectF src,
            RectF dst, int stf) {
        Matrix_Delegate d = sManager.getDelegate(native_object);
        if (d == null) {
            return false;
        }

        if (src.isEmpty()) {
            reset(d.mValues);
            return false;
        }

        if (dst.isEmpty()) {
            d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
               = d.mValues[6] = d.mValues[7] = 0;
            d.mValues[8] = 1;
        } else {
            //[lxb #] sx, sy 表示x轴, y轴的缩放
            //[lxb #] tx, ty 表示x轴,y轴的平移
            float    tx, sx = dst.width() / src.width();
            float    ty, sy = dst.height() / src.height();
            boolean  xLarger = false;

            if (stf != ScaleToFit.FILL.nativeInt) {
                //[lxb #] 取小的比率,经过下面的处理,选择了较小的放大率,sx == sy,简单的理解就是,
                //[lxb #] 使得图片按照比例放大缩小,使得长宽小于等于view的长宽
                if (sx > sy) {
                    //[lxb #] 标记X为比较大的
                    xLarger = true;
                    sx = sy;
                } else {
                    sy = sx;
                }
            }

            //[lxb #] 以原点为基准点,如果按比例缩放,那么每一段距离都将按比例缩放
            //[lxb #]      Top
            //[lxb #] Left  --------------------------
            //[lxb #]      |     | src.top 
            //[lxb #]      |-----.(0,0)
            //[lxb #]      |  src.left
            //[lxb #]      |
            //[lxb #]      |
            //[lxb #]      |
            //[lxb #]      |
            //[lxb #]      |
            tx = dst.left - src.left * sx;
            ty = dst.top - src.top * sy;
            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
                float diff;
                //[lxb #] diff 代表需要偏移的值,由上面可知,如果是CENTER,END,START sx == sy
                //[lxb #] diff 最终代表的是,drawable/view的长和宽分别的比值大的一方的偏移量(为啥是大的?因为小的一方已经完全贴合了)
                //[lxb #] 总结,最简单的理解是,有空隙的方向,没有铺满的方向的偏移量
                if (xLarger) {
                    diff = dst.width() - src.width() * sy;
                } else {
                    diff = dst.height() - src.height() * sy;
                }

                //[lxb #] 居中显示
                if (stf == ScaleToFit.CENTER.nativeInt) {
                    diff = diff / 2;
                }

                //[lxb #] 如果你有疑问,为什么将大的进行偏移
                if (xLarger) {
                    tx += diff;
                } else {
                    ty += diff;
                }
            }

            d.mValues[0] = sx;
            d.mValues[4] = sy;
            d.mValues[2] = tx;
            d.mValues[5] = ty;
            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;

        }
        // shared cleanup
        d.mValues[8] = 1;
        return true;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值