这篇文章主要介绍了适配Android 10(Q)后,调用系统拍照,系统相册,系统裁剪和上传问题,这是一个很常用的功能,但是在Android 6.0,Android 7.0和Android 10.0以上版本的实现都有所不同,这篇文章从Android 4适配到Android 10。 之前写毕设的时候,在写上传头像的功能时,参考网上的方法写了一大堆,在我的手机(Android 9)上可以正常运行,当时没多想,以为高版本可以向下兼容,后来我把程序发给同学去试验,结果都告诉我上传头像用不了,一问才知道他们用的是Android 10的手机,于是只能上网查找原因,然后发现Android 10的存储方式发生了变化,Android 10的文件系统采用了沙盒文件系统,最显著的变化就是文件系统变安全了,于是app也没办法拿到外部文件的绝对路径了,网上给出的方法就是将共享文件复制到沙盒目录下,然后再进行文件操作。话不多说,上代码。 在文件清单AndroidManifest.xml中添加权限: 1 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 储存卡的读权限 -->2 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 储存卡的写权限 -->3 <uses-permission android:name="android.permission.CAMERA" /><!-- 调用相机权限 --> 在官方7.0的以上的系统中,尝试传递 file://URI可能会触发FileUriExposedException,使用FileProvider来共享文件,AndroidManifest.xml: <application ... <!-- 兼容Android7.0拍照闪退 --> <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.camera.test" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> 在主界面放一个ImageView和两个按钮,activity_main.xml: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas./apk/res/android" xmlns:tools="http://schemas./tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <ImageView android:id="@+id/image" android:layout_width="250dp" android:layout_height="250dp" android:layout_marginTop="20dp" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/tv_camera" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:text="相机" android:textSize="18sp" android:textColor="#FFF" android:padding="10dp" android:background="#1878FF" android:layout_marginHorizontal="20dp" android:gravity="center_horizontal"/> <TextView android:id="@+id/tv_album" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:text="相册" android:textSize="18sp" android:textColor="#FFF" android:padding="10dp" android:background="#1878FF" android:layout_marginHorizontal="20dp" android:gravity="center_horizontal"/></LinearLayout> 接下来是主页面的代码: 获取控件,对两个按钮添加点击监听,判断权限: private ImageView image; private TextView tvCamera, tvAlbum; @Override 权限申请回调: @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case PHOTO_REQUEST_CAMERA: //相机权限请求回调 if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED) { //跳转相机 openCamera(); } else { //无权限提示 Toast.makeText(context, "权限未通过", Toast.LENGTH_SHORT).show(); } } break; case PHOTO_REQUEST_ALBUM: if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { //跳转相册 openAlbum(); } else { //无权限提示 Toast.makeText(context, "权限未通过", Toast.LENGTH_SHORT).show(); } } break; } } 跳转相机: = (activity != && context != && intent.resolveActivity(activity.getPackageManager()) != = uri = file = (file != (Build.VERSION.SDK_INT >= uri = FileProvider.getUriForFile(context, "com.example.camera.test"=="相机保存的图片Uri:" + (uri != Android 10以上的创建Uri,Uri创建在沙盒内: contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/0/"); 以上设置的保存路径为:".../包名/files/Pictures/0",可按需更改 用于保存拍照之后的照片: private Uri createImageUri(@NonNull Context context){ String status = Environment.getExternalStorageState(); ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, SAVE_AVATAR_NAME); contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*"); contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/0/"); //判断是否有SD卡 if (status.equals(Environment.MEDIA_MOUNTED)){ return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); } else { return context.getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, contentValues); } } Android 10以下的返回一个file来保存拍照后的图片: private File createImageFile(@NonNull Context context){ File file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (file != null && !file.exists()){ if (file.mkdir()){ Log.e(TAG, "文件夹创建成功"); } else { Log.e(TAG, "file为空或者文件夹创建失败"); } } File tempFile = new File(file, SAVE_AVATAR_NAME); Log.e(TAG, "临时文件路径:" + tempFile.getAbsolutePath()); if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))){ return null; } return tempFile; } 跳转相册: private void openAlbum(){ Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, ALBUM_REQUEST_CODE); } 跳转裁剪,裁剪在相机拍照后跳转,用一个file来加载: private void openCrop(Uri uri){ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) && context != null){ file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES + "/0"), SAVE_AVATAR_NAME); } Intent intent = new Intent("com.android.camera.action.CROP"); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setDataAndType(uri, "image/*"); // 设置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // 裁剪后输出图片的尺寸大小 intent.putExtra("outputX", 250); intent.putExtra("outputY", 250); //适配Android10,存放图片路径 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); // 图片格式 intent.putExtra("outputFormat", "PNG"); intent.putExtra("noFaceDetection", true);// 取消人脸识别 intent.putExtra("return-data", true);// true:不返回uri,false:返回uri startActivityForResult(intent, TAILOR_REQUEST_CODE); } 跳转相机、相册和裁剪的回调,如果有上传需求的,直接上传代码中的file即可: @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == -1){ //回调成功 switch (requestCode) { case CAMERA_REQUEST_CODE: //相机回调 Log.e(TAG, "相机回调"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { //照片裁剪 openCrop(imageUri); } else { Toast.makeText(context, "未找到存储卡", Toast.LENGTH_SHORT).show(); } break; case ALBUM_REQUEST_CODE: //相册回调 Log.e(TAG, "相册回调"); if (data != null && data.getData() != null) { image.setImageURI(data.getData()); //如果需要上传操作的可以使用这个方法 File file = FileUtils.uriToFile(data.getData(), context); //这里的file就是需要上传的图片了 } break; case TAILOR_REQUEST_CODE: //图片剪裁回调 Log.e(TAG, "图片剪裁回调");// Glide.with(context).load(file).into(image); Uri uri = Uri.fromFile(file); image.setImageURI(uri); //如果需要上传全局的这个file就是需要上传的图片了 File file = this.file; break; } } else { Toast.makeText(context, "取消", Toast.LENGTH_SHORT).show(); } } |
|