最近在项目中要用到使用系统相机和相册,单选照片,并上传。现在总结一下其中遇到的坑…
一、打开相机
- 1.正常情况下打开相机(低于6.0系统)
这种情况打开相机没有过多的设置,只要在manifest中配置权限:相机权限、内存的读写权限就OK了。
//拍的照片存放的位置
File file = new File(Environment.getExternalStorageDirectory(), "/temp/" + System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
imageUri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
activity.startActivityForResult(intent, SELECT_CAMERA);
- 2.适配6.0
2.1 由于6.0系统需要动态的请求权限,所以在manifest设置权限之后,还要在打开相机之前请求是否拥有权限,动态请求
//适配6.0 动态请求权限
final String permission = Manifest.permission.CAMERA; //相机权限
final String permission1 = Manifest.permission.WRITE_EXTERNAL_STORAGE; //写入数据权限
//先判断是否被赋予权限,没有则申请权限
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(activity, permission1) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { //给出权限申请说明
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_REQUEST_CODE);
} else { //直接申请权限
//申请权限,可同时申请多个权限,并根据用户是否赋予权限进行判断
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
}
} else { //赋予过权限,则直接调用相机拍照
//在此调用打开相机的代码
}
2.2 在请求权限之后,要对请求做出反应(是否同意该权限)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) { //申请权限的返回值
case CAMERA_REQUEST_CODE:
int length = grantResults.length;
final boolean isGranted = length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[length - 1];
if (isGranted) { //如果用户赋予权限,则调用相机
//在此调用打开相机代码
} else { //未赋予权限,则做出对应提示
}
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 3.适配7.0
7.0系统特性
根据7.0的特性,在适配的时候需要使用provider来共享文件
3.1 在manifest中声明provider,不会的可以百度,或者看博主之前的文章
3.2
//7.0调用调用相机的provider
//MYAPP_CAMERA_PROVIDER_NAME值需要需要和在maifest中声明的provider的authorities值相同
private static final String MYAPP_CAMERA_PROVIDER_NAME = "[你自己的provider的路径].myfileprovider";
//在打开相机的时候加适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
imageUri = FileProvider.getUriForFile(activity, MYAPP_CAMERA_PROVIDER_NAME, file);
} else {
imageUri = Uri.fromFile(file);
}
- 4.合并起来
/**
* 选择系统相机
* 已增加适配7.0
* 对于6.0的适配 权限,默认是拥有使用相机的权限
*
* @param activity
*/
public void openCameraForPhoto(Activity activity) {
//适配6.0 动态请求权限
final String permission = Manifest.permission.CAMERA; //相机权限
final String permission1 = Manifest.permission.WRITE_EXTERNAL_STORAGE; //写入数据权限
//先判断是否被赋予权限,没有则申请权限
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(activity, permission1) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { //给出权限申请说明
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_REQUEST_CODE);
} else { //直接申请权限
//申请权限,可同时申请多个权限,并根据用户是否赋予权限进行判断
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
}
} else { //赋予过权限,则直接调用相机拍照
openCamera(activity);
}
}
/**
* 打开相机
*
* @param activity
*/
private void openCamera(Activity activity) {
//适配7.0 共享文件的uri改为由FileProvider获取
file = new File(Environment.getExternalStorageDirectory(), "/temp/" + System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
imageUri = FileProvider.getUriForFile(activity, MYAPP_CAMERA_PROVIDER_NAME, file);
} else {
imageUri = Uri.fromFile(file);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
activity.startActivityForResult(intent, SELECT_CAMERA);
}
- 5.拍照之后返回
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SELECT_CAMERA://选择相机拍照返回
if (resultCode == Activity.RESULT_OK) {
//上面定义的file和imageUri就是照片的
}
break;
case SELECT_PHOTOALBUM://选择相册照片返回
if (resultCode == Activity.RESULT_OK && data != null) {
if (takePhotoListener != null) {
takePhotoListener.takePhotoSuccess(getFileFromMediaUri(activity, data.getData()), data.getData());
} else {
takePhotoListener.takePhotoFailed("选择照片失败,请重试");
}
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
二、打开相册
- 1.正常打开相册
Intent intent;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
activity.startActivityForResult(intent, SELECT_PHOTOALBUM);
- 2.选择一张照片返回,拿到照片
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SELECT_PHOTOALBUM://选择相册照片返回
if (resultCode == Activity.RESULT_OK && data != null) {
if (takePhotoListener != null) {
//选择的照片的uri
Uri selectUri=data.getData();
} else {
//"选择照片失败,请重试"
}
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
三、在此提供一个uri转为File的方法,同时进行质量压缩
/**
* 通过Uri获取文件
*
* @param ac
* @param uri
* @return
*/
public static File getFileFromMediaUri(Context ac, Uri uri) {
if (uri.getScheme().toString().compareTo("content") == 0) {
ContentResolver cr = ac.getContentResolver();
Cursor cursor = cr.query(uri, null, null, null, null);// 根据Uri从数据库中找
if (cursor != null) {
cursor.moveToFirst();
String filePath = cursor.getString(cursor.getColumnIndex("_data"));// 获取图片路径
cursor.close();
if (filePath != null) {
return new File(filePath);
}
}
} else if (uri.getScheme().toString().compareTo("file") == 0) {
return new File(uri.toString().replace("file://", ""));
}
return null;
}
/**
* 通过uri获取图片并进行压缩
*
* @param uri
*/
public static Bitmap getBitmapFormUri(Activity ac, Uri uri) throws FileNotFoundException, IOException {
InputStream input = ac.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
int originalWidth = onlyBoundsOptions.outWidth;
int originalHeight = onlyBoundsOptions.outHeight;
if ((originalWidth == -1) || (originalHeight == -1))
return null;
//图片分辨率以480x800为标准
float hh = 800f;//这里设置高度为800f
float ww = 480f;//这里设置宽度为480f
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//be=1表示不缩放
if (originalWidth > originalHeight && originalWidth > ww) {//如果宽度大的话根据宽度固定大小缩放
be = (int) (originalWidth / ww);
} else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的话根据宽度固定大小缩放
be = (int) (originalHeight / hh);
}
if (be <= 0)
be = 1;
//比例压缩
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = be;//设置缩放比例
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
input = ac.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return compressImage(bitmap);//再进行质量压缩
}
/**
* 质量压缩方法
*
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 100;
while (baos.toByteArray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
//第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差 ,第三个参数:保存压缩后的数据的流
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
options -= 10;//每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
return bitmap;
}