这是一篇比较简单的处理图片的应用文,我们先看一下具体的场景:



这里实现了上传图片的功能,图片限制在红色框框内且比例为4/3,标准的照片比例。但是如果用户上传了其他比例的图片,为了照顾观感肯定是要对图片进行一个等比压缩处理,最后的效果和ImageView中ScareType的FitCenter效果是一样的,也就是保持图片的比例,并且最大化的显示在时框中。

那么对于自定义View如何实现这一效果呢?首先分析一下图片是如何被处理的。

三种情况:

  • 比例小于视框
    也就是图片比较“高”,例如上图左边,是一张16比9的照片,9/16 < 3/4。
    显示高度 = 视框高度
  • 比例等于视框
    也就是4比3的图片,可以完美的贴合视框不留空白
    显示宽高 = 视框宽高
  • 比例大于视框
    也就是图片比较“宽”,例如上图右边,比例为1比1, 1 > 3/4

对于图片的处理就比较简单了,计算图片和视框的比例,按照比例进行缩放即可。

那么知道图片处理的判断情况以及处理方式后,接下来就是具体代码实现了:

//取得图片和图片所处空间的比例
float scaleBitmap = ((float) bitmap.getWidth()) / bitmap.getHeight();
float scaleView = ((float)getWidth()) / getHeight();

// 空间的大小 / bitmap 的大小 = bitmap 缩放的倍数
float scaleWidth = ((float) getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) getHeight()) / bitmap.getHeight();

Matrix matrix = new Matrix();
//按照比例选择缩放参照
if (scaleBitmap < scaleView) {
    //比例小于视框时,全部按照图片高度和视框高度的比例进行缩放
    matrix.postScale(scaleHeight, scaleHeight);
}else {
    //比例大于视框时,全部按照图片宽度和视框宽度的比例进行缩放
    matrix.postScale(scaleWidth, scaleWidth);
}


//bitmap 缩放
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
//draw 上去
//canvas.drawBitmap(bitmap, 0, 0, paint);

//居中显示
canvas.drawBitmap(bitmap,
        (getWidth() - bitmap.getWidth()) / 2,
        (getHeight() - bitmap.getHeight()) / 2, paint);
canvas.restore();
复制代码

这里要注意一个问题就是,Java中进行除法运算并获得带小数点的结果,需要将算式中的一个值转为float才可以取得。

在最后将bitmap画入画布时,也要记得计算一下位置,居中显示才算完整实现效果。

然后简单介绍一下Android中缩放图像生成缩略图的三种方式:

1、BitmapFactory和 BitmapFactory.Options

在设定好缩放值inSampleSize 后,通过BitmapFactory.decodeFile或者decode其他形式,生成缩放后的Bitmap位图。如果已经有Bitmap图了,可以转成File地址来实现。

而缩放值inSampleSize 可以直接设定具体倍数,比如2就是2分之一倍,或者通过计算原图宽高和设定的想达到的宽高得到比例。

其中很实用的一点是,在获取图片的宽高的时候,可以将inJustDecodeBounds设成true,此时bitmap不会加载到内存,而只是获取到图片的height和width,节省内存提高效率。

示例:

//使用BitmapFactory.Options的inSampleSize参数来缩放  
            public static Drawable resizeImage2(String path,  
                        int width,int height)   
                {  
        BitmapFactory.Options options = new BitmapFactory.Options();  
        options.inJustDecodeBounds = true;//不加载bitmap到内存中  
        BitmapFactory.decodeFile(path,options);   
        int outWidth = options.outWidth;  
        int outHeight = options.outHeight;  
        options.inDither = false;  
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
        options.inSampleSize = 1;  
          
        if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0)   
        {  
            int sampleSize=(outWidth/width+outHeight/height)/2;  
            Log.d(tag, "sampleSize = " + sampleSize);  
            options.inSampleSize = sampleSize;  
        }  
      
        options.inJustDecodeBounds = false;  
        return new BitmapDrawable(BitmapFactory.decodeFile(path, options));       
    }  
复制代码
2、使用Bitmap加Matrix来缩放

首先要获得原bitmap,创建一个Matrix对象,包含想要达到的宽和高,最后在原Bitmap的基础上生成新图片。效率比较低。

示例:

//使用Bitmap加Matrix来缩放  
    public static Drawable resizeImage(Bitmap bitmap, int w, int h)   
                {    
        Bitmap BitmapOrg = bitmap;    
        int width = BitmapOrg.getWidth();    
        int height = BitmapOrg.getHeight();    
        int newWidth = w;    
        int newHeight = h;    
  
        float scaleWidth = ((float) newWidth) / width;    
        float scaleHeight = ((float) newHeight) / height;    
  
        Matrix matrix = new Matrix();    
        matrix.postScale(scaleWidth, scaleHeight);    
        // if you want to rotate the Bitmap     
        // matrix.postRotate(45);     
        Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 0, 0, width,    
                                        height, matrix, true);    
        return new BitmapDrawable(resizedBitmap);    
    } 
复制代码
3、Android自带的ThumbnailUtils

Android自带的处理方法,会结合第一种和第二种方法和一些其他算法对图片进行加工,效率会比第二种高一点,使用也比较方便,一行代码就可以了:

imView.setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap,200,100));
复制代码
关于图片压缩

最后在三种图片的处理方式中都发现了一个问题,也是以前我对图片处理的一个知识盲区:图片不仅在拉大的时候会模糊,在缩小的时候同样也可能模糊。一张照片,压缩10倍之后,其分辨率已经不足以看清细节,锯齿非常严重。所以这三种图片的压缩,是会降低显示效果的,如果需要尺寸减小画质不变,应该需要其他的算法来解决。