本文讲解用已有的相机应用进行拍照与录像操作。而非自定义相机。
一般的应用需要拍照的地方不多,最多的莫过于头像,拍照或者选择文件上传。因此不需要自己实现一个相机那么复杂。只需要启动相机应用来拍照然后处理图像即可。为了防止设备没有相机应用而导致崩溃,因此要进行检测。
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);
}
}
在onActivityResult()中可以获得一个小的bitmap,即缩略图。适用于作为图标显示,但这并不是原尺寸图。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}
当指定保存的文件全路径时,原图将会被保存下来。一般来说,我们会将图片保存到sd卡中。一般地,通过getExternalStoragePublicDirectory(),同时,传递DIRECTORY_PICTURES参数,该目录中图片可以被所有app共享。由于用到sd卡存储,因此要申明权限
如果想要只能被本应用使用,则要用getExternalFilesDir()方法。Android4.3及更早版本,需要WRITE_EXTERNAL_STORAGE权限,4.4及以后,不需要此权限,因为4.4以后该目录不能被所有app共享。因此,当minSdkVesion比4.4早时,可以用如下方法:
...
当然,如果是私有目录,则应用卸载时,图片也被删除。
创建保存文件:
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
// 创建文件名称
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;
}
指定保存文件:
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 确保有相机应用
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// 创建图片保存的文件
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// 创建文件发生错误
...
}
// 仅当文件创建成功,才继续执行
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
注意:上面使用了FileProvider.getUriForFile()方法,将会返回一个content://URI,7.0及以后,如果传递file://URI会引发FileUriExposedException。因此,使用FileProvider来生成URI。所以,还需要配置FileProvider。
...
...
注意,authorities属性要与FileProvider.getUriForFile()中的第二个参数一致。
name要与指定的文件获取目录对应。
注意:由于全尺寸图片一般会很大,动不动就是10几M的大小,如果直接加载会造成内存溢出,因此要进行压缩。就不赘述了。
拍照之后,图库等应用不会立马收到通知,不会立马显示出来,因此要发送广播通知已经拍摄完,可以进行刷新。
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);
}
与拍照类似,直接上代码了:
static final int REQUEST_VIDEO_CAPTURE = 1;
private void dispatchTakeVideoIntent() {
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
}
}
与拍照不同的是,在onActivityResult中返回的是指向Video在存储中的位置Uri。可以在VideoView中播放。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
Uri videoUri = intent.getData();
mVideoView.setVideoURI(videoUri);
}
}