Android 进阶技术篇专题我爱编程Android知识

Android 从相册选取图片,拍照、裁剪一篇就够了

2018-05-08  本文已影响141人  SwitchLife

开篇

  基于我们开App发过程中经常要选择图片(或拍照)并裁剪做为头像上传到服务器,我把选取图片、拍照、裁剪图片功能整理优化了,有了它你再也不用为这些芝麻小事所烦恼啦!这一篇就够了!

立即体验

扫描以下二维码下载体验App(从0.2.3版本开始,体验App内嵌版本更新检测功能):


JSCKit库传送门:https://github.com/JustinRoom/JSCKit

源码简析

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK)
            return;

        switch (requestCode) {
            case REQUEST_CODE_PIC_PHOTO://手机相册
                onPickPhotoResult(data.getData());
                break;
            case REQUEST_CODE_TAKE_PHOTO://拍照
                onTakePhotoResult(FileProviderCompat.getUriForFile(this, takePhotoTempFile), takePhotoTempFile);
                break;
            case REQUEST_CODE_PHOTO_CROP://裁剪
                onCropPhotoResult(data.getData(), cropPhotoTempFile);
                break;
        }
    }

在使用到Uri,请注意兼容Android7.0。FileProviderCompat.java

特别提示:
1、需要权限android.permission.READ_EXTERNAL_STORAGE

    public void openAlbum() {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(intent, REQUEST_CODE_PIC_PHOTO);
    }

特别提示:
1、需要权限android.permission.WRITE_EXTERNAL_STORAGEandroid.permission.CAMERA

    public void openCamera(File directory, String photoPathName) {
        //如果没有设置文件夹,拍照后则保存在sdk根目录的Pictures文件夹下面
        if (directory == null)
            directory = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);

        //如果没有设置图片名称,则根据当前系统时间设置默认的图片名称
        if (photoPathName == null || photoPathName.trim().length() == 0)
            photoPathName = getDefaultTakePhotoFileName(Bitmap.CompressFormat.JPEG);

        takePhotoTempFile = new File(directory, photoPathName);
        String takePhotoTempFilePath = takePhotoTempFile.getPath();
        //如果保存图片的文件夹还没有创建,则创建文件夹
        File dir = new File(takePhotoTempFilePath.substring(0, takePhotoTempFilePath.lastIndexOf(File.separator)));
        if (!dir.exists())
            dir.mkdirs();

        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProviderCompat.getUriForFile(this, takePhotoTempFile));
        startActivityForResult(cameraIntent, REQUEST_CODE_TAKE_PHOTO);
    }

特别提示:
1、需要权限android.permission.WRITE_EXTERNAL_STORAGE
2、如果有设置aspectX、aspectY或者outputX、outputY,那么它就是按比例裁剪;否则是自由裁剪
3、比例优先。举个栗子:如果有设置aspectX=4、aspectY=3、outputX=480、outputY=560,那么它裁剪时真正的outputX=480、outputY=360。

    /**
     * open system crop photo feature.
     * <br/>1、如果有设置aspectX、aspectY或者outputX、outputY,那么它就是按比例裁剪;否则是自由裁剪
     * <br/>2、比例优先。举个栗子:如果有设置aspectX=4、aspectY=3、outputX=480、outputY=560,那么它裁剪时真正的outputX=480、outputY=360。
     * <br/>Caller must ensure {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission.
     *
     * @param uri
     * @param config
     */
    public void cropPhoto(Uri uri,CropConfig config) {
        if (config == null)
            config = new CropConfig();
        Intent intent = new Intent("com.android.camera.action.CROP");
        FileProviderCompat.setDataAndType(intent, uri, "image/*", true);
        intent.putExtra(CropConfig.EXTRA_CROP, String.valueOf(config.isCrop()));
        int outputX = config.getOutputX();
        int outputY = config.getOutputY();
        int aspectX = config.getAspectX();
        int aspectY = config.getAspectY();
        boolean isOutputSet = outputX > 0 && outputY > 0;
        boolean isAspectSet = aspectX > 0 && aspectY > 0;

        if (isAspectSet) {
            intent.putExtra(CropConfig.EXTRA_ASPECT_X, aspectX);
            intent.putExtra(CropConfig.EXTRA_ASPECT_Y, aspectY);
            //如果有设置outputX、outputY,则根据aspectX、aspectY计算出合适的outputX、outputY
            if (isOutputSet) {
                if (aspectX == aspectY) {
                    outputX = outputY = Math.min(outputX, outputY);
                } else {
                    double ratioX = outputX * 1.0 / aspectX;
                    double ratioY = outputY * 1.0 / aspectY;
                    if (ratioX > ratioY) {//以outputY为标准
                        int avg = outputY / aspectY;
                        outputX = avg * aspectX;
                        outputY = avg * aspectY;
                    } else if (ratioX < ratioY) {//以outputX为标准
                        int avg = outputX / aspectX;
                        outputX = avg * aspectX;
                        outputY = avg * aspectY;
                    }
                }
                config.setOutputX(outputX);
                config.setOutputY(outputY);
            }
        }
        if (isOutputSet) {
            intent.putExtra(CropConfig.EXTRA_OUTPUT_X, outputX);
            intent.putExtra(CropConfig.EXTRA_OUTPUT_Y, outputY);
        }
        intent.putExtra(CropConfig.EXTRA_SCALE, config.isScale());
        intent.putExtra(CropConfig.EXTRA_CIRCLE_CROP, String.valueOf(config.isCircleCrop()));

        //如果没有设置文件夹,裁剪后则保存在sdk根目录的Pictures文件夹下面
        File directory = config.getDirectory();
        if (directory == null){
            directory = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
            config.setDirectory(directory);
        }

        //如果没有设置图片名称,则根据当前系统时间设置默认的图片名称
        String photoPathName = config.getPhotoPathName();
        if (photoPathName == null || photoPathName.trim().length() == 0){
            photoPathName = getDefaultCropPhotoFileName(config.getOutputFormat());
            config.setPhotoPathName(photoPathName);
        }
        cropPhotoTempFile = new File(directory, photoPathName);
        String cropPhotoTempFilePath = cropPhotoTempFile.getPath();
        //如果保存图片的文件夹还没有创建,则创建文件夹
        File dir = new File(cropPhotoTempFilePath.substring(0, cropPhotoTempFilePath.lastIndexOf(File.separator)));
        if (!dir.exists())
            dir.mkdirs();

        intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProviderCompat.getUriForFile(this, cropPhotoTempFile));
        intent.putExtra(CropConfig.EXTRA_RETURN_DATA, config.isReturnData());
        intent.putExtra(CropConfig.EXTRA_NO_FACE_DETECTION, config.isNoFaceDetection());
        intent.putExtra(CropConfig.EXTRA_OUTPUT_FORMAT, config.getOutputFormat().name());
        startActivityForResult(intent, REQUEST_CODE_PHOTO_CROP);
    }

看看CropConfig.java请参照注释:

    public static final String EXTRA_CROP = "crop";
    public static final String EXTRA_ASPECT_X = "aspectX";
    public static final String EXTRA_ASPECT_Y = "aspectY";
    public static final String EXTRA_OUTPUT_X = "outputX";
    public static final String EXTRA_OUTPUT_Y = "outputY";
    public static final String EXTRA_SCALE = "scale";
    public static final String EXTRA_CIRCLE_CROP = "circleCrop";
    public static final String EXTRA_RETURN_DATA = "return-data";
    public static final String EXTRA_NO_FACE_DETECTION = "noFaceDetection";
    public static final String EXTRA_OUTPUT_FORMAT = "outputFormat";

    private boolean crop;//发送裁剪信号(不一定有效)
    private int aspectX;//X方向上的比例
    private int aspectY;//Y方向上的比例
    private int outputX;//裁剪后的图片的宽。与裁剪框的宽没有半毛钱关系,不要搞混了!
    private int outputY;//裁剪后的图片的高。与裁剪框的高没有半毛钱关系,不要搞混了!
    private boolean scale;//是否保留比例(不一定有效)
    private boolean circleCrop;//是否是圆形裁剪区域(不一定有效)
    private boolean returnData;//是否将数据保留在Bitmap中返回
    private boolean noFaceDetection;//是否取消人脸识别

    private File directory;//裁剪后的图片的保存目录
    private String photoPathName;//裁剪后的图片名称,可以包含目录路径
    private Bitmap.CompressFormat outputFormat;//裁剪后的图片格式,系统默认为jpeg格式(不一定有效)。如果文件格是png,但图片格式可能是jpeg。

如何使用

使用请参照这里https://github.com/JustinRoom/JSCKit/blob/master/app/src/main/java/jsc/exam/jsckit/ui/PhotoActivity.java

篇尾

  如果在使用过程中有问题请给我留言,一起学习,一起进步!我是JustinEoyQQ:1006368252

人类的全部历史都告诫有智慧的人,不要笃信时运,而应坚信思想。 —— 爱献生

上一篇下一篇

猜你喜欢

热点阅读