2021年3月1日星期一

适配Android10 拍照,相册,裁剪,上传图片

本文主要介绍了适配Android10调用系统相册,系统相机,系统裁剪和对应上传操作

  这篇文章主要介绍了适配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也没办法拿到外部文件的绝对路径了,网上给出的方法就是将共享文件复制到沙盒目录下,然后再进行文件操作。话不多说,上代码。

  demo源码

在文件清单AndroidManifest.

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.

<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="@ />  </provider> </application>

在主界面放一个ImageView和两个按钮,activity_main.

<??><LinearLayout ="http://schemas.android.com/apk/res/android" ="http://schemas.android.com/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
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init(){ image = findViewById(R.id.image); tvCamera = findViewById(R.id.tv_camera); tvAlbum = findViewById(R.id.tv_album); tvCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //相机 if ((ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) { //权限都齐的情况下,跳转相机 openCamera(); } else { if (activity != null) { //请求权限 ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, PHOTO_REQUEST_CAMERA); } } } }); tvAlbum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //相册 if ((ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) { //权限都齐的情况下,跳转相册 openAlbum(); } else { if (activity != null) { //请求权限 ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, PHOTO_REQUEST_ALBUM); } } } }); }

权限申请回调:

 @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;  } }

跳转相机:

 private void openCamera(){  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  //判断是否有相机  if (activity != null && context != null && intent.resolveActivity(activity.getPackageManager()) != null){   File file;   Uri uri = null;   if (isAndroidQ){    //适配Android10    uri = createImageUri(context);   } else {    //Android10以下    file = createImageFile(context);    if (file != null){     //Android10以下     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){      //适配Android7.0文件权限      uri = FileProvider.getUriForFile(context, "com.example.camera.test", file);     } else {      uri = Uri.fromFile(file);     }    }   }   imageUri = uri;   Log.e(TAG, "相机保存的图片Uri:" + imageUri);   if (uri != null){    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);    startActivityForResult(intent, CAMERA_REQUEST_CODE);   }  } }

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();  } }

以上,如果有改进的建议的,欢迎骚扰

QQ:1336140321









原文转载:http://www.shaoqun.com/a/596607.html

跨境电商:https://www.ikjzd.com/

mile:https://www.ikjzd.com/w/1746

聚贸:https://www.ikjzd.com/w/1305


本文主要介绍了适配Android10调用系统相册,系统相机,系统裁剪和对应上传操作  这篇文章主要介绍了适配Android10(Q)后,调用系统拍照,系统相册,系统裁剪和上传问题,这是一个很常用的功能,但是在Android6.0,Android7.0和Android10.0以上版本的实现都有所不同,这篇文章从Android4适配到Android10。  之前写毕设的时候,在写上传头像的功能时,参考
一淘网:https://www.ikjzd.com/w/1698
zozotown:https://www.ikjzd.com/w/2180
tenso:https://www.ikjzd.com/w/1552
实战宝典!亚马逊卖家如何巧用广告进行选品、打造爆品?:https://www.ikjzd.com/home/21700
口述:无耻老公逼我观摩他和小姐床事老公小姐爸爸:http://lady.shaoqun.com/m/a/43153.html
PingPong发升级通知,外汇局答疑跨境外汇支付问题!:https://www.ikjzd.com/home/101199

没有评论:

发表评论