MainActivity.java:
public class MainActivity extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;
public static final int CHOOSE_PHOT0 = 2;
private Uri imageUri;
private ImageView picture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button takePhoto = findViewById(R.id.take_photo);
picture = findViewById(R.id.picture);
takePhoto.setOnClickListener(view -> {
File outputFile = new File(getExternalCacheDir(),"output_image.jpg");
//getExternalCacheDir()方法得到当前应用缓存数据位置
try {
if (outputFile.exists()) {
outputFile.delete();
}
outputFile.createNewFile();
imageUri = FileProvider.getUriForFile(this,
"com.example.cameraalbumtest.fileprovider",outputFile);
//启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
if (intent.resolveActivity(getPackageManager()) != null){
startActivityForResult(intent, TAKE_PHOTO);//第二个参数是请求码
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO && resultCode == RESULT_OK) {
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(imageUri));
picture.setImageBitmap(BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
Android 7.0及以上要求我们用fileprovider,禁止应用将 file:// 开头的Uri共享给其他的应用读写文件,否则会收到FileUriExposedException的异常。FileProvider生成的Uri会使用我们自己配置的的name属性替换真实的文件路径。
在AndroidManifest.xml中添加provider标签:
<application>
...
<provider
android:authorities="com.example.cameraalbumtest.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
provider>
...
application>
之后在 res/xml 文件夹下创建文件 file_paths.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_images" path="." />
paths>
其中 external-cache-path标签对应getExternalCacheDirs()
name就是我们想要替换上去的名称,这样外部应用即使拿到Uri也无法知到具体的存储位置。
说明:
if (intent.resolveActivity(getPackageManager()) != null) 用来判断是否有对应的action来相应这个intent,如果有再startActivity。
public class MainActivity extends AppCompatActivity {
public static final int CHOOSE_PHOT0 = 2;
private Uri imageUri;
private ImageView picture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
picture = findViewById(R.id.picture);
Button chooseFromAlbum = findViewById(R.id.choose_from_album);
chooseFromAlbum.setOnClickListener(view -> {
//动态申请权限
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.
PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
openAlbum();
}
});
}
private void openAlbum() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOT0);//打开相册
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if(requestCode == 1) {
if (grantResults.length > 0 && grantResults[0]==PackageManager.
PERMISSION_GRANTED) {
// openAlbum();
} else {
Log.d("tag",String.valueOf(grantResults[0]));
Toast.makeText(this,"You denied the permission",
Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CHOOSE_PHOT0 && resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= 19) {
//4.4及以上系统选取相册中的图片不再返回图片的真实Uri了,而是
//一个封装过的Uri,需要对这个Uri进行解析。
handleImageOnKitKat(data);
} else {
handleImageBeforeKitKat(data);
}
}
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
//解析封装过的Uri
String imagePath = null;
Uri uri = data.getData();
if (DocumentsContract.isDocumentUri(this ,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);
} 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);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
//如果是content类型的Uri,则使用普遍方式处理
imagePath = getImagePath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
//如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
displayImage(imagePath);
}
private String getImagePath(Uri uri, String selection) {
String path=null;
//通过Uri和selection类获取真实的图片路径
Cursor cursor = getContentResolver().query(uri,null,selection, null ,null);
if (cursor != null) {
if(cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.
Images.Media.DATA);
path = cursor.getString(index);
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath) {
if (imagePath != null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
} else {
Toast.makeText(this,"failed to get image", Toast.LENGTH_SHORT).show();
}
}
}
在 AndroidManifest.xml 中需要加入权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
在 Android Q 以后采用隔离存储,WRITE_EXTERNAL_STORAGE 只有了对媒体内容进行读写的权限。在本应用的存储沙盒之中读写不需要权限。