Android 拍照功能详解

本文详细介绍了Android中实现拍照功能的步骤,包括简单的拍照、保存完整图片以及图片压缩。在简单拍照中,讨论了权限注册、使用Camera App和处理缩略图。保存完整图片涉及文件写入权限、创建文件目录及FileProvider配置。最后,提到了图片压缩的必要性和方法,并指出如何使拍照图片可供其他应用访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

欢迎大家留言交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值