android拍照、图库与裁剪

一、前言

好多APP都有上传图片的功能,无聊是调起相册还是拍照,都必须要在activity的onActivityResult回调方法处理,实现起来比较繁琐。至此对其进一步封装。使用起来更加简单。

二、直接上源码:

GitHub源码

1、首先创建一个PicOptions,作用是配置相关信息

public class PicOptions {

    private boolean isCrop;//是否裁剪
    private int cropWidth;//裁剪宽度
    private int cropHeight;//裁剪高度
    private String providerAuthority;//7.0以上访问sdcard需要用到
    private File cacheFile;//缓存文件

    private PicOptions(Builder builder) {
        isCrop = builder.isCrop;
        cropWidth = builder.cropWidth;
        cropHeight = builder.cropHeight;
        providerAuthority = builder.providerAuthority;
        cacheFile = builder.cacheFile;
    }

    public boolean isCrop() {
        return isCrop;
    }

    public int getCropWidth() {
        return cropWidth;
    }

    public int getCropHeight() {
        return cropHeight;
    }

    public String getProviderAuthority() {
        return providerAuthority;
    }

    public File getCacheFile() {
        return cacheFile;
    }

    public static Builder beginBuilder() {
        return new Builder();
    }

    public static class Builder {

        private boolean isCrop = false;
        private int cropWidth = 0;
        private int cropHeight = 0;
        private String providerAuthority;
        private File cacheFile;

        public Builder setCrop(boolean crop) {
            isCrop = crop;
            return this;
        }

        public Builder setCropWidth(int cropWidth) {
            this.cropWidth = cropWidth;
            return this;
        }

        public Builder setCropHeight(int cropHeight) {
            this.cropHeight = cropHeight;
            return this;
        }

        public Builder setProviderAuthority(String providerAuthority) {
            this.providerAuthority = providerAuthority;
            return this;
        }

        public Builder setCacheFile(File cacheFile) {
            this.cacheFile = cacheFile;
            return this;
        }

        public PicOptions build() {
            if (this.cacheFile == null) {
                throw new IllegalArgumentException("cacheFile no found...");
            }
            return new PicOptions(this);
        }
    }
}

2、回调接口PicListener

public interface PicListener {

    void onTakePicSuccess(Uri uri);//成功

    void onTakePicFail();//失败
}

3、管理器PicManager

public class PicManager {

    private final String TAG = "PicManager";

    private final int REQUEST_CODE_CAMERA = 1009;//拍照
    private final int REQUEST_CODE_PICTURE = 1008;//相册
    private final int REQUEST_CODE_CUT_PIC = 1007;//裁剪

    private final String REQUEST_TYPE = "requestType";

    private PicOptions picOptions;
    private PicListener listener;

    public PicManager setPicOptions(PicOptions picOptions) {
        this.picOptions = picOptions;
        return this;
    }

    public PicManager setListener(PicListener listener) {
        this.listener = listener;
        return this;
    }

    public void takeCamera(Context context) {
        startPicActivity(context, REQUEST_CODE_CAMERA);
    }

    public void takePhotoAlbum(Context context) {
        startPicActivity(context, REQUEST_CODE_PICTURE);
    }


    private void startPicActivity(Context context, int type) {
        Intent intent = new Intent(context, PicActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(REQUEST_TYPE, type);
        context.startActivity(intent);
    }

    public void behavior(Activity activity, Intent intent, Bundle savedInstanceState) {
        if (intent == null) {
            YLogUtil.INSTANCE.eTag(TAG, "behavior intent is null");
            onDestroy(activity);
            return;
        }
        if (intent.getExtras() == null) {
            YLogUtil.INSTANCE.eTag(TAG, "behavior extras is null");
            onDestroy(activity);
            return;
        }
        if (savedInstanceState == null) {
            int requestType = intent.getExtras().getInt(REQUEST_TYPE);
            if (requestType == REQUEST_CODE_CAMERA) {
                executeCamera(activity);
            } else if (requestType == REQUEST_CODE_PICTURE) {
                executePhotoAlbum(activity);
            }
        }
    }

    private void executeCamera(Activity context) {

        String providerAuthority = context.getPackageName() + ".FileProvider";
        if (null != picOptions.getProviderAuthority() && !picOptions.getProviderAuthority().isEmpty()) {
            providerAuthority = picOptions.getProviderAuthority();
        }

        Uri imageUri;
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 判断版本大于等于7.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                imageUri = FileProvider.getUriForFile(context, providerAuthority, picOptions.getCacheFile());
            } catch (Exception e) {
                e.printStackTrace();
                imageUri = Uri.fromFile(picOptions.getCacheFile());
            }
            // 给目标应用一个临时授权
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            imageUri = Uri.fromFile(picOptions.getCacheFile());
        }
        // 指定调用相机拍照后照片的储存路径
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

        context.startActivityForResult(intent, REQUEST_CODE_CAMERA);
    }

    private void executePhotoAlbum(Activity context) {
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                "image/*");
        context.startActivityForResult(intent, REQUEST_CODE_PICTURE);
    }

    private void crop(Activity context, Uri uri) {

        YLogUtil.INSTANCE.iFormatTag(TAG, "crop uri:%s", uri);

        Intent intent = new Intent("com.android.camera.action.CROP");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        intent.setDataAndType(uri, "image/*");
        // crop为true是设置在开启的intent中设置显示的view可以剪裁
        intent.putExtra("crop", "true");

        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);

        // outputX,outputY 是剪裁图片的宽高
        intent.putExtra("outputX", picOptions.getCropWidth());
        intent.putExtra("outputY", picOptions.getCropHeight());
        intent.putExtra("return-data", false);

        // intent.putExtra(MediaStore.EXTRA_OUTPUT,
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picOptions.getCacheFile()));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection

        context.startActivityForResult(intent, REQUEST_CODE_CUT_PIC);

    }

    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        if (listener == null || (requestCode != REQUEST_CODE_CAMERA && requestCode != REQUEST_CODE_PICTURE && requestCode != REQUEST_CODE_CUT_PIC)) {
            YLogUtil.INSTANCE.eTag(TAG, "onActivityResult Unknown requestCode or listener is null");
            onDestroy(activity);
            return;
        }
        if (resultCode != android.app.Activity.RESULT_OK) {
            YLogUtil.INSTANCE.eTag(TAG, "onActivityResult--cancel--resultCode", resultCode);
            onDestroy(activity);
            return;
        }
        switch (requestCode) {
            case REQUEST_CODE_CAMERA:
                YLogUtil.INSTANCE.iTag(TAG, "onActivityResult camera");
                scanFile(activity, picOptions.getCacheFile().getAbsolutePath(), picOptions.isCrop());
                break;
            case REQUEST_CODE_PICTURE:

                if (data == null || data.getData() == null) {
                    YLogUtil.INSTANCE.eTag(TAG, "onActivityResult picture data is null ");
                    listener.onTakePicFail();
                    onDestroy(activity);
                    return;
                }
                YLogUtil.INSTANCE.iTag(TAG, "onActivityResult picture");
                if (picOptions.isCrop()) {
                    crop(activity, data.getData());
                } else {
                    YLogUtil.INSTANCE.iFormatTag(TAG, "onActivityResult picture callback uri:%s", data.getData());
                    listener.onTakePicSuccess(data.getData());
                    onDestroy(activity);
                }
                break;
            case REQUEST_CODE_CUT_PIC:
                YLogUtil.INSTANCE.iTag(TAG, "onActivityResult crop");
                scanFile(activity, picOptions.getCacheFile().getAbsolutePath(), false);
                break;
        }
    }

    private void scanFile(final Activity activity, String path, final boolean isCrop) {
        MediaScannerConnection.scanFile(activity, new String[]{path}, new String[]{"image/jpeg"}, new MediaScannerConnection.OnScanCompletedListener() {
            @Override
            public void onScanCompleted(String path, final Uri uri) {
                HandlerUtil.INSTANCE.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (isCrop) {
                            crop(activity, uri);
                        } else {
                            YLogUtil.INSTANCE.iFormatTag(TAG, "scanFile camera or crop callback uri:%s", uri);
                            listener.onTakePicSuccess(uri);
                            onDestroy(activity);
                        }
                    }
                });
            }
        });
    }

    /**
     * 摧毁本库的 PicActivity
     */
    private void onDestroy(Activity activity) {
        if (activity != null) {
            activity.finish();
        }
    }
}

4、Pic

public class Pic {
    private PicManager picManager;

    private Pic() {
        picManager = new PicManager();
    }

    public static synchronized Pic getInstance() {
        return PicHolder.instance;
    }

    private static class PicHolder {
        private static final Pic instance = new Pic();
    }

    public PicManager getPicManager() {
        return picManager;
    }
}

5、真正调起拍照或相册的activity,PicActivity

public class PicActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //不接受触摸屏事件
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        Pic.getInstance().getPicManager().behavior(this, getIntent(), savedInstanceState);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Pic.getInstance().getPicManager().onActivityResult(this, requestCode, resultCode, data);
    }
}

6、注册PicActivity到manifest

 <activity
            android:name=".pic.PicActivity"
            android:launchMode="singleTask"
            android:theme="@style/Theme.Transparent" />

7、示例:

调起摄像头

Pic.getInstance().picManager.setPicOptions(getPicOptions()).setListener(listener).takeCamera(context)

调起相册

Pic.getInstance().picManager.setPicOptions(getPicOptions()).setListener(listener).takePhotoAlbum(context)

private PicOptions getPicOptions(){
        File cacheFile = File(FileUtils.getSdcardPath(), TimeUtil.nowTimeStr + ".jpg");
        return PicOptions.beginBuilder()
                .setCacheFile(cacheFile)
                .setCrop(checkBox.isChecked)
                .setCropHeight(200)
                .setCropWidth(200)
                .build();
    }

 

点赞