Android 选择图片拍照裁剪压缩整理

之前的项目中别人做好了头像拍照,选择图片这些操作,没有详细的了解这一块的内容,这几天刚好遇见了这块的东西,前后查找了一些资料,差不多弄明白了些,这里做一个记录。

一般项目中如头像这样的一些图片选择我们都直接调用系统的相机和图片库来操作,所以从这个调用到剪裁一起在捋一捋。

1.如何调用

分析:

Abdroid 系统提供了通过intent 方式访问系统相机和图片库的ACTION,因此我们可以轻松的获取图片。

我们采用Uri的方式返回图片,因此提前写一个Uri。

 imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "image.jpg"));

选择图片方式:

Intent selectIntent = new Intent(Intent.ACTION_PICK, null);
selectIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(selectIntent, SELECT_IMAGE);//选择图片方式,返回一个data,Uri
//注意:选择图片的结果返回时Uri,在onActivityForResult中获取:Uri uri = data.getData();

拍照方式:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//拍照传一个uri
startActivityForResult(intent, CAMERA_IMAGE);

上述方式将图片选出来或者拍照保存成文件保存在Uri中的路径中。然后我们在onActivityResult()方法中根据请求码进行相应的操作。这里先给出代码。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.e(TAG, "data==" + data);
        if (requestCode == CAMERA_IMAGE && resultCode == RESULT_OK) {//拍照
            int rotationAngle = getCameraPhotoOrientation(this, imageUri, imageUri.getPath());
            Log.e(TAG, "旋转到角度1:" + rotationAngle);
            try {
                rotateImage(imageUri.getPath());
                iv_image.setImageURI(imageUri);
            } catch (IOException e) {
                e.printStackTrace();
            }
            int rotationAngle2 = getCameraPhotoOrientation(this, imageUri, imageUri.getPath());
            Log.e(TAG, "旋转到角度2:" + rotationAngle2);
        } else if (requestCode == SELECT_IMAGE && resultCode == RESULT_OK) {//选择图片工具返回一个uri,不能获取path
            Uri uri = data.getData();//此处选择图片返回了uri和物流客一样,公司电脑的uri是自己写的??
            Log.e(TAG, "选择图片结果:" + uri);
            cropImageUri(uri, 600, 600, CROP_IMAGE);
            iv_image.setImageURI(uri);
        } else if (requestCode == CROP_IMAGE && resultCode == RESULT_OK && data != null) {

            Bitmap bitmap = data.getParcelableExtra("data");
            Log.e(TAG, "裁剪图片结果1" + data.getData());
            Log.e(TAG, "裁剪图片结果2" + bitmap);//返回结果史bitmap
            if (bitmap != null) {
                iv_image.setImageBitmap(bitmap);
            } else {
                iv_image.setImageURI(data.getData());
            }
        }
    }
上述代码分别处理了拍照,选择图片以及图片等裁剪工作,正常情况下这样就可以获取到我们需要的结果了。但是我们在项目中可能遇见其他问题。比如:三星手机拍照上传到服务器后你在获取下来发现图片可能被旋转来90度,拍照的图片太大,上传过程可能失败等。

对于图片出现了旋转,可以在上传前做一个处理,获取到图片旋转角度,然后给他转回来。通过一个方法操作:

   /**
     *  获取图片的旋转角度
     * @param context
     * @param imageUri
     * @param imagePath
     * @return
     */
    public static int getCameraPhotoOrientation(Context context, Uri imageUri, String imagePath) {
        int rotate = 0;
        try {
            context.getContentResolver().notifyChange(imageUri, null);
            File imageFile = new File(imagePath);
            ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
            int orientation = exif.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_UNDEFINED);
            switch (orientation) {
                case ExifInterface.ORIENTATION_NORMAL:
                    rotate = 0;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rotate;
    }
进行旋转:

  /**
     * @param file
     * @throws IOException
     */
    public void rotateImage(String file) throws IOException {

        BitmapFactory.Options bounds = new BitmapFactory.Options();
        bounds.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(file, bounds);

        BitmapFactory.Options opts = new BitmapFactory.Options();
        Bitmap bm = BitmapFactory.decodeFile(file, opts);

        File file1 = new File(file);
        int rotationAngle = getCameraPhotoOrientation(this, Uri.fromFile(file1), file);
        Log.e(TAG, "旋转到角度:" + rotationAngle);
        Matrix matrix = new Matrix();
        matrix.postRotate(rotationAngle, (float) bm.getWidth() / 2, (float) bm.getHeight() / 2);
        Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bounds.outWidth, bounds.outHeight, matrix, true);
        FileOutputStream fos = new FileOutputStream(file);
        rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        fos.flush();
        fos.close();
    }

经过旋转后,上传服务器拿到就不会在被旋转了。

2.图片裁剪压缩

分析:上面完成了从图库获取一个图片和拍照的操作,但是这样拿到的图片一般比较大,尤其是拍照,现在的手机像素都比较大,照片可以达到5M多大小,一般我们没有必要这样大的图片,而且太大了上传也容易失败。有两种操作方式可以对图片进行处理,裁剪和压缩。

裁剪:这种可以用在如头像这样的操作中,裁剪出一个部分来就可以了,裁剪也可以调用系统操作。

这里可能对于调用系统的Intent的参数有不明白,查资料给出一些裁剪Intent的参数:

附加选项数据类型描述
cropString发送裁剪信号
aspectXintX方向上的比例
aspectYintY方向上的比例
outputXint裁剪区的宽
outputYint裁剪区的高
scaleboolean是否保留比例
return-databoolean是否将数据保留在Bitmap中返回
dataParcelable相应的Bitmap数据
circleCropString圆形裁剪区域?
MediaStore.EXTRA_OUTPUT ("output")URI将URI指向相应的file:///...,详见代码示例
其中MediaStore.EXTRA_OUTPUT以及return-data选项不好理解,大致含义是:我们有两个方式获取到裁剪的图片结果,一种是Bitmap形式, 一种是传一个Url,最后还是一个Uri。采用哪一种方式裁剪,看我们的需要。一般头像的小图可以用返回Bitmap的形式,大图最好用Uri形式,方式内存溢出。

1.返回一个bitmap

这种形式 主要设置:

intent.putExtra("return-data", true); 
//intent.putExtra(MediaStore.EXTRA_OUTPUT, uri_big);//这个情况可以不设置输出的Uri
2.返回一个Uri

设置:

intent.putExtra("return-data", true); 
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri_big);
需要设置两个,return-date 为true,并且添加一个uri,最后在onActivityForResult中获取到Uri。其他参数一样


以上设置参数对于从图库选择图片和拍照获取图片时一样的

一种裁剪方式:

  /**
     * 裁剪图片
     *
     * @param uri
     * @param outputX     输入宽
     * @param outputY     输入高
     * @param requestCode
     */
    private void cropImageUri(Uri uri, int outputX, int outputY, int requestCode) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 2);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", outputX);
        intent.putExtra("outputY", outputY);
        intent.putExtra("scale", true);
        //intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//添加一个输入的uri
        intent.putExtra("return-data", true);//裁剪方式:true表示返回一个bitmap,false需要上面的uri
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection
        Log.e(TAG, "裁剪图片");
        startActivityForResult(intent, requestCode);
        String s = new String();
    }

上述裁剪,输入了一个图片的Uri,设置一个裁剪的宽和高, 最后返回一个Bitmap,我们在onActivityForresult中可以获取到。

Bitmap bitmap = data.getParcelableExtra("data");
如果需要,最后在保存Bitmap为文件上传。

    /**
     * 把一个bitmap 保存成File
     * @param b Bitmap
     * @return 图片存储的位置
     * @throws
     */
    public static String saveImg(Bitmap b,String name) throws Exception{
        String path = Environment.getExternalStoragePublicDirectory("longlong").getAbsolutePath();
        File mediaFile = new File(path + File.separator + name + ".jpg");
        if(mediaFile.exists()){
            mediaFile.delete();
        }
        if(!new File(path).exists()){
            new File(path).mkdirs();
        }
        mediaFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(mediaFile);
        b.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
        b.recycle();
        Log.e(TAG,"压缩后的文件:"+mediaFile.getAbsolutePath());
        b = null;
        System.gc();
        return mediaFile.getPath();
    }

上述选择图片和裁剪在onActivityForResult中的处理,显示在一个ImageView上

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case SELECT_IMAGE:{//选择图片
                if(data.getData()!=null){
                    cropImageUri(data.getData(),800,800,SHOW_IMAGE);
                }
                break;
            }
            case CROP_IMAGE:{//开始裁剪
                cropImageUri(uri,800,800,SHOW_IMAGE);
                break;
            }
            case SHOW_IMAGE:{//裁剪后显示图片
                if(data!=null ){
                    Bitmap bitmap = data.getParcelableExtra("data");
                    if(bitmap!=null){
                        imageView.setImageBitmap(bitmap);
                    }else{
                        String path = data.getData().getPath();
                        imageView.setImageURI(data.getData());
                    }
                }
                break;
            }
        }
    }

结果:(上图选择,下图拍照)



压缩:

方式一:采用比例压缩或者质量压缩

这里采用其中的一个方式:

   /**
     * 压缩图片保存返回一个路径
     * @param oldPath
     * @param bitmapMaxWidth
     * @return
     * @throws Exception
     */
    private String getThumbUploadPath(String oldPath,int bitmapMaxWidth) throws Exception {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(oldPath, options);
        int height = options.outHeight;
        int width = options.outWidth;
        int reqHeight = 0;
        int reqWidth = bitmapMaxWidth;
        reqHeight = (reqWidth * height)/width;
        // 在内存中创建bitmap对象,这个对象按照缩放大小创建的
        options.inSampleSize = calculateInSampleSize(options, bitmapMaxWidth, reqHeight);
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeFile(oldPath, options);
        Bitmap bbb = compressImage(Bitmap.createScaledBitmap(bitmap, bitmapMaxWidth, reqHeight, false));
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                .format(new Date());
        return saveImg(bbb,timeStamp);
    }


方式二:

采用开源压缩实现,这种方式直接使用。

例如:鲁班算法

https://github.com/Curzibn/Luban

具体实现:

1.添加依赖:

compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
compile 'top.zibin:Luban:1.0.9'

2.单个文件压缩

Luban.get(this)
    .load(File)                     //传人要压缩的图片
    .putGear(Luban.THIRD_GEAR)      //设定压缩档次,默认三挡
    .setCompressListener(new OnCompressListener() { //设置回调

        @Override
        public void onStart() {
            // TODO 压缩开始前调用,可以在方法内启动 loading UI
        }
        @Override
        public void onSuccess(File file) {
            // TODO 压缩成功后调用,返回压缩后的图片文件,这个文件保存在内部的一个缓存文件中,可以直接获取这个文件上传服务器
        }

        @Override
        public void onError(Throwable e) {
            // TODO 当压缩过去出现问题时调用
        }
    }).launch();    //启动压缩

其他关于鲁班的使用方法参考原作者。


其他参考:

https://my.oschina.net/ryanhoo/blog/86843

http://ryanhoo.github.io/blog/2014/06/03/the-ultimate-approach-to-crop-photos-on-android-2/

http://ryanhoo.github.io/blog/2014/06/03/the-ultimate-approach-to-crop-photos-on-android-3/











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值