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/

时间: 2024-10-25 22:01:07

Android实现图片裁切的相关文章

Android剪裁图片简单的方法

/** * 按正方形裁切图片 */ public static Bitmap ImageCrop(Bitmap bitmap) { int w = bitmap.getWidth(); // 得到图片的宽,高 int h = bitmap.getHeight(); int wh = w > h ? h : w;// 裁切后所取的正方形区域边长 int retX = w > h ? (w - h) / 2 : 0;//基于原图,取正方形左上角x坐标 int retY = w > h ? 0

Android 解决图片大量下载:软引用必须懂4点

Android 解决图片大量下载:软引用必须懂4点 可能对于Android开发者来说,软引用这个词有的会不是很熟悉,软引用在Java开发中用的比较多,但是,在Android开发中引用软引用,会给我们解决很多难题. AD: 1.对象的强.软.弱和虚引用 为了能更加灵活控制对象的生命周期,需要知道对象引用的4中级别,由高到低依次为 :强引用.软引用.弱引用和虚引用 备注: 这四种的区别: ⑴强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会

仿优酷Android客户端图片左右滑动(自动滑动)

最终效果: 页面布局main.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent

Android圆形图片--自定义控件

Android圆形图片控件效果图如下: 代码如下: RoundImageView.java package com.dxd.roundimageview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas

android获取图片的旋转角度

public static int getExifOrientation(String filepath) { int degree = 0; ExifInterface exif = null; try { exif = new ExifInterface(filepath); } catch (IOException ex) { Log.d(TAG, "cannot read exif" + ex); } if (exif != null) { int orientation =

android 拉伸图片

Android拉伸图片用的是9.png格式的图片,这种图片可以指定图片的那一部分拉伸,那一部分显示内容,美工给的小图片也能有很好的显示效果. 原背景图片 可以看到原背景图片很小,即使在再长的文字,背景图片的圆角也不会拉伸 制作###.9.png 打开Android 的sdk>tools>draw9patch.bat,将图片拖进去开始制作###.9.png 点击四周即可添加黑点,拖拉黑点可成线.图片四周的黑线和黑点都有不同的意思. 上面黑线或者点表示纵向可拉伸的区域 一般一个点即可 左边黑线或者

android 背景图片滚动

昨天在给客户端做天气展示页面的时候,发现很多app的天气页面背景图片都会缓慢移动,形成了一种3d的感觉.例如下雨,静态图片缓慢移动,雨滴位置变换感觉就真的在下雨.云朵的移动也很酷.于是研究了一下午.写了一个自定义view控件. 我的自定义控件继承了view,重写ondraw方法.本人C#转android才3个月,以下代码如有错或者有可以改进的地方,请各位在评论中指出.望不吝赐教! ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

android 背景图片的设置

在java文件中对控件设置背景图片 layout.setBackgroundDrawable(getResources().getDrawable(R.drawable.bgimage)) 在设置中,通过发送广播对整个布局中的背景进行更改. menu_bg1.setOnClickListener(new SendBroadcast()); private class SendBroadcast implements View.OnClickListener { @Override public

新修正的 Ajax PHP 图片裁切-来自www.srcfans.com开源代码

Ajax PHP Image Cut PHP图片裁切,图片裁切之生成缩略图部分和预览功能的实现,这是一个关于PHP Ajax裁切图片并在服务器端生成新图片的简单例子,这次不是jquery,而是使用另一个有名的JS插件:prototype,同样具有很好的性能.本图片裁切在 IE11 , FireFox 5 , Chrome55下测试通过(Opear 下有一点点问题,剪切框的初始位置不对,这个问题研究至今一直没解决掉,抱歉!). 在线演示:http://www.srcfans.com/demo/im