- 自Android 6.0以后對某些涉及用戶隱私權限的獲取需要動態(tài)獲取,所以首先是檢查權限,如沒有權限則動態(tài)申請權限,這里我們需要用到的權限是WRITE_EXTERNAL_STORAGE和CAMERA卿闹。
- 自Android 7.0后系統(tǒng)禁止應用向外部公開file://URI ,因此需要FileProvider來向外界傳遞URI萝快。
- 獲取到拍照后的照片锻霎,按照現在的手機拍照文件大小來說不做處理直接展示很容易發(fā)生OOM,可以通過采樣率對圖片進行縮小杠巡,或者壓縮的操作量窘。
接下來我們看如何實現調用相機拍照并返回圖片和從相冊中選擇相
片。
一氢拥、調用相機拍照并返回圖片
1.動態(tài)申請權限
首先在Mainfest.xml文件中聲明權限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA"/>
在代碼中動態(tài)申請
private void requestPermission() {
int permission = ActivityCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
//動態(tài)申請權限
ActivityCompat.requestPermissions(
this,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
//拍照的權限
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA}, REQUEST_EXTERNAL_STORAGE);
}
}
我們可以重寫onRequestPermissionsResult的方法對是否申請權限進行處理蚌铜。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
this.finish();
}
}
}
2.FileProvider
FileProvider是是ContentProvider的一個子類,用于應用程序之間私有文件的傳遞嫩海。自Android 7.0后系統(tǒng)禁止應用向外部公開file://URI 冬殃,因此需要FileProvider來向外界傳遞URI,傳遞的形式是content : //Uri叁怪,使用時需要在清單文件中注冊审葬。
<provider
android:authorities="com.hx.yolov5.provider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false"><!-- 一定要加這句-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepath"/>
</provider>
上面代碼中的是我們在res目錄下新建的一個xml文件夾,在文件夾下創(chuàng)建一個名filepath的xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--files-path 相當于 getFilesDir()-->
<files-path name="my_images" path="images"/>
<!--cache-path 相當于 getCacheDir()-->
<!--external-path 相當于 Environment.getExternalStorageDirectory()-->
<!--external-files-path 相當于 getExternalFilesDir("") -->
<!--external-cache-path 相當于 getExternalCacheDir() -->
</paths>
3. 獲取URI
通過FileProvider.getUriForFile來獲取奕谭,其中com.hx.yolov5.provider就是在注冊文件的provider中的android:authorities的值
/**
* 獲取uri
*/
public Uri getMediaFileUri(Context context) {
String cameraPath = getFilesDir() + File.separator + "images" + File.separator;
mediaFile = new File(cameraPath, "picture" + System.currentTimeMillis() + ".jpg");
if (!mediaFile.exists()) {
mediaFile.getParentFile().mkdirs();
}
//sdk>=24 android7以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(context, "com.hx.yolov5.provider", mediaFile);
} else {
imageUri = Uri.fromFile(mediaFile);
}
return imageUri;
}
4.調起相機
/**
* 打開相機
*/
private void IntentCamera() {
Intent openCameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
imageUri = getMediaFileUri(MainActivity.this);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
//將存儲圖片的uri讀寫權限授權給相機應用
//Android7.0添加臨時權限標記涣觉,此步千萬別忘了
openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
openCameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(openCameraIntent, CAMERA_RESULT);
}
5.顯示圖片
重寫onActivityResult的方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_PICK_IMAGE:
imageBitmap = getBitmapFromUri(getRealUri(data.getData()), 400, 400);
break;
case CAMERA_RESULT:
imageBitmap = getBitmapFromUri(mediaFile.getAbsolutePath(), 400, 400);
break;
default:
break;
}
if (imageBitmap != null) {
resultImageView.setImageBitmap(imageBitmap);
}
}
獲取圖片并且通過采樣率進行壓縮
/**
* 通過采樣率來加載圖片
*/
public Bitmap getBitmapFromUri(String uri, int width, int height) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = calculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(uri, options);
return bitmap;
}
/**
* 計算合適的采樣率
*
* @param options
* @param width
* @param height
* @return
*/
private int calculateInSampleSize(BitmapFactory.Options options, int width, int height) {
final int originHeight = options.outHeight;
final int originWidth = options.outWidth;
int inSampleSize = 1;
if (originHeight > height || originWidth > width) {
final int halfHeight = originHeight / 2;
final int halfWidth = originWidth / 2;
while ((halfHeight / inSampleSize) >= height && (halfWidth / inSampleSize) >= width) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
之前參考了網上的方法通過 InputStream input = context.getContentResolver().openInputStream(uri);這個進行獲取的圖片的文件流獲取失敗(解決方法還沒找到血柳,如果知道解決方法的歡迎留言)官册,后來發(fā)現可以直接通過 Bitmap bitmap = BitmapFactory.decodeFile(uri, options);獲得Bitmap對象之后做相應的處理。
對圖片進行壓縮
/**
* 對圖片進行質量的壓縮
* 改方法刪除难捌,因為壓縮后的圖片過于模糊
*
* @param bitmap
* @return
*/
@Deprecated
private Bitmap compressImage(Bitmap bitmap) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//質量壓縮方法膝宁,這里的100表示不壓縮,把壓縮后數據存放到outputStream
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
int options = 100;
//是否大于100k
while (outputStream.toByteArray().length / 1024 > 100) {
//重置outputStream
outputStream.reset();
//第一個參數根吁,圖片格式,第二個參數:圖片的質量员淫,100為最高,0為最差 击敌,第三個參數:保存壓縮后的數據的流
bitmap.compress(Bitmap.CompressFormat.JPEG, options, outputStream);
options -= 10;
if (options <= 0) {
break;
}
}
////把壓縮后的數據存放到ByteArrayInputStream中
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
//生成圖片
Bitmap imageBitmap = BitmapFactory.decodeStream(inputStream, null, null);
return imageBitmap;
}
二介返、從相冊中提取
1.在申請權限的前提下,打開相冊
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_PICK_IMAGE);
2.重寫onActivityResult的方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
//相冊選擇圖片返回的結果
case REQUEST_PICK_IMAGE:
imageBitmap = getBitmapFromUri(getRealUri(data.getData()), 400, 400);
break;
case CAMERA_RESULT:
//拍照返回的結果
imageBitmap = getBitmapFromUri(mediaFile.getAbsolutePath(), 400, 400);
break;
default:
break;
}
if (imageBitmap != null) {
resultImageView.setImageBitmap(imageBitmap);
}
}
因為data.getData()返回的uri的形式是content://所以進行轉化愚争,然后步驟和上面調起相機的的第5個步驟相同
/**
* 獲取相冊中返回的真正的路徑
*
* @param selectedImage
* @return
*/
public String getRealUri(Uri selectedImage) {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = this.getContentResolver().query(selectedImage, filePathColumn, null, null, null);
assert cursor != null;
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
return picturePath;
}