源码分析ImageView中ScaleType设置fitCenter和centerInside的区别

Posted 碎格子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析ImageView中ScaleType设置fitCenter和centerInside的区别相关的知识,希望对你有一定的参考价值。

在设置ImageView的scaleType时,有时常常搞不清fitCenter和centerInside的区别,网上资料说的也很模糊,今天就从底层源码看看这俩类型的区别。

查看ImageView的代码,发现有这样的一个ScaleType数组,里面存放了我们所有的可设置的type枚举。

private static final ScaleType[] sScaleTypeArray = 
        ScaleType.MATRIX,
        ScaleType.FIT_XY,
        ScaleType.FIT_START,
        ScaleType.FIT_CENTER,
        ScaleType.FIT_END,
        ScaleType.CENTER,
        ScaleType.CENTER_CROP,
        ScaleType.CENTER_INSIDE
    ;

centerInside

搜索ScaleType.CENTER_INSIDE,找到使用枚举处的方法。为了便于理解,我将其他类型的代码都注释掉了,只留下我们要分析的代码。

ImageView.java

private void configureBounds() 
        if (mDrawable == null || !mHaveFrame) 
            return;
        

        final int dwidth = mDrawableWidth;  //图片的宽度
        final int dheight = mDrawableHeight;  //图片的高度

        final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;  //view的宽度
        final int vheight = getHeight() - mPaddingTop - mPaddingBottom; //view的高度

        ...

        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) 
           ...
         else 
            // We need to do the scaling ourself, so have the drawable
            // use its native size.
            mDrawable.setBounds(0, 0, dwidth, dheight);

            if (ScaleType.MATRIX == mScaleType) 
               ...
             else if (fits) 
                ...
             else if (ScaleType.CENTER == mScaleType) 
               ...
             else if (ScaleType.CENTER_CROP == mScaleType) 
                ...
             else if (ScaleType.CENTER_INSIDE == mScaleType) 
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;

                if (dwidth <= vwidth && dheight <= vheight)  //如果图片本身宽高均小于view的宽高,则无需进行缩放
                    scale = 1.0f;
                 else  //在宽度比和高度比中取较小的值作为缩放比例
                    scale = Math.min((float) vwidth / (float) dwidth, 
                            (float) vheight / (float) dheight);
                

                dx = Math.round((vwidth - dwidth * scale) * 0.5f);  //x轴平移距离
                dy = Math.round((vheight - dheight * scale) * 0.5f);  //y轴平移距离

                mDrawMatrix.setScale(scale, scale);  //设置缩放
                mDrawMatrix.postTranslate(dx, dy);  //平移
             else 
                ...
            
        
    

fitCenter

针对FIT有关的类型,ImageView里定义了一个专门存放Fit类型的数组:

ImageView.java

private static final Matrix.ScaleToFit[] sS2FArray = 
        Matrix.ScaleToFit.FILL,
        Matrix.ScaleToFit.START,
        Matrix.ScaleToFit.CENTER,
        Matrix.ScaleToFit.END
    ;

其实从定义可以看出,实现fitCenter的方式是用矩阵方式存改变量(平移量,缩放量等)

之前configureBounds()方法省略的代码里,else处理的就是fit相关的几个类型,所以fitCenter是走的else逻辑

ImageView.java

else if (ScaleType.CENTER_INSIDE == mScaleType) 
                ...
             else 
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);

                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            

查看setRectToRect方法

Matrix.java

@FastNative
    private static native boolean nSetRectToRect(long nObject,
                                                 RectF src, RectF dst, int stf);

public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) 
    if (dst == null || src == null) 
        throw new NullPointerException();
    
    return nSetRectToRect(native_instance, src, dst, stf.nativeInt);

nSetRectToRect定义的是native方法,实际上是通过Matrix_Delegate类实现这个方法的,找到实际使用CENTER的地方:

Matrix_Delegate.java

@LayoutlibDelegate    
/*package*/ static boolean nSetRectToRect(long native_object, RectF src,
            RectF dst, int stf)  //src是源图片的RectF,dst是view的RectF
        Matrix_Delegate d = sManager.getDelegate(native_object);
        ...
        if (dst.isEmpty()) 
            ...
         else 
            float    tx, sx = dst.width() / src.width(); //view宽度和原图片宽度比
            float    ty, sy = dst.height() / src.height(); //view宽度和原图片高度比
            boolean  xLarger = false;  //宽度比值是否更大

            if (stf != ScaleToFit.FILL.nativeInt) 
                if (sx > sy)  //宽度比大于高度比,按更小的值设置缩放比
                    xLarger = true;
                    sx = sy;
                 else 
                    sy = sx;
                
            

            tx = dst.left - src.left * sx; //源图片缩放后left位置差值
            ty = dst.top - src.top * sy;
            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) 
                float diff;

                if (xLarger)  //宽度比更大(x比更大)
                    diff = dst.width() - src.width() * sy;  //按更小的缩放比缩放后求出宽度差
                 else 
                    diff = dst.height() - src.height() * sy; //按更小的缩放比缩放后求出高度差
                

                if (stf == ScaleToFit.CENTER.nativeInt) 
                    diff = diff / 2;
                

                if (xLarger) 
                    tx += diff;  //平移居中
                 else 
                    ty += diff;
                
            

            d.mValues[0] = sx; //x轴缩放
            d.mValues[4] = sy; //y轴缩放
            d.mValues[2] = tx; //x轴平移
            d.mValues[5] = ty; //y轴平移
            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0; //其他不动

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

上面代码看出不管源图片的宽高多少,设置fitCenter一定会以View的宽或高为基准对图片进行缩放处理。

区别

由设置图片1的例子可以看出,其实区别在于dwidth <= vwidth && dheight <= vheight这种情况下,centerInside 是不会对图片进行缩放,会保持图片原来的大小进行居中展示,而fitCenter一定会按宽度比或者高度比中更小的比例进行缩放,使图片的宽或高等于view的宽或高。

以上是关于源码分析ImageView中ScaleType设置fitCenter和centerInside的区别的主要内容,如果未能解决你的问题,请参考以下文章

ImageView的源码解读,以及几种ScaleType的分析

ImageView 的ScaleType属性使用详解

Android 源码系列之<一>从源码的角度深入理解ImageView的ScaleType属性

ImageView的Scaletype

android中imageview里的图片大小是不是可以比imageview本身大,如果可以,如何设置?

Android开发教程--设置ImageView图片的显示比例