這篇文章主要介紹了適配Android 10(Q)后,調(diào)用系統(tǒng)拍照,系統(tǒng)相冊(cè),系統(tǒng)裁剪和上傳問(wèn)題,這是一個(gè)很常用的功能,但是在Android 6.0,Android 7.0和Android 10.0以上版本的實(shí)現(xiàn)都有所不同,這篇文章從Android 4適配到Android 10。 之前寫畢設(shè)的時(shí)候,在寫上傳頭像的功能時(shí),參考網(wǎng)上的方法寫了一大堆,在我的手機(jī)(Android 9)上可以正常運(yùn)行,當(dāng)時(shí)沒(méi)多想,以為高版本可以向下兼容,后來(lái)我把程序發(fā)給同學(xué)去試驗(yàn),結(jié)果都告訴我上傳頭像用不了,一問(wèn)才知道他們用的是Android 10的手機(jī),于是只能上網(wǎng)查找原因,然后發(fā)現(xiàn)Android 10的存儲(chǔ)方式發(fā)生了變化,Android 10的文件系統(tǒng)采用了沙盒文件系統(tǒng),最顯著的變化就是文件系統(tǒng)變安全了,于是app也沒(méi)辦法拿到外部文件的絕對(duì)路徑了,網(wǎng)上給出的方法就是將共享文件復(fù)制到沙盒目錄下,然后再進(jìn)行文件操作。話不多說(shuō),上代碼。 在文件清單AndroidManifest.xml中添加權(quán)限: 1 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 儲(chǔ)存卡的讀權(quán)限 -->2 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 儲(chǔ)存卡的寫權(quán)限 -->3 <uses-permission android:name="android.permission.CAMERA" /><!-- 調(diào)用相機(jī)權(quán)限 --> 在官方7.0的以上的系統(tǒng)中,嘗試傳遞 file://URI可能會(huì)觸發(fā)FileUriExposedException,使用FileProvider來(lái)共享文件,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> 在主界面放一個(gè)ImageView和兩個(gè)按鈕,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="相機(jī)" 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="相冊(cè)" android:textSize="18sp" android:textColor="#FFF" android:padding="10dp" android:background="#1878FF" android:layout_marginHorizontal="20dp" android:gravity="center_horizontal"/></LinearLayout> 接下來(lái)是主頁(yè)面的代碼: 獲取控件,對(duì)兩個(gè)按鈕添加點(diǎn)擊監(jiān)聽(tīng),判斷權(quán)限: private ImageView image; private TextView tvCamera, tvAlbum; @Override 權(quán)限申請(qǐng)回調(diào): @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case PHOTO_REQUEST_CAMERA: //相機(jī)權(quán)限請(qǐng)求回調(diào) if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED) { //跳轉(zhuǎn)相機(jī) openCamera(); } else { //無(wú)權(quán)限提示 Toast.makeText(context, "權(quán)限未通過(guò)", Toast.LENGTH_SHORT).show(); } } break; case PHOTO_REQUEST_ALBUM: if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { //跳轉(zhuǎn)相冊(cè) openAlbum(); } else { //無(wú)權(quán)限提示 Toast.makeText(context, "權(quán)限未通過(guò)", Toast.LENGTH_SHORT).show(); } } break; } } 跳轉(zhuǎn)相機(jī): = (activity != && context != && intent.resolveActivity(activity.getPackageManager()) != = uri = file = (file != (Build.VERSION.SDK_INT >= uri = FileProvider.getUriForFile(context, "com.example.camera.test"=="相機(jī)保存的圖片Uri:" + (uri != Android 10以上的創(chuàng)建Uri,Uri創(chuàng)建在沙盒內(nèi): contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/0/"); 以上設(shè)置的保存路徑為:".../包名/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以下的返回一個(gè)file來(lái)保存拍照后的圖片: private File createImageFile(@NonNull Context context){ File file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (file != null && !file.exists()){ if (file.mkdir()){ Log.e(TAG, "文件夾創(chuàng)建成功"); } else { Log.e(TAG, "file為空或者文件夾創(chuàng)建失敗"); } } File tempFile = new File(file, SAVE_AVATAR_NAME); Log.e(TAG, "臨時(shí)文件路徑:" + tempFile.getAbsolutePath()); if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))){ return null; } return tempFile; } 跳轉(zhuǎn)相冊(cè): 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); } 跳轉(zhuǎn)裁剪,裁剪在相機(jī)拍照后跳轉(zhuǎn),用一個(gè)file來(lái)加載: 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/*"); // 設(shè)置裁剪 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);// 取消人臉識(shí)別 intent.putExtra("return-data", true);// true:不返回uri,false:返回uri startActivityForResult(intent, TAILOR_REQUEST_CODE); } 跳轉(zhuǎn)相機(jī)、相冊(cè)和裁剪的回調(diào),如果有上傳需求的,直接上傳代碼中的file即可: @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == -1){ //回調(diào)成功 switch (requestCode) { case CAMERA_REQUEST_CODE: //相機(jī)回調(diào) Log.e(TAG, "相機(jī)回調(diào)"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { //照片裁剪 openCrop(imageUri); } else { Toast.makeText(context, "未找到存儲(chǔ)卡", Toast.LENGTH_SHORT).show(); } break; case ALBUM_REQUEST_CODE: //相冊(cè)回調(diào) Log.e(TAG, "相冊(cè)回調(diào)"); if (data != null && data.getData() != null) { image.setImageURI(data.getData()); //如果需要上傳操作的可以使用這個(gè)方法 File file = FileUtils.uriToFile(data.getData(), context); //這里的file就是需要上傳的圖片了 } break; case TAILOR_REQUEST_CODE: //圖片剪裁回調(diào) Log.e(TAG, "圖片剪裁回調(diào)");// Glide.with(context).load(file).into(image); Uri uri = Uri.fromFile(file); image.setImageURI(uri); //如果需要上傳全局的這個(gè)file就是需要上傳的圖片了 File file = this.file; break; } } else { Toast.makeText(context, "取消", Toast.LENGTH_SHORT).show(); } } |
|