之前搜索了各种网上的开启摄像头拍照和选取相册的方式,发现大部分都不太兼容、适合。因此在这里总结一下切实可用的方法(参考第一行代码)
拍照使用的是FileProvider,这是为了保证uri安全机制而使用的一个类。使用此类在最新安卓版本中不需要访问SD卡中内容。
--工具包
1.CameraUtil(相机相关工具类)
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import com.frz.marryapp.activity.ChoosePhotoActivity;
import com.frz.marryapp.base.GlobalApplication;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class CameraUtil {
public static final int TAKE_PHOTO = 1;
public static final int CHOOSE_PHOTO = 2;
public static final int CROP_PHOTO = 3;
public static boolean IS_LARGER_VERSION_19 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
/**
* 对于4.4版本通过FileProvider获取到的Uri
* @param activity 放入当前的活动
* @return 返回拍照后的对应文件uri
*/
public static Uri getPhotoUri(Activity activity, String providerAuthority){
//创建File对象,用于存储拍照后的图片
Uri imageUri;
File outputImage = new File(activity.getExternalCacheDir(), "output_image.jpg");
try{
if(outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
if(Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(activity,
providerAuthority, outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
return imageUri;
}
/**
* 在权限通过后,可以调用此方法打开相册
* @param activity 传入当前活动
*/
public static void openAlbum(Activity activity){
Intent intent;
if(IS_LARGER_VERSION_19){
//4.4版本及以上建议使用这个打开相册
intent = new Intent("android.intent.action.OPEN_DOCUMENT");
}else{
intent = new Intent("android.intent.action.GET_CONTENT");
}
intent.setType("image/*");
activity.startActivityForResult(intent, CHOOSE_PHOTO);
}
/**
* 传入uri返回图片路径,这里不适用FileProvider提供的方式,而是适用SD卡获取
* @param uri 传入获取到的Intent数据
* @param activity 传入当前活动
* @return 返回图片路径
*/
public static String handleImage(Uri uri, Activity activity){
String imagePath;
if(IS_LARGER_VERSION_19){
//4.4版本以上使用这个而方法处理图片
imagePath = handleImageNewWay(uri, activity);
}else{
imagePath = handleImageOldWay(uri, activity);
}
return imagePath;
}
private static String handleImageOldWay(Uri uri, Activity activity) {
String imagePath = getImagePath(uri, null, activity);
return imagePath;
}
@TargetApi(19)
private static String handleImageNewWay(Uri uri, Activity activity) {
String imagePath = null;
if(DocumentsContract.isDocumentUri(activity, uri)){
//如果是document类型的uri,则通过document id处理
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];//解析出数字格式的id
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, activity);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(docId));
imagePath = getImagePath(contentUri, null, activity);
}
}else if("content".equalsIgnoreCase(uri.getScheme())){
//如果content类型的uri,则使用普通方式处理
imagePath = getImagePath(uri, null, activity);
}else if("file".equalsIgnoreCase(uri.getScheme())){
//如果是file类型的Uri,直接截取图片路径即可
imagePath = uri.getPath();
}
return imagePath;
}
private static String getImagePath(Uri uri, String selection, Activity activity) {
String path = null;
//通过Uri和selection来获取真实的图片路径
Cursor cursor = activity.getContentResolver().query(uri, null, selection, null, null);
if(cursor != null){
if(cursor.moveToFirst()){
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
/**
* 通过provider获得的Uri,相反想获取路径通过此方法获取
* @param context 当前context上下文,传入活动也可
* @param uri 通过FileProvider转换而成的封装过的uri
* @return 返回路径
*/
public static String getPathByUri(Context context, Uri uri) {
try {
List<PackageInfo> packs = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS);
if (packs != null) {
String fileProviderClassName = FileProvider.class.getName();
for (PackageInfo pack : packs) {
ProviderInfo[] providers = pack.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if (uri.getAuthority().equals(provider.authority)) {
if (provider.name.equalsIgnoreCase(fileProviderClassName)) {
Class<FileProvider> fileProviderClass = FileProvider.class;
try {
Method getPathStrategy = fileProviderClass.getDeclaredMethod("getPathStrategy", Context.class, String.class);
getPathStrategy.setAccessible(true);
Object invoke = getPathStrategy.invoke(null, context, uri.getAuthority());
if (invoke != null) {
String PathStrategyStringClass = FileProvider.class.getName() + "$PathStrategy";
Class<?> PathStrategy = Class.forName(PathStrategyStringClass);
Method getFileForUri = PathStrategy.getDeclaredMethod("getFileForUri", Uri.class);
getFileForUri.setAccessible(true);
Object invoke1 = getFileForUri.invoke(invoke, uri);
if (invoke1 instanceof File) {
String filePath = ((File) invoke1).getAbsolutePath();
return filePath;
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
break;
}
break;
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 剪裁图片功能
* @param activity 传入当前活动
* @param sourceUri 传入需要剪裁图片的uri
* @param targetPath 传入需要保存的"文件"路径
* @param width 传入需要剪裁成的宽度
* @param height 传入需要剪裁成的高度
*/
public static void cropImg(Activity activity, Uri sourceUri, String targetPath, int width, int height){
Intent intent = new Intent("com.android.camera.action.CROP");
if(IS_LARGER_VERSION_19){
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加这一句表示对目标应用临时授权该Uri所代表的文件
}
intent.setDataAndType(sourceUri, "image/*");
intent.putExtra("crop", "true"); // crop为true是设置在开启的intent中设置显示的view可以剪裁
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", width);
intent.putExtra("outputY", height);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
File file = new File(targetPath);
if(file.exists()){
file.delete();
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
activity.startActivityForResult(intent, CameraUtil.CROP_PHOTO);
}
}
2.BitmapUtil(图片处理相关工具类)
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.util.Log;
import java.io.IOException;
public class BitmapUtil {
/**
* 此方法用于获取图片选取后旋转角度获取
* @param filepath 文件路径
* @return 返回此时照片旋转角度
*/
public static int getImageDegree(String filepath){
int degree = 0;
ExifInterface exif = null;
try {
exif = new ExifInterface(filepath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
switch (orientation){
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
}catch (IOException e){
Log.d("错误", "不能创建出ExifInterface");
}
return degree;
}
/**
* 更正图片旋转
* @param bitmap 目标图片
* @param degree 需要旋转的角度
* @return
*/
public static Bitmap resetImage(Bitmap bitmap, int degree){
Matrix matrix = new Matrix();
matrix.postRotate(degree);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
*
* @param imgPath map的路径
* @param sampleSize map的缩放大小,宽和高都同比例缩放
* @return 返回压缩后的bitmap
*/
public static Bitmap compressBitmapBySampleSize(String imgPath, int sampleSize){
BitmapFactory.Options options = new BitmapFactory.Options();
/* options.inJustDecodeBounds = true;//只去读图片的头信息,不去解析真实的位图
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);*/
options.inSampleSize = sampleSize;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
return bitmap;
}
/**
* 使用Matrix将Bitmap压缩到指定大小
* @param imgPath 图片路径
* @param w 要压缩的图片宽
* @param h 要压缩的图片高
* @return
*/
public static Bitmap resizeBitmap(String imgPath, int w, int h)
{
Bitmap bitmap = BitmapFactory.decodeFile(imgPath);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scaleWidth = ((float) w) / width;
float scaleHeight = ((float) h) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, true);//使用bitmap,从(x,y)点开始,截取宽width,高height,根据matrix进行缩放,filter=true表示对其进行剪裁操作
return resizedBitmap;
}
}
MainActivity
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn1, btn2;
private ImageView img1, img2;
private Uri imageUri;
private String targetCropPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initView() {
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
img1 = (ImageView) findViewById(R.id.img1);
img2 = (ImageView) findViewById(R.id.img2);
}
private void initListener() {
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn1:
take_photo();
break;
case R.id.btn2:
choose_photo();
break;
default:
break;
}
}
private void choose_photo() {
//权限确认
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else{
CameraUtil.openAlbum(this);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
CameraUtil.openAlbum(this);
}else{
Toast.makeText(this, "您拒绝赋予此权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void take_photo() {
//启动相机程序
imageUri = CameraUtil.getPhotoUri(this, "com.frz.marryapp.activity.fileprovider");
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CameraUtil.TAKE_PHOTO);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
targetCropPath = getExternalCacheDir() + File.separator + "temp.jpg";
switch (requestCode){
case CameraUtil.TAKE_PHOTO:
if(resultCode == RESULT_OK){
//将拍摄的照片显示出来
//正数为顺时针旋转
String imagePath = CameraUtil.getPathByUri(this, imageUri);
Bitmap bitmap1 = BitmapFactory.decodeFile(imagePath);
bitmap1 = BitmapUtil.resetImage(bitmap1, BitmapUtil.getImageDegree(imagePath));
img1.setImageBitmap(bitmap1);
CameraUtil.cropImg(this, imageUri, targetCropPath, 500, 500);
}
break;
case CameraUtil.CHOOSE_PHOTO:
if(resultCode == RESULT_OK){
String imagePath = CameraUtil.handleImage(data.getData(), this);
Bitmap bitmap1 = BitmapFactory.decodeFile(imagePath);
bitmap1 = BitmapUtil.resetImage(bitmap1, BitmapUtil.getImageDegree(imagePath));
img1.setImageBitmap(bitmap1);
CameraUtil.cropImg(this, data.getData(), targetCropPath, 500, 500);
}
break;
case CameraUtil.CROP_PHOTO:
if(resultCode == RESULT_OK){
Bitmap bitmap = BitmapFactory.decodeFile(targetCropPath);
bitmap = BitmapUtil.resetImage(bitmap, BitmapUtil.getImageDegree(targetCropPath));
img2.setImageBitmap(bitmap);
}
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="before"/>
<ImageView
android:id="@+id/img1"
android:layout_width="match_parent"
android:layout_height="400dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="after"/>
<ImageView
android:id="@+id/img2"
android:layout_width="match_parent"
android:layout_height="400dp" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拍照"/>
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选取照片"/>
</LinearLayout>
</ScrollView>
效果:
照片点击确定后,自动伸张为500X500,如果选取的区域过小,其分辨率将会很低