您的位置:首页 > 移动开发 > Android开发

android选择图片拍照详解(裁剪-旋转-压缩)

2016-01-12 15:00 621 查看
最新开发一个选择相机拍照功能,遇到了不少问题,这里记录一下。

打开相机一般来说有两种方式,一种是自己初始化相机,调用Camera组件,相对来说这种比较复杂,需要做的工作也比较多,还有一种是调用相机的隐式Intent打开相机,打开相机选择图片这种需求其实调用隐式Intent就可以了,没必要自己初始化camera对象。



下面是介绍的是如何打开camera相机;

/**
     * 初始化相机控件
     */
    private void openCamera() {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            // 部分机型更改了action的值,这里尽量使用系统定义的常量
            Intent getImageByCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            if (TextUtils.isEmpty(H5Constant.bigPicPath)) {
                H5Constant.bigPicPath = SysConfig.SD_IMAGE_PATH + H5Constant.tempImage;
            }
            // 将相机所拍摄图片保存在临时目录
            getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(H5Constant.bigPicPath)));
            // 添加隐士intent跳转的判断,若没有对应的activity,则不做处理
            if (getImageByCamera.resolveActivity(h5Activity.getPackageManager()) != null) {
                h5Activity.startActivityForResult(getImageByCamera, H5Constant.h5CameraCode);
            }
        } else {
            Toast.makeText(h5Activity, "请确认已经插入SD卡", Toast.LENGTH_LONG).show();
        }
    }
首先是判断SD卡的状态,然后创建打开相机的Intent对象,同时设置参数将相机拍照的相片保存在指定目录用于我们后期的裁剪,旋转,压缩等操作;

这里有一个需要注意的地方时,在使用友盟测试的时候,部分机型调用如下代码会报错:

Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");


这是由于不同的系统修改的隐式intent的字符串值....,所以我们尽量使用系统的常量,并增加判断,该intent是否有对应的activity:

if (getImageByCamera.resolveActivity(h5Activity.getPackageManager()) != null) {
                h5Activity.startActivityForResult(getImageByCamera, H5Constant.h5CameraCode);
            }
若存在则打开对应的activity;

这时候会打开相机,我们可以选择拍照,拍照完成之后的处理逻辑,我们需要回调当前activity的onActivityResult方法:

// 打开相机解析图片
        if (requestCode == H5Constant.h5CameraCode && resultCode == RESULT_OK) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap bitmapTemp = null;
                    if (TextUtils.isEmpty(H5Constant.bigPicPath)) {
                        H5Constant.bigPicPath = SysConfig.SD_IMAGE_PATH + H5Constant.tempImage;
                    }
                    if (H5Constant.bigPicPath != null) {
                        // 压缩图片的宽高
                        int photoWidth = 320, photoHeight = 480;
                        bitmapTemp = BitmapUtils.getInSampleBitmap(H5Constant.bigPicPath, photoWidth, photoHeight);
                    }
                    if (bitmapTemp != null) {
                        // 判断图片是否需要旋转角度,若需要的话则旋转
                        bitmapTemp = BitmapUtils.rotateBitmapByDegree(bitmapTemp, BitmapUtils.getBitmapDegree(H5Constant.bigPicPath));
                        if (bitmapTemp != null) {
                            if (TextUtils.isEmpty(H5Constant.picPath)) {
                                H5Constant.picPath = SysConfig.SD_IMAGE_PATH + H5Constant.compressImage;
                            }
                            BitmapUtils.saveImage(bitmapTemp, H5Constant.picPath);
                            try {
                                if (!TextUtils.isEmpty(H5Constant.cameraCallback)) {
                                    ...
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (bitmapTemp != null && !bitmapTemp.isRecycled()) {
                                bitmapTemp.recycle();
                            }
                        }
                    }
                }
            }).start();


这里需要注意一个地方:我们需要判断resultCode是否为RESULT_OK,当resultCode为RESULT_OK时,拍照成功,若resultCode不是RESULT_OK则说明用户并没有拍照而是直接返回,这种情况下我们暂时不做任何处理;

(一)由于裁剪,旋转,压缩操作比较多,所以暂时在子线程中执行;

(二)裁剪功能

if (H5Constant.bigPicPath != null) {
                        // 压缩图片的宽高
                        int photoWidth = 320, photoHeight = 480;
                        bitmapTemp = BitmapUtils.getInSampleBitmap(H5Constant.bigPicPath, photoWidth, photoHeight);
                    }
压缩比例为320 * 480

public static Bitmap getInSampleBitmap(String filePath, int width, int height) {
<span style="white-space:pre">	</span>// 将dp转换为px
        int widthNeed = (int) (width * DisplayUtil.density);
        int heightNeed = (int) (height * DisplayUtil.density);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(filePath, options);
        int originalWidth = options.outWidth;
        int originalHight = options.outHeight;
<span style="white-space:pre">	</span>// 获取裁剪比例
        int inSample;
        if (originalWidth >= originalHight) {
            inSample = getInsampleSize(originalWidth, widthNeed);
        } else {
            inSample = getInsampleSize(originalHight, heightNeed);
        }
        options.inJustDecodeBounds = false;
        options.inSampleSize = inSample;
<span style="white-space:pre">	</span>// 执行裁剪操作
        return BitmapFactory.decodeFile(filePath, options);
    }


(三)旋转操作

// 判断图片是否需要旋转角度,若需要的话则旋转
bitmapTemp = BitmapUtils.rotateBitmapByDegree(bitmapTemp, BitmapUtils.getBitmapDegree(H5Constant.bigPicPath));


获取图片的旋转角度

/**
     * 读取图片的旋转的角度
     *
     * @param path 图片绝对路径
     * @return 图片的旋转角度
     */
    public static int getBitmapDegree(String path) {
        int degree = 0;
        try {
            // 从指定路径下读取图片,并获取其EXIF信息
            ExifInterface exifInterface = new ExifInterface(path);
            // 获取图片的旋转信息
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }


旋转图片

/**
     * 将图片按照某个角度进行旋转
     *
     * @param bm     需要旋转的图片
     * @param degree 旋转角度
     * @return 旋转后的图片
     */
    public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
        Bitmap returnBm = null;

        // 根据旋转角度,生成旋转矩阵
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        try {
            // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
            returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
        } catch (OutOfMemoryError e) {
        }
        if (returnBm == null) {
            returnBm = bm;
        }

        if (bm != null && bm != returnBm) {
            bm.recycle();
        }

        return returnBm;
    }


(四)压缩图片

BitmapUtils.saveImage(bitmapTemp, H5Constant.picPath);


压缩图片并保存

/**
     * 保存并压缩图片
     *
     * @param photo
     * @param spath
     * @return
     */
    public static boolean saveImage(Bitmap photo, String spath) {
        try {
            File mPhotoFile = new File(spath);
            if (!mPhotoFile.exists()) {
                mPhotoFile.createNewFile();
            }
            photo = BitmapUtils.getInSampleBitmapByBitmap(photo);
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream(spath, false));
            photo.compress(Bitmap.CompressFormat.JPEG, 50, bos);
            bos.flush();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


1)采用50%的压缩比例

2)经测试在1300W像素的手机上(主流手机后置摄像头目前均未1300W)

原始图片

像素:4208 * 2368

大小:1.32M

旋转角度:0度;

经过裁剪,旋转,压缩之后

像素:1052 * 592

大小:51k

旋转角度:0度;

3)需要注意的是经过压缩之后图片信息所保存的Exif信息丢失
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: