-------------------------------------------------------------------------------------------

作者:Alien

----------------------------------------------------------------------------------------

在部分Android手机(如MT788、Note2)上,使用Camera拍照以后,得到的照片会被自动旋转(90°、180°、270°),这个情况很不符合预期。仔细分析了一下,因为照片属性中是存储了旋转信息的,所以要解决这个问题,可以在onActivityResult方法中,获取到照片数据后,读取它的旋转信息,如果不是0,说明这个照片已经被旋转过了,那么再使用android.graphics.Matrix将照片旋转回去即可。

1、读取图片的旋转属性

/**
 * 读取图片的旋转的角度
 *
 * @param path
 *            图片绝对路径
 * @return 图片的旋转角度
 */
private int getBitmapDegree(String path) {
    int degree = 0;
    try {
        // 从指定路径下读取图片,并获取其EXIF信息
        ExifInterface exifInterface = new ExifInterface(path);
        // 获取图片的旋转信息
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);//NORMAL 为0,即不旋转
        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90://右旋90度
            degree = 90;
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            degree = 180;
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            degree = 270;
            break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return degree;
}


2、将图片按照某个角度进行旋转


/**
 * 将图片按照某个角度进行旋转
 *
 * @param bm
 *            需要旋转的图片
 * @param degree
 *            旋转角度
 * @return 旋转后的图片
 */
public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
    Bitmap returnBm = null;
  
    // 根据旋转角度,生成旋转矩阵
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    try {
        // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
        returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
    } catch (OutOfMemoryError e) {
    }
    if (returnBm == null) {
        returnBm = bm;
    }
    if (bm != returnBm) {
        bm.recycle();
    }
    return returnBm;
}


3、将旋转后返回的bitmap以文件形式进行存储

/**
     * 保存bitmap到sd卡filePath文件中 如果有,则删除
     * @param bitmap
     * @param filePath :图片绝对路径
     * @return boolean :是否成功
     */
    public static boolean saveBitmap2file(Bitmap bitmap,String filePath){
        if (bitmap==null){
            return false;
        }
        //压缩格式
        CompressFormat format = CompressFormat.JPEG;
        int quality =100;
        OutputStream stream=null;
        File file=new File(filePath);
        File dir=file.getParentFile();
        if (!dir.exists()){
            dir.mkdirs();//创建父目录
        }
        if (file.exists()){
            file.delete();
        }

        try {
            stream = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return  bitmap.compress(format,quality,stream);
    }
}



4、剪切

首先通过图片文件的路径获取图片的uri,然后开始截图图片

Uri uri=Uri.fromFile(new File(PhotoUtils.getPhotoPath(pic_name)));
cropImageUri(uri,250,250, CameraActvity.ActivityResult_CROP_SMALL_PICTURE);


图片截取方法 cropInageUri()

/**
     * 图片截取
     * @param uri :图片文件的Uri
     * @param outputX   :裁剪图片的宽
     * @param outputY   :裁剪图片的高
     * @param requestCode   :int
     */
    private void cropImageUri(Uri uri, int outputX, int outputY, int requestCode) {
        Intent intent=new Intent("com.android.camera.action.CROP");//调用系统裁剪功能
        intent.setDataAndType(uri,"image/*");
        //在开启的Intent中设置显示的VIEW可裁剪
        intent.putExtra("crop","true");
        // aspectX aspectY 是宽高的比例,这里设置的是正方形(长宽比为1:1)
        intent.putExtra("aspectX",1);
        intent.putExtra("aspectY",1);
        // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX",outputX);
        intent.putExtra("outputY",outputY);
        //裁剪时是否保留图片的比例,这里的比例是1:1
        intent.putExtra("scale",true);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,PhotoUtils.getPhotoPath(pic_name));
        //是否将数据保留在Bitmap中返回
        intent.putExtra("return-data",true);
        //设置输出的格式
        intent.putExtra("outputFormat",Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection",true);
        intent.putExtra("scaleUpIfNeeded", "true");
        startActivityForResult(intent,requestCode);
    }

接收处理:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            /**
             * 裁剪返回
             */
            case CameraActvity.ActivityResult_CROP_SMALL_PICTURE:
                if (resultCode==RESULT_OK){
                    Bitmap bitmap= (Bitmap) data.getExtras().get("data");
                    if (PhotoUtils.saveBitmap2file(bitmap,PhotoUtils.getPhotoPath(pic_name))){//bitmap存储成file
                        setResultBack();//数据返回并关闭页面
                    }
                }
                break;
            default :
                break;
        }
    }


------------------------------------问题-------------------------------------

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

源码解析:

Returns an immutable bitmap from subset of the source bitmap, transformed by the optional matrix. The new bitmap may be the same object as source, or a copy may have been made. It is  initialized with the same density as the original bitmap.  If the source bitmap is immutable and the requested subset is the same as the source bitmap itself, then the source bitmap is returned and no new bitmap is created.

利用源位图的子集,通过矩阵变换,生成不可变的位图bitmap 。新的位图可能与源位图是相同的对象,或可能是源位图的一个副本。它的初始化密度与原始位图一致。如果源位图是不可变的,并且请求的子集是与源位图一致,那么则返回源位图,而不会创建新的位图。【也即图片的裁剪】

  • source :源位图,用来剪裁的图片源;
  • x :在源位图中第一个像素点的x坐标/剪裁x方向的起始位置
  • y :在源位图中第一个像素点的y坐标
  • width :每一行像素点的数量/ 剪裁的宽度
  • height :列数目/裁剪的高度
  • Matrix m :可选矩阵
  • filter :true 原图片也会被裁剪,false原图片不会。true if the source should be filtered. Only applies if the matrix contains more than just translation. 

需要注意的是:必须满足条件:x+width<=bitmap.width()(图片源的原始宽度)否则会抛出IllegalArgumentException异常。