Android 拍照及从相册选择图片传详解
先上图
新知识点速览
-
URI(统一资源标识符)是标识逻辑或物理资源的字符序列,与URL类似,也是一串字符。通过使用位置,名称或两者来标识Internet上的资源;它允许统一识别资源。
有两种类型的URI,统一资源标识符(URI)和统一资源名称(URN)
任何URI的通用格式都是:
scheme:[// [user:password @] host [:port]] [/] path [?查询] [#片段]
-
关联缓存目录就是SD卡中专门用于存放当前应用缓存的数据位置,使用关联缓存目录可以跳过Android6以上的权限问题
-
从Android6开始读写SD卡被列为危险权限,如果将图片放到SD卡或其他任何目录都要进行运行时权限处理,从Android7开始直接使用本地真实的路径的Uri被认为是不安全的,会抛出一个FileUriExposedException异常
从Android4.4开始选取相册中的图片不再返回真实的Uri,而是一个封装过的uri -
FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性的将封装过的Uri共享给外部,从而提高了应用的安全性
-
打开相机的方法
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
实例
AndroidManifest.xml配置
Android10版本不加android:requestLegacyExternalStorage="true"
会报错
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.camerastuday">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CameraStuday">
<provider
<!-- 必须与后面的Provider中的authorities相同 -->
android:authorities="com.example.camerastuday.fileProvider"
<!-- name属性是固定的 -->
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<!-- 通过meta标签来指定Uri的共享路径 -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path"
/>
</provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
file_path配置资源
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="mt-image"
path="."/>
</paths>
<!-- .表示将整个SD卡目录进行共享 -->
java核心代码
package com.example.camerastuday;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import android.Manifest;
import android.content.ContentUris;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URI;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "TAG";
private Button btnGetPhotoWithCamera;
private Button btnGetPhotoInRes;
private ImageView IVShowImage;
private Uri imageUri;
private final int REQUESTCONDE_FORPERMISSONS = 2;
private String[] permissions = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnGetPhotoWithCamera = findViewById(R.id.btn_GetPhotoWithCamera);
btnGetPhotoInRes = findViewById(R.id.btn_GetPhotoInRes);
IVShowImage = findViewById(R.id.IV_showImage);
initView();
}
//动态申请权限
public void RequestPermissions(){
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(MainActivity.this,permissions[i]) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,permissions,REQUESTCONDE_FORPERMISSONS);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUESTCONDE_FORPERMISSONS:
for (int i = 0; i < grantResults.length; i++) {
if (grantResults.length>0 && grantResults[i]==PackageManager.PERMISSION_GRANTED){
Log.d(TAG, "onRequestPermissionsResult: "+"成功申请到" + permissions[i]);
}else{
Toast.makeText(this, permissions[i]+"未成功申请成功", Toast.LENGTH_SHORT).show();
}
}
break;
}
}
public void initView() {
RequestPermissions();
//拍照
btnGetPhotoWithCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过getExternalCacheDir()得到关联缓存目录
File OutImage = new File(getExternalCacheDir(), "outPut_image.jpg");
try {
if (OutImage.exists()) {
//如果文件存在,就删掉
OutImage.delete();
}
//创建一个新文件
OutImage.createNewFile();
} catch (Exception e) {
}
if (Build.VERSION.SDK_INT >= 24) {
//getUriForFile() 将一个File对象转化成一个封装过的Uri对象,注意第二个参数com.example.camerastuday时包名
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.camerastuday.fileProvider", OutImage);
} else {
imageUri = Uri.fromFile(OutImage);
}
//打开相机部分
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
//指定输出图片的地址
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, 1);
}
});
//从相册选择
btnGetPhotoInRes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//打开相册
openColumn();
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 1:
if (resultCode==RESULT_OK){
try {
//将图片转为Bitmap
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
//设置图片
IVShowImage.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
case 2:
if (resultCode==RESULT_OK){
if (Build.VERSION.SDK_INT>=19){
if (data!=null){
//4.4及以上采用这个方法
handleImageONKitKat(data);
}else{
//4.4系统一下采用这个方法
handleImageBeforeKitKat(data);
}
}
}
break;
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void handleImageONKitKat(Intent data){
String imagePath = null;
Uri uri = data.getData();
Log.d(TAG, "uri=: "+uri); // content://com.android.providers.media.documents/document/image%3A37
Log.d(TAG, "getUri`s Authority: "+uri.getAuthority()); //输出是这样的: com.android.providers.media.documents
if (DocumentsContract.isDocumentUri(this,uri)){
//如果document的类型是uri,则通过document id处理
String docId = DocumentsContract.getDocumentId(uri);
Log.d(TAG, "docId=: "+docId); //输出是这样的: image:37
if ("com.android.providers.media.documents".equals(uri.getAuthority())){
//解析出数字格式的ID
String id = docId.split(":")[1];
Log.d(TAG, "id=: "+id); //37
String selection = MediaStore.Images.Media._ID + "=" + id;
Log.d(TAG, "selection=: "+selection); //_id=37
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
Log.d(TAG, "imagePath=: "+imagePath); ///storage/emulated/0/Download/R65398d6ad86129f9628c0ad80da4040c.jpeg
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUIri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));
Log.d(TAG, "contentUIri: "+contentUIri);
imagePath = getImagePath(contentUIri,null); ///storage/emulated/0/Download/R65398d6ad86129f9628c0ad80da4040c.jpeg
}
}else if ("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(uri,null);
}else if ("file".equalsIgnoreCase(uri.getScheme())){
imagePath = uri.getPath();
}
disPlayImage(imagePath);
}
public void handleImageBeforeKitKat(Intent data){
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
disPlayImage(imagePath);
}
//打开相册
public void openColumn(){
Intent intent = new Intent("android.intent.action.GET_CONTENT");
//选择文件的格式,除此之外还有 intent.setType(“audio/*”); //选择音频 //intent.setType(“video/*”); //选择视频 (mp4 3gp 是android支持的视频格式) //intent.setType(“video/;image/”);//同时选择视频和图片
//文件选错
intent.setType("image/*");
startActivityForResult(intent,2);
}
//展示图片
private void disPlayImage(String imagePath) {
if (imagePath != null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
IVShowImage.setImageBitmap(bitmap);
}else{
Toast.makeText(this, "未能成功加载图片", Toast.LENGTH_SHORT).show();
}
}
private String getImagePath(Uri uri, String Selection) {
String path = null;
//通过Uri和Selection来获取真实的图片路径
Cursor cursor = getContentResolver().query(uri,null,Selection,null,null);
Log.d(TAG, "cursor : "+cursor);
//-> android.content.ContentResolver$CursorWrapperInner@6105cc6
if (cursor!=null){
// 查询得到的cursor是指向第一条记录之前的,因此查询得到cursor后第一次调用moveToFirst或moveToNext都可以将cursor移动到第一条
if (cursor.moveToFirst()){
Log.d(TAG, "getColumnIndex: "+cursor.getColumnIndex(MediaStore.Images.Media.DATA)); //17
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
Log.d(TAG, "getImagePath: "+path);
// ->/storage/emulated/0/Download/R65398d6ad86129f9628c0ad80da4040c.jpeg
return path;
}
}