Android拍照功能相信都不陌生。这篇文章总结下Android拍照功能的具体知识。
分两种场景:
- 场景一,拍照然后显示照片在页面;
- 场景二,拍照然后将照片以文件方式存储。
简单的拍照功能
原理:调用系统的Camera应用生成一张照片
1.要求系统支持拍照功能
<manifest ... >
<uses-feature android:name="android.hardware.camera"
android:required="true" />
...
</manifest>
在AndroidManifest.xml中注册相机权限,这样在支持拍照功能的手机上允许安装应用。如果手机系统不支持拍照功能,则不允许安装应用程序。
2.使用Camera App拍照
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
方法中用到一个resolveActivity(),该方法返回intent可以操作的第一个Activity。使用该方法可以防止Intent启动过程出现空指针异常
3.处理缩略图
Camera应用程序将照片作为小的位图发送到onActivityResult()的Intent中,key值为data。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_IMAGE_CAPTURE){
Bundle bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get("data");
mImageView.setImageBitmap(bitmap);
}
}
上面介绍了使用Android系统的Camera应用生成一个缩略图,如果我们希望获取完整的拍照图片,我们需要以File的形式来存储拍摄照片。
保存完整的拍照图片
保存拍照图片为完整的文件,关键是得到图片的Uri,然后用Intent调起拍照的时候将文件的Uri放入MediaStore.EXTRA_OUTPUT参数中。
1.首先添加文件的写入权限
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
2.创建文件目录
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
getExternalFilesDir(Environment.DIRECTORY_PICTURES)方法获取到的文件目录是在:
Android/data/包名/files/Pictures
当然,你可以将图片放到任何位置,比如SDCard中、Public目录中等等。该方法可以作为一个文件目录创建的参考。
3.拍照操作
static final int REQUEST_TAKE_PHOTO = 2;
private void dispatchTakePictureFileIntent() {
Uri photoURI;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
if (photoFile != null) {
//根据不同的系统版本处理文件目录访问的方式
if(Build.VERSION.SDK_INT >= 24){
photoURI = FileProvider.getUriForFile(this,
"com.camerademo.fileprovider",
photoFile);
}else {
photoURI = Uri.fromFile(photoFile);
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
因为Android7.0以后改变了文件的Uri操作方式。Android7.0需要通过FileProvider来获取文件的Uri路径。
Android7.0及以上获取到的Uri为:file:// URI
Android7.0以前获取到的Uri为:content:// URI
FileProvider配置
因此,我们需要在AndroidMainfest.xml里面指定我们的FileProvider:
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.camerademo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
...
</application>
注意:这里的authorities就是上面代码FileProvider.getUriForFile()中的对应的名称。resource下面的file_paths是在values->xml目录下的file_paths文件
<?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.camerademo/files/Pictures" />
</paths>
这里可以指定path的目录,也可以将path目录滞空。
图片压缩
使用相机拍照生成的照片往往好几兆,当我们显示在页面上的时候根本不需要显示像素这么高的图片,不仅会增大我们应用的内存消耗,有时候甚至会导致应用内存泄漏,因此在显示照片的时候我们都要做图片的压缩处理。
private void setPic() {
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
Log.e("wj","压缩前的图片宽高:" + photoW + "*" + photoH);
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
Log.e("wj","压缩后的图片宽高:" + bmOptions.outWidth + "*" + bmOptions.outHeight);
mImageView.setImageBitmap(bitmap);
}
我们看下图片在处理前和处理后,大小区别
09-05 14:20:49.972 18911-18911/com.camerademo E/wj: 压缩前的图片宽高:1840*3264
09-05 14:20:50.192 18911-18911/com.camerademo E/wj: 压缩后的图片宽高:460*816
图片压缩也是我们常用方法之一。该方法可以当做图片压缩的一个处理的参考。
至此拍照功能介绍结束。
最后我们考虑这样一个问题,我们在上面存储拍照后的图片使用的文件路径是getExternalFilesDir(),该路径只能在我们应用内访问,系统不会将该图片添加到系统的Media Provider中,也就是说我们无法在其他应用中通过Media Scanner功能发现该照片。
要想让其他应用可以通过媒体搜索发现我们拍照生成照片,我们需要发送一个系统广播,通知系统将我们的照片路径下的照片文件添加到系统的Media Provider 的database中。
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
欢迎大家留言交流。