- 自Android 6.0以后对某些涉及用户隐私权限的获取需要动态获取,所以首先是检查权限,如没有权限则动态申请权限,这里我们需要用到的权限是WRITE_EXTERNAL_STORAGE和CAMERA。
- 自Android 7.0后系统禁止应用向外部公开file://URI ,因此需要FileProvider来向外界传递URI。
- 获取到拍照后的照片,按照现在的手机拍照文件大小来说不做处理直接展示很容易发生OOM,可以通过采样率对图片进行缩小,或者压缩的操作。
接下来我们看如何实现调用相机拍照并返回图片和从相册中选择相
片。
一、调用相机拍照并返回图片
1.动态申请权限
首先在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"/>
在代码中动态申请
private void requestPermission() {
int permission = ActivityCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
//动态申请权限
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后系统禁止应用向外部公开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文件夹,在文件夹下创建一个名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);这个进行获取的图片的文件流获取失败(解决方法还没找到,如果知道解决方法的欢迎留言),后来发现可以直接通过 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;
}