作者:郭孝星
微博:郭孝星的新浪微博
邮箱:[email protected]
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells
请求相机使用权限
... >
"android.hardware.camera"
android:required="true" />
...
如果我们的App使用相机,但相机并不是App正常运行所必不可少的组件,可以将android:required 设置为 “false” 。这样的话,Google Play也会允许没有相机的设备下载该应用。当然我们有必要在使用以下方法:
hasSystemFeature(PackageManager.FEATURE_CAMERA);
来检查设备上是否有相机。如果没有,应该禁用和相机相关的功能。
利用一个描述了做什么的Intent对象,Android可以将某些执行任务委托给其他应用。整个过程包含三部分:
举例
通过发送一个Intent来捕获照片,如下所示:
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent()
{
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//在调用startActivityForResult()方法之前,先调用resolveActivity(),这个方法会返回能处理该Intent的第一个Activity,即检查有没有能处理这个Intent的Activity。执行这个检查非常重要,因为如果调用startActivityForResult()时,没有应用能处理Intent,那么应用将会崩溃。所以只要返回结果不为null,使用该Intent就是安全的。
if (takePictureIntent.resolveActivity(getPackageManager()) != null)
{
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
Android的相机应用会把拍好的照片编码为缩小的Bitmap,使用extra value的方式添加到返回的Intent当中,并传送给onActivityResult()),并且对应的Key为data。
举例
取这一图片并显示在ImageView上,如下所示:
@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);
}
}
如果我们提供一个File对象给Android的相机程序,它会保存这张全尺寸照片到给定的路径下。这个时候我们需要提供存储图片所需要的含有后缀名形式的文件名。
一般而言,用户使用设备相机所拍摄的任何照片都应该被存放在设备的公共外部存储中,这样它们就能被所有的图片访问。将DIRECTORY_PICTURES作为参数,传递给getExternalStoragePublicDirectory())方法,可以返回适用于存储公共图片的目录。由于该方法提供的目录被所有应用共享,因此对该目录进行读写操作分别需要READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。另外,因为写权限隐含了读权限,所以如果需要外部存储的写权限,那么仅需要请求一项权限就可以,如下所示:
...>
"android.permission.WRITE_EXTERNAL_STORAGE" />
...
然而,如果希望照片对我们的应用而言是私有的,我们可以使用getExternalFilesDir()提供的目录。在Android 4.3及以下版本的系统中,写这个目录需要WRITE_EXTERNAL_STORAGE权限。从Android 4.4开始,该目录将无法被其他应用访问,所以该权限就不再需要了,你可以通过添加maxSdkVersion属性,声明只在低版本的Android设备上请求这个权限,如下所示:
...>
"android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
一旦选定了存储文件的目录,我们就需要设计一个保证文件名不会冲突的命名规则。有时将路径存储在一个成员变量里以备在将来使用,如下所示:
以时间戳作为文件名
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 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
有了以上方法,我们就可以给新照片创建文件对象了,如下所示:
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
...
}
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
当通过Intent创建了一张照片,我们知道图片的存储位置。但是对其他人来说,查看照片最简单的方式是通过系统的Media Provider。另外,如果我们将图片存储在getExternalFilesDir())提供的目录中,媒体扫描器是访问不到我们的文件的,因为它们属于你的应用的私有数据。
举例
触发系统的Media Scanner将照片添加到Media Provider的数据库中,这样就可以使得Android相册程序与其他程序能够读取到这些照片。
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);
}
在有限的内存下,管理许多全尺寸的图片会很棘手。因为它会消耗了很多内存,解决方案就是通过缩放图片到目标视图尺寸,之后再载入到内存中的方法,来显著降低内存的使用,如下所示:
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;
// 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);
mImageView.setImageBitmap(bitmap);
}