ImageView之ScaleType详解及拓展

4,551 阅读4分钟

ImageView中有个很重要也很常用的属性android:scaleType,相信大家都应该不陌生,主要用于控制图片在ImageView中显示的样式,比如显示大小、显示位置、显示内容区域。当然也可以在代码中设置: setScaleType(ImageView.ScaleType.xxx); ScaleType的取值一共有8种:fitCenterfitEndcentercenterCropcenterInsidematrixfitXYfitStart。其中默认值是fitCenter。 我们先来看比较简单的center吧,一个60dp*40dp的ImageView(为了显示效果,背景设成蓝色)里面放了张20dp*20dp的图片。上图看看效果:

center的含义是:保持原图的大小,显示在ImageView的中心。当原图的size大于ImageView的size,超过部分裁剪处理。 再来看比较“暴力”的fitXY

fitXY的含义:拉伸显示图片,不保持原比例,填满ImageView.嗯,果然很暴力。

嗯,接下来我们...什么?难道你以为我要把剩下的6种样式都贴张图出来,然后愉快地水一篇博客吗?

所以接下来我放大招了,开始分析源码!

详解

上面设置了这么多种ScaleType,那么最终生效的位置在哪呢?答案就在 configureBounds()方法里,限于篇幅我贴了上面两种样式的法,其他的样式实现也都类似。

   private void configureBounds() {
        //图片宽高
        final int dwidth = mDrawableWidth;
        final int dheight = mDrawableHeight;
         //Imageview宽高
        final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;//
        final int vheight = getHeight() - mPaddingTop - mPaddingBottom;

        final boolean fits = (dwidth < 0 || vwidth == dwidth)
                && (dheight < 0 || vheight == dheight);

        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
         
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            mDrawable.setBounds(0, 0, dwidth, dheight);
             if (ScaleType.MATRIX == mScaleType) {//样式应用自定义MATRIX
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            } 
         if (ScaleType.CENTER == mScaleType) {
                // Center bitmap in view, no scaling.
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
                                         Math.round((vheight - dheight) * 0.5f));
            } 

可以看到最终的实现用到了Drawable类和Matrix类的方法。前者大家都很熟悉,就是我们设置的要显示的图片,而Matrix叫做矩阵,是一个专门用来处理图形变换中的重要工具类,熟悉自定义View的同学应该对它不陌生,canvas.setMatrix(matrix)可以做出很多独特效果来。而我们今天的主角是ImageView,所以没错imageView也有这个方法:

imageView.setImageMatrix(matrix)

通过上述方法我们就能将自己定义的Matrix应用到ImageView中。可以看到源码,要使这个方法生效,ScaleType一定要设为MATRIX,不然执行完自己的变换后还是会执行样式自己的变换,相当于没执行。

FIT_XY的实现很简单,就用到了setBounds(int left, int top, int right, int bottom),这个四参数指的是drawable的绘制区域。 center用到了postTranslate(float dx, float dy),将Drawable移到ImageView中间,因为没有其他处理,图片如果比ImageView大,那么多余的区域就会被裁剪掉,不显示了。

拓展

说了这么多来个小栗子来加深理解吧: 还是上面那张图片,scaleType设为matrix,我们再加一个按钮,点击事件如下:

    int m = 20;
    //平移
    private void onClick(View v) {
        Matrix matrix = new Matrix();
        matrix.postTranslate(m, m);
        imageView.setImageMatrix(matrix);
        m = m + 3;
    }

来看看效果:

通过改变postTranslate参数的值我们就能改变图片在View中绘制的起始位置。 当然不会这么简单。Matrix还有其他方法: postRotate(float degrees, float px, float py)//旋转: 我们仿照center的处理将图片移到ImageView中心,然后进行旋转:

    int m = 20;
    //旋转
    private void onClick(View v) {
       Matrix matrix = new Matrix();
        int width = imageView.getDrawable().getIntrinsicWidth();
        int height = imageView.getDrawable().getIntrinsicHeight();
        final int vwidth = imageView.getWidth();
        final int vheight = imageView.getHeight();
        matrix.setTranslate(Math.round((vwidth - width) * 0.5f),
                Math.round((vheight - height) * 0.5f));
        matrix.postRotate(m, vwidth * 0.5f, vheight * 0.5f);
        imageView.setImageMatrix(matrix);
        m = m + 3;
    }

看看效果:

还有一种是错切,将图片的形状改变: void setSkew(float kx, float ky) 实际用到的比较少,就不贴代码了,直接看效果吧:

总结

说了这么多,这篇文章就是给大家介绍了android:scaleType的自定义用法,当遇到Imageview预设的显示模式不能满足我们现有需求的时候(比如我就遇到原图按照原来的大小居底部显示,如果原图的大小超过了ImageView的大小,那么就剪裁掉多余部分,保留底部,和centerCrop裁剪四周有所不同),我们就可以动态设置自己的matrix来自定义显示效果。当然如果你经常要用到这种方式的时候,可以继承Imageview,封装这些自定义样式。