首页 > 代码库 > Android实现图片裁切

Android实现图片裁切

介绍

在应用开发中,如果涉及到个人信息,头像一般是不可避免的,类似这种情况,我们就需要用到图片裁切的功能,实现头像裁切,然后上传给服务器。

一般裁切的做法就是图层叠加选取框,然后根据坐标,计算裁切区域,通过图形函数裁切,既然了解大概原理,造轮子的事情就不做了,上github找开源库,发现了一个叫做edmodo/cropper的库,是原生实现的裁切。

地址:https://github.com/edmodo/cropper

但是使用后发现这个库还存以下两个主要问题,体验就很不好了。

1、图片太大会出现无法显示

2、图片过小又无法自适应

难道就没有更好的办法了?

又百度了一下,发现原来android的Intent已经自带有裁切的action了,而且体验非常好,只需要在Intent附上参数就可以实现相册/相机裁切图片。

地址:https://github.com/ryanhoo/PhotoCropper

原理

1、请看参数

技术分享

2、拍照裁切需要先将结果保存在本地,然后再读取保存的结果裁切,否则默认情况下拍照直接返回的结果是缩略图。

3、通过onActivityResult来处理结果。

实现

参考的项目是经过重构的,但我总觉得看的晕,所以demo就全部放在一个页面,加上了个人理解注释,方便学习。

CropParams类

package com.example.cropimage;import android.graphics.Bitmap;import android.net.Uri;import android.os.Environment;public class CropParams {    public static final String CROP_TYPE = "image/*";    public static final String OUTPUT_FORMAT = Bitmap.CompressFormat.JPEG            .toString();    public static final int DEFAULT_ASPECT = 1;    public static final int DEFAULT_OUTPUT = 300;    public Uri uri;    public String type;    public String outputFormat;    public String crop;    public boolean scale;    public boolean returnData;    public boolean noFaceDetection;    public boolean scaleUpIfNeeded;    public int aspectX;    public int aspectY;    public int outputX;    public int outputY;    public CropParams() {        uri = Uri.fromFile(Environment.getExternalStorageDirectory())                .buildUpon().appendPath("crop_cache_file.jpg").build();        type = CROP_TYPE;        outputFormat = OUTPUT_FORMAT;        crop = "true";        scale = true;        returnData = false;        noFaceDetection = true;        scaleUpIfNeeded = true;        aspectX = DEFAULT_ASPECT;        aspectY = DEFAULT_ASPECT;        outputX = DEFAULT_OUTPUT;        outputY = DEFAULT_OUTPUT;    }}

 

核心代码

package com.example.cropimage;import java.io.File;import java.io.FileNotFoundException;import android.support.v7.app.ActionBarActivity;import android.app.Activity;import android.app.AlertDialog;import android.app.Dialog;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.provider.MediaStore;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.widget.ImageView;import android.widget.Toast;public class MainActivity extends ActionBarActivity {    ImageView imageView1;    CropParams mCropParams = new CropParams();    public static final int REQUEST_CROP = 127;    public static final int REQUEST_CAMERA = 128;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageView1 = (ImageView) findViewById(R.id.imageView1);        // 弹出提示框选择照片        final String[] arrayFruit = new String[] { "拍照", "从相册选择照片" };        Dialog alertDialog = new AlertDialog.Builder(MainActivity.this)                .setIcon(R.drawable.ic_launcher)                .setItems(arrayFruit, new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        switch (which) {                        case 0:                            // 进入相机                            startActivityForResult(                                    buildCaptureIntent(mCropParams.uri),                                    REQUEST_CAMERA);                            break;                        case 1:                            // 进入相册                            startActivityForResult(                                    buildCropFromGalleryIntent(mCropParams),                                    REQUEST_CROP);                            break;                        default:                            break;                        }                    }                })                .setNegativeButton("取消", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        // TODO Auto-generated method stub                    }                }).create();        alertDialog.show();    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.main, menu);        return true;    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        if (resultCode == Activity.RESULT_CANCELED) {            Toast.makeText(this, "Crop canceled!", Toast.LENGTH_LONG).show();        } else if (resultCode == Activity.RESULT_OK) {            switch (requestCode) {            case REQUEST_CROP:                Log.d("cropImage", "Photo cropped!");                Toast.makeText(this, "Photo cropped!", Toast.LENGTH_LONG)                        .show();                imageView1.setImageURI(mCropParams.uri);                break;            case REQUEST_CAMERA:                Intent intent = buildCropFromUriIntent(mCropParams);                startActivityForResult(intent, REQUEST_CROP);                break;            }        }        super.onActivityResult(requestCode, resultCode, data);    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }    /**     * 结束后删除临时裁切图片,或者不删除,用来干别的。     */    @Override    protected void onDestroy() {        File file = new File(mCropParams.uri.getPath());        if (file.exists()) {            boolean result = file.delete();            if (result)                Log.i("cropImage", "Cached crop file cleared.");            else                Log.e("cropImage", "Failed to clear cached crop file.");        } else {            Log.w("cropImage",                    "Trying to clear cached crop file but it does not exist.");        }        super.onDestroy();    }    /**     * 创建裁切Intent     *      * @param action     *            操作     * @param params     *            参数     * @return     */    public static Intent buildCropIntent(String action, CropParams params) {        return new Intent(action, null)                .setDataAndType(params.uri, params.type)                // .setType(params.type)                .putExtra("crop", params.crop).putExtra("scale", params.scale)                .putExtra("aspectX", params.aspectX)                .putExtra("aspectY", params.aspectY)                .putExtra("outputX", params.outputX)                .putExtra("outputY", params.outputY)                .putExtra("return-data", params.returnData)                .putExtra("outputFormat", params.outputFormat)                .putExtra("noFaceDetection", params.noFaceDetection)                .putExtra("scaleUpIfNeeded", params.scaleUpIfNeeded)                .putExtra(MediaStore.EXTRA_OUTPUT, params.uri);    }    /**     * 这一步是在相机拍照完成之后调用,注意action。     *      * @param params     * @return     */    public static Intent buildCropFromUriIntent(CropParams params) {        return buildCropIntent("com.android.camera.action.CROP", params);    }    /**     * 创建相册裁切Intent     *      * @param params     *            奥秘全在params里     * @return     */    public static Intent buildCropFromGalleryIntent(CropParams params) {        return buildCropIntent(Intent.ACTION_GET_CONTENT, params);    }    /**     * 创建相机拍照Intent,由于相机拍照直接返回的是缩略图,所以一般的做法是拍照保存在本地之后,通过uri再读取一次     *      * @param uri     *            保存路径     * @return     */    public static Intent buildCaptureIntent(Uri uri) {        return new Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(                MediaStore.EXTRA_OUTPUT, uri);    }    /**     * 解析uri成bitmap     *      * @param context     * @param uri     * @return     */    public static Bitmap decodeUriAsBitmap(Context context, Uri uri) {        if (context == null || uri == null)            return null;        Bitmap bitmap;        try {            bitmap = BitmapFactory.decodeStream(context.getContentResolver()                    .openInputStream(uri));        } catch (FileNotFoundException e) {            e.printStackTrace();            return null;        }        return bitmap;    }}

完成之后看起来是这样的

技术分享   技术分享

 demo地址:

链接:http://pan.baidu.com/s/1c0xqxEw 密码:u2ot

 

参考:

http://ryanhoo.github.io/blog/2014/05/26/the-ultimate-approach-to-crop-photos-on-android-1/

Android实现图片裁切