引言:
调用系统相机拍照是安卓中比较常见,也比较简单的功能。那么拍照后得到的照片,我们有两种处理:第一种,我们只把它们显示出来(不保存在文件中,但是这种只能显示缩略图)。第二种,我们将照片写入到文件中,即保存起来,那么就可以重复使用。对于写入到文件这种操作,7.0以上版本需要特殊照顾,以避免出现FileUriExposedException异常。
第一种(获取缩略图):
首先,启动系统相机:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
然后,重写回调方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==REQUEST_IMAGE_CAPTURE&& resultCode == RESULT_OK){
Bundle extras=data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
save(imageBitmap);
}
}
现在,imageBitmap就是我们的缩略图,可以直接显示在UI界面。其实,缩略图也是可以保存的,我这里的save方法,就是将它写入到外部存储中。
/**
* 处理系统相机拍照后对图片的保存,但是实际上保存的仅仅是一张缩略图,有些时候并不能满足需要
* 而完整图片的获取在7.0以上版本并不容易,用原始方法的话会报异常,
* @param bitmap
*/
private void save(Bitmap bitmap){
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
String filename = format.format(date) + ".jpg";
File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File folder=new File(dir,"my_pictures");
/**
* File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
* getExternalStorageDirectory()方法已经废弃,官方也不推荐使用,getExternalFilesDir是
* 官方文档中使用的API
*/
if(!folder.exists()){
folder.mkdir();
}
File file=new File(folder,filename);
try {
FileOutputStream outputStream=new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
outputStream.flush();
outputStream.close();
Toast.makeText(this, "Take finish", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.w("Tag",file.getPath()); //打印存储路径
}
一张缩略图,放大了就会模糊,可能不太够用。所以不建议这样做,那么下面开始第二种方法的讲解。
第二种(存入到文件中):
/**
* 调用系统相机拍照并保存的原始方法,7.0以上需要适配
* 这里涉及到的问题就是内容共享,要使用到Provider类
*/
private void dispatchTakePictureIntent() {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
String filename = format.format(date) + ".jpg";
File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//以dir文件为父级文件夹创建子文件
File folder=new File(dir,"my_pictures");
/**
* File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
* getExternalStorageDirectory()方法已经废弃,官方也不推荐使用,getExternalFilesDir是
* 官方文档中使用的API
*/
if(!folder.exists()){
//创建目录
folder.mkdir();
}
File file=new File(folder,filename);
Uri uri=null;
if(Build.VERSION.SDK_INT>=24){
uri= FileProvider.getUriForFile(MainActivity.this,"com.example.camera.fileprovider",file);
}else{
uri=Uri.fromFile(file);
}
Log.w("Tag0",uri+"\n"+getExternalFilesDir(Environment.DIRECTORY_PICTURES));
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
我们发现对Intent的操作多了一行,takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
这句话大概是将拍摄完成的照片存储到对应uri的路径,原来旧版很容易实现,定义好存储路径,直接一行代码,通过文件获取路径就可以了。7.0以上对内容共享做了限制,需要使用到Provider类,然后调用FileProvider类的方法获取uri,下面给出适配的相关操作:
1.在res目录下新建xml文件夹,然后创建根节点为paths的路径文件:
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/Android/data/com.example.camera/files/Pictures/my_pictures"/>
</paths>
external-path标签就代表着外部存储,path属性相当于它之下的目录,我们可以不再往下写,直接写一个"/",其实最终文件存储的路径都一样,只是我们得到的Uri不同,那就来对比一下吧:
完整uri
不完整uri
打印的第一行是我们最终调用getUriForFile得到的uri,第二行是getExternalFilesDir(Environment.DIRECTORY_PICTURES)返回的路径。
2.在清单文件中注册provider:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.camera.fileprovider"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
/>
</provider>
都是固定用法,注意authorities请使用"包名.fileprovider",貌似后面是什么影响不大,但是包名必须写对,在getUriForFile方法中要传入authorities对应String型参数。
android:grantUriPermissions=“true”,不可以省略,必须为true,不然会报错。
文献参考:
Android Developers官方文档:拍照这里面还介绍了如何将照片显示在相册(默认情况是不会直接显示在相册的),以及图片的解码。