Android -- 调用系统相册之图片裁剪保存
前言
最近线上反馈,部分vivo手机更换头像时调用系统相册保存图片失败,经本人测试,确实有问题。
经修复后,贴出这块的代码供小伙伴们参考使用。
功能
更换头像选择图片:
- 调用系统相机拍照,调用系统图片裁剪并保存。
- 调用系统相册选择照片,调用系统图片裁剪并保存。
此功能需要动态申请 相机和读写外部存储的权限,此处省略了,请自行动态申请添加。
String[] permissions=new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
1、布局文件activity_picture.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/takePictureFromCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照" />
<Button
android:id="@+id/takePictureFromLib"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="从相册选取" />
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"/>
</LinearLayout>
2、PictureActivity:
public class PictureActivity extends AppCompatActivity {
public class Const {
public static final int PHOTO_GRAPH = 1;// 拍照
public static final int PHOTO_ZOOM = 2; // 相册
public static final int PHOTO_RESOULT = 3;// 结果
public static final String IMAGE_UNSPECIFIED = "image/*";
}
public String authority;
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture);
authority = getApplicationContext().getPackageName() + ".fileprovider";
imageView = findViewById(R.id.img);
findViewById(R.id.takePictureFromCamera).setOnClickListener(
v -> openCamera(Const.PHOTO_GRAPH));
findViewById(R.id.takePictureFromLib).setOnClickListener(
v -> openCamera(Const.PHOTO_ZOOM));
}
private void openCamera(int type) {
Intent intent;
if (type == Const.PHOTO_GRAPH) {
//打开相机
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//指定调用相机拍照后照片的储存路径
File photoFile = new File(CoreConstants.getNurseDownloadFile(this), "temp.jpg");
if (!photoFile.exists()) {
photoFile.getParentFile().mkdirs();
}
Uri uri = FileUtil.getUri(this, authority, photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
} else {
//打开相册
intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Const.IMAGE_UNSPECIFIED);
}
startActivityForResult(intent, type);
}
/**
* 调用系统的裁剪图片
*/
private void crop(Uri uri) {
try {
Intent intent = new Intent("com.android.camera.action.CROP");
String contentURl = CoreConstants.getNurseDownloadFile(this)
+ File.separator + "temp.jpg";
File cropFile = new File(contentURl);
Uri cropUri;
//在7.0以上跨文件传输uri时候,需要用FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
cropUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", cropFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
cropUri = Uri.fromFile(cropFile);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);
intent.setDataAndType(uri, Const.IMAGE_UNSPECIFIED);
intent.putExtra("crop", "true");
// 裁剪框的比例,1:1
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// 裁剪后输出图片的尺寸大小
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());// 图片格式
intent.putExtra("noFaceDetection", true);// 取消人脸识别
intent.putExtra("return-data", true);//是否返回裁剪后图片的Bitmap
intent.putExtra("output", cropUri);
//重要!!!添加权限,不然裁剪完后报 “保存时发生错误,保存失败”
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, cropUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
ComponentName componentName = intent.resolveActivity(getPackageManager());
if (componentName != null) {
// 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT
startActivityForResult(intent, Const.PHOTO_RESOULT);
}
} catch (Exception e) {
String s = e.getMessage().toString();
}
}
public Bitmap convertUriToBitmap(Uri uri) {
ContentResolver contentResolver = getContentResolver();
try {
// 将Uri转换为字节数组
return BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 拍照
if (requestCode == Const.PHOTO_GRAPH) {
// 设置文件保存路径
File picture = new File(CoreConstants.getNurseDownloadFile(this)
+ File.separator + "temp.jpg");
Uri uri = FileUtil.getUri(this, authority, picture);
crop(uri);
}
if (data == null)
return;
//读取相册图片
if (requestCode == Const.PHOTO_ZOOM) {
crop(data.getData());
}
//处理裁剪后的结果
if (requestCode == Const.PHOTO_RESOULT) {
Bundle extras = data.getExtras();
Bitmap photo = null;
if(extras != null) {
photo = extras.getParcelable("data");
}
if (photo == null && data.getData() != null) {
//部分小米手机extras是个null,所以想拿到Bitmap要转下
photo = convertUriToBitmap(data.getData());
}
if (photo != null) {
//拿到Bitmap后直接显示在Image控件上
imageView.setImageBitmap(photo);
//将图片上传到服务器
// String fileName = CommonCacheUtil.getUserId();
// final File file = FileUtil.saveImgFile(this, photo, fileName);
// final String fileKey = UUID.randomUUID().toString().replaceAll("-", "");
//将file通过post上传到服务器
//TODO:后续自行发挥
}
}
}
}
3、FileUtil 工具类:
public class FileUtil {
public static Uri getUri(Context context, String authority, File file) {
Uri uri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, authority, file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
public static File saveImgFile(Context context, Bitmap bitmap, String fileName) {
if (fileName == null) {
System.out.println("saved fileName can not be null");
return null;
} else {
fileName = fileName + ".png";
String path = context.getFilesDir().getAbsolutePath();
String lastFilePath = path + "/" + fileName;
File file = new File(lastFilePath);
if (file.exists()) {
file.delete();
}
try {
FileOutputStream outputStream = context.openFileOutput(fileName, 0);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException var7) {
var7.printStackTrace();
} catch (IOException var8) {
var8.printStackTrace();
}
return file;
}
}
}
4、工具类 CoreConstants:
public class CoreConstants {
public static String getNurseDownloadFile(Context context) {
return context.getExternalFilesDir("").getAbsolutePath() + "/img";
}
}
5、在 AndroidManifest.xml 中配置 FileProvider:
<application
....
>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
6、filepaths.xml 文件:
<paths>
<external-path path="notePadRecorder/" name="notePadRecorder" />
<external-path name="my_images" path="Pictures"/>
<external-path name="external_files" path="."/>
<root-path name="root_path" path="." />
</paths>
这部分代码在小米和vivo手机上测过,是正常的。
目前线上也没有反馈在其他机型上该功能有问题,如有问题,后续持续更新此文章。