Android之图片选择与裁剪

对于现在的应用来说,基本都有用户模块,当然也就涉及到了用户头像的修改问题。修改头像我们一般采用两种方式:调用系统相机进行拍照或从图库中选择图片进行上传。但是由于Rom定制市场的混乱,导致了出现此问题在解决时需要重点考虑了兼容性,下面给出一种通用解决方案。

启动相机进行拍照

需要注意的是,Android系统为了防止传送原图出现OOM,拍照后默认返回的是缩略图,为了解决这种问题,我们在启动相机的时候要先设置照片的存储路径,即传递MediaStore.EXTRA_OUTPUT参数,这样拍照完成后我们就可以根据之前设置的路径读取原图了。

1
2
3
4
5
6
// 设置图片的输出路径
File file = new File(Environment.getExternalStorageDirectory() + "/temp_icon.jpg");
iconUri = Uri.fromFile(file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, iconUri);
startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);

打开图库选择图片

选择图片操作其实没什么难度,选择完成后默认返回的就是图片的存储路径。

1
2
3
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, REQUEST_CODE_CHOOSE_IMAGE);

裁剪图片

和拍照的情况一样,Android为了防止传送大图片出现OOM,在截图的过程中默认传递的也是缩略图,这样裁减后的图片会非常模糊。所以采用URi的方式传递来原图的路径。需要注意的是,如果传递原图,那么拍照时需要设置照片的输出路径,也就是在调用相机时传递MediaStore.EXTRA_OUTPUT参数,同时为了防止某些手机默认传递原图的问题(如小米4),在传递大图片时将return-data置为false;对于某些手机或平板,输入Uri和输出Uri不能相等,否则裁减后的图片大小变为0kb,如平板Teclast P80h.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 裁减图片操作
* @param uri
*/
private void startCropImage(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 使图片处于可裁剪状态
intent.putExtra("crop", "true");
// 裁剪框的比例(根据需要显示的图片比例进行设置)
intent.putExtra("aspectX", 3);
intent.putExtra("aspectY", 2);
// 让裁剪框支持缩放
intent.putExtra("scale", true);
// 裁剪后图片的大小(注意和上面的裁剪比例保持一致)
intent.putExtra("outputX", AndroidPlatformUtil.dpToPx(this, 120));
intent.putExtra("outputY", AndroidPlatformUtil.dpToPx(this, 80));
// 传递原图路径
File cropFile = new File(Environment.getExternalStorageDirectory() + "crop_image.jpg");
cropImageUri = Uri.fromFile(cropFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
// 设置裁剪区域的形状,默认为矩形,也可设置为原形
//intent.putExtra("circleCrop", true);
// 设置图片的输出格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
// return-data=true传递的为缩略图,小米手机默认传递大图,所以会导致onActivityResult调用失败
intent.putExtra("return-data", false);
// 是否需要人脸识别
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE);
}

获取传递的图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if(intent != null) {
switch (requestCode) {
// 将拍摄的照片进行裁剪(注意,这里需要传递的是照片的路径,而不是intent.getData(), 因为intent.getData()返回的是缩略图的数据)
case REQUEST_CODE_TAKE_PHOTO:
startCropImage(iconUri);
break;
// 将选择的图片进行裁剪
case REQUEST_CODE_CHOOSE_IMAGE:
if (intent.getData() != null) {
iconUri = intent.getData();
startCropImage(iconUri);
}
break;
// 将裁剪后的图片进行上传
case REQUEST_CODE_CROP_IMAGE:
// 上传图片操作
break;
default:
break;

}
} else {
// 解决某些手机intent为空的情况
if (requestCode == REQUEST_CODE_TAKE_PHOTO && iconUri != null) {
startCropImage(iconUri);
}
}
}

总结

通过上面的四步,图片的选择与裁剪就完成了。总的来说,需要注意的只有一点,无论是拍照还是选选择图片到裁剪的过程,只有传递图片的路径才能从根本上实现此问题。否则将出现各种兼容性问题。还有一点需要提醒的是:裁剪是真正决定所要展示图片的比例和大小的。

,