android拍照的简单方式:调用已存在的camera应用

如果你的app中有要拍照的需求,有两种方式可以实现,一是直接调用已安装的app,二是自己写一个拍照界面。这篇文章讲解最简单的方式,直接调用已有的camera应用。其实绝大多数安卓手机都会有自带的camera应用。

获取camera权限

如果你的应用必须要用到拍照的功能,那么你需要在google play上将可以下载该应用的设备限制在拥有camera程序的设备中,为了表明你的应用依赖于camera程序,你需要在 manifest 文件中使用<uses-feature>标签:

<manifest ... >
    <uses-feature android:name="android.hardware.camera" />
    ...
</manifest ... >

而如果你的应用会用到拍照功能,但是不用也无关大碍的话,你可以给<uses-feature>标签加上android:required="false",表明你的应用可以在没有camera的设备上使用,google play将允许那些没有装有camera的设备下载,但是这时你必须在自己的代码中判断一个设备是由可调用camera,如果没有camera,你应该关闭拍照功能,判断的方式是:

hasSystemFeature(PackageManager.FEATURE_CAMERA)

开始调用camera应用拍照

在安卓中调用其他activity是通过激发描述行为的intent来实现的,这一过程包含了三个方面,intent本省、调用外部activity、获得调用完成之后的返回值。

下面是通过intent来发出拍照请求的代码:

private void dispatchTakePictureIntent(int actionCode) {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(takePictureIntent, actionCode);
}

通过上面几行简单的代码就能调用拍照程序了,但是如果设备中根本没有一个应用能处理该intent请求(MediaStore.ACTION_IMAGE_CAPTURE),那么你的应用就会崩溃,为此,我们需要先判断设备中是否有能响应这个intent的app,代码如下:

public static boolean isIntentAvailable(Context context, String action) {
    final PackageManager packageManager = context.getPackageManager();
    final Intent intent = new Intent(action);
    List<ResolveInfo> list =
            packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    return list.size() > 0;
}

显示照片

但是你的目的可能不仅仅是拍照,而且还需要获得拍出的照片,然后对之进行某种操作。安卓的camera应用会在拍照完成之后返回一个intent给 onActivityResult()函数,该intent的extra部分包含了经过camera编码的图片的`Bitmap`,通过以下方式将该`Bitmap` 显示在imageview中:

private void handleSmallCameraPhoto(Intent intent) {
    Bundle extras = intent.getExtras();
    mImageBitmap = (Bitmap) extras.get("data");
    mImageView.setImageBitmap(mImageBitmap);
}

注意,如果图片比较大,这个过程可能有点耗时。

保存照片

如果你提供了一个完整的文件路径,安卓的camera程序会自动保存一张原始尺寸的图片。你需要提供存储位置、文件名、目录。

下面是获得图片路径的一个简单方法,但是只适用于android2.2以上版本:

storageDir = new File(
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    ),
    getAlbumName()
);

更早的版本需要你自己提供文件目录名:

storageDir = new File (
    Environment.getExternalStorageDirectory()
        + PICTURES_DIR
        + getAlbumName()
);

注:PICTURES_DIR其实就是 Pictures/,这是外部存储系统上存储图片的标准位置。

设置照片的文件名

上面生成文件名都是通过系统环境的函数获得的,我们需要做的是为图片取一个名字,文件名最好是按照一定规则来生成,下面提供了一个生成文件名的方法:

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp =
        new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
    File image = File.createTempFile(
        imageFileName,
        JPEG_FILE_SUFFIX,
        getAlbumDir()
    );
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

将该文件路径放入intent中

通过intent将文件的路径传递给camera程序:

File f = createImageFile();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));

将图片添加进gallery

可能你自己很清楚照片放在什么地方,因为是你存的,但为了让其他人也能看到这张图片,我们需要让系统的Media Provider能访问它。通过如下方法可以将图片添加进media provider数据库中,这样gallery等应用就可以访问到这张图片。

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);
}