开发中我们常须要通过相机获取照片(拍照上传等)。一般通过调用系统提供的相机应用就可以满足需求;有一些复杂需求还须要我们自己定义相机相关属性,下篇我们会涉及到。

首先我们来研究怎样简单调用系统相机应用来获取照片

GitHub地址:CameraDemo

调用系统相机获取照片基本上涉及下面三个过程:

1.启动系统相机拍照

2.获取拍摄到的图片

3.图片处理

下面是详细编码过程

Camera Permission

我们要使用系统相机,首先须要在Manifest中声明

<Manifest>
        <uses-features android:name="android.hardware.camera"
                        android:required="true"/>
        ...
    </Manifest>

附注<uses-features/> 用来请求使用某些硬件或软件资源

详细使用详见

Intent

启动相机的Intent构造 MediaSotre.ACTION_IMAGE_CAPTURE

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

我们须要获得相机拍摄的照片。所以startActivity(intent,requestCode);之后在onActivityResult()中接收返回的数据

startActivityForResult(takePictureIntent,REQUEST_IMAGE_CAPTURE);

获得图片

这里须要注意,直接调用相机返回的照片是缩略图,并非完整尺寸,要想获得完整尺寸还须要做进一步处理:

1,获得缩略图(Thumbnail)

onActivityResult()方法的返回的intent的extras中存储在相应data下。一张缩略图

public void onActivityResult(int requestCode,int resultCode,Intent data){
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap)extras.get("data") ;
        mImageView.setImageBitmap(imageBitmap);
    }
2。获得完整尺寸图片

很多时候,缩略图并不能满足我们的需求。我们须要完整尺寸的图片。为此我们还须要做些工作;

首先我们须要将拍到的照片存储为文件(提供文件名称和完整的文件路径),之后通过该文件Uri便可訪问完整尺寸的图片;

1).涉及到文件存储。因此我们须要在Manifest中声明读写Storage的权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2).文件路径问题,我们须要通过getExternalFileDir()getExternalStoragePublicDirectory()两个方法得到存储路径。二者的差别是:前者获得路径是private。仅仅能供本程序读取,后者是public,全部应用均可訪问该图片。

...
    private static final String CAMERA_DIR = "/dcim/";
    private static final String albumName ="CameraSample";
    ...
    //获得文件路径,这里以public为例
    private File getPhotoDir(){
        File storDirPrivate = null;
        File storDirPublic = null;

        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){

            //private,仅仅有本应用可訪问
            storDirPrivate = new File (
                    Environment.getExternalStorageDirectory()
                            + CAMERA_DIR
                            + albumName
            );

            //public 全部应用均可訪问
            storDirPublic = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                    albumName);

            if (storDirPublic != null) {
                if (! storDirPublic.mkdirs()) {
                    if (! storDirPublic.exists()){
                        Log.d("CameraSample", "failed to create directory");
                        return null;
                    }
                }
            }
        }else {
            Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
        }

        return storDirPublic;//或者return storDirPrivate;

    }

3).文件名称问题。假设我们将文件名称写死,明显会出现文件名称冲突,同一路径无法存储多个文件,因此我们一般通过“时间戳”的方式命名图片;下面是一个简单的经常使用创建文件名称演示样例:

...
    private static final String JPEG_FILE_PREFIX = "IMG_";
    private static final String JPEG_FILE_SUFFIX = ".jpg";
    ...
    private File createFile() throws IOException {
        File photoFile = null;

        String fileName;
        //通过时间戳差别文件名称
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());


        fileName = JPEG_FILE_PREFIX+timeStamp+"_";

        photoFile = File.createTempFile(fileName,JPEG_FILE_SUFFIX,getPhotoDir());

        return photoFile;
    }

4).我们还须要将文件的Uri传递给intent。同前面简易调用相机不同。这里我们须要将获得图片后,图片存储的文件Uri传递给Intent

Intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(photoFile));

5).完整尺寸照片的解码

我们还须要将保存的图片解码一次,代码演示样例

private void setPic() {

        //获得图像的尺寸
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(photoFile.getAbsolutePath(),bmOptions);

        int photoW = bmOptions.outWidth;
        int photoH =bmOptions.outHeight;

        //计算缩放
        int scaleFactor = 1;
        if((targetW>0)||(targetH>0)){
            scaleFactor = Math.min(photoW/targetW,photoH/targetH);
        }

        //将保存的文件解码
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;


        Bitmap bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath(), bmOptions);


        mImageView.setImageBitmap(bitmap);
    }

6).Tips:一般这么获得的照片一般无法在手机相冊中直接浏览,能够将其加入至相冊以便我们浏览。

//将图片文件加入至相冊(便于浏览)
private void galleryAddPic() {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri contentUri = Uri.fromFile(photoFile);
        mediaScanIntent.setData(contentUri);
        this.sendBroadcast(mediaScanIntent);
    }

综合演示样例:

假设还有疑问,请參考:

CameraDemo

參考资料:

Taking Photos Simply

Android 4高级编程 P579

Android Camera 官方资料

Controlling the Camera

Camera