Android 拍照或相册剪裁后取头像

引用

1、Android7.0 头像 拍照、照片裁剪
2、联合使用:Android 仿IOS的PopupWindow和通用BasePopupWindow搭建

截图

crop_img.png
croping.png

实现

1、功能步骤
  • 1.1、相机和相册使用到的权限
  • 1.2、打开相机、打开相册
  • 1.3、相机拍照回调、相册选取回调
  • 1.4、两种不同方式回调后的uri使用并开启剪裁
  • 1.5、剪裁图片回调、展示图片(各种样式)
  • 1.6、其他注意:android 7.0 后的共享文件FileProvider配置等
2、工具类
  • 2.1、此工具类不一定符合所有业务,可自行扩展或者弄成接口类来完成各种功能
  • 2.2、使用相机:注意android 7.0的fileprovider来获取uri,完成拍照存储
  • 2.3、使用相册:注意ACTION_PICK和ACTION_GET_CONTENT的区别,并在不同api下返回的uri值不一样导致“无法加载图片”的问题,代码中写了注释
  • 2.4、相机图片剪裁:注意要加上intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);作为图片来源加载在剪切图中
  • 2.5、相册图片剪裁:不用加【2.4】内容
  • 2.6、剪裁输出源:可直接intent.putExtra("return-data", true);返回直接是bitmap有可能导致oom,还是设置false,然后靠uri来指向输出源比较好
/**
 * Created by wujn on 2018/10/31.
 * Version : v1.0
 * Function: crop photo by camera or album
 */
public class CropPhotoHelper {
    private Activity instance;

    // 裁剪后图片的宽(X)和高(Y),320 X 320的正方形。
    private static int output_X = 480;
    private static int output_Y = 480;

    private String localUserImgName ;       //原始拍照图片
    private String localUserImgPath;
    private Uri takePhotoUri;               //拍照的uri

    private String localUserCropImgName;    //最后的剪裁图片
    private String localUserCropImgPath;
    private Uri localUserIconUri;           //用户头像图片uri

    private String parentDir;               //父目录


    public CropPhotoHelper(Activity instance, String localUserImgName, String localUserCropImgName, String parentDir){
        this.instance = instance;
        this.localUserImgName = localUserImgName;
        this.localUserImgPath = parentDir + localUserImgName;
        this.localUserCropImgName = localUserCropImgName;
        this.localUserCropImgPath = parentDir + localUserCropImgName;
        this.parentDir = parentDir;
    }


    /**
     * 拍照
     * uri = content://.fileprovider/external_storage_root/xxx/xxx.jpg
     * */
    public void takePhoto(boolean isDeleteOld, int requestCode){
        FileUtil.mkDir(parentDir);
        File tmpFile=new File(localUserImgPath); //固定的,用户图片

        //删除旧的
        if (isDeleteOld) {
            try {
                if (tmpFile.exists()) {
                    tmpFile.delete();//删除
                }
                tmpFile.createNewFile();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            takePhotoUri = Uri.fromFile(tmpFile);
        }else{
            takePhotoUri = FileProvider.getUriForFile(instance, Constant.FileProviderValue, tmpFile);
        }

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, takePhotoUri);
        //intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        //intent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
        //intent.putExtra("aspectX", 4);
        //intent.putExtra("aspectY", 3);
        //intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 120 * 1024);
        //intent.putExtra("return-data", true);
        instance.startActivityForResult(intent, requestCode);
    }


    /**
     * 打开相册
     * 1、Intent.ACTION_PICK,null :
     * uri=content://media/external/images/media/86916
     *
     * 2、Intent.ACTION_GET_CONTENT :
     *   (1)<=4.3 : uri 返回的是带文件路径的
     *   (2)>4.3 : uri 返回content://com.android.providers.media.documents/document/image:3951
     *       这样的,没有路径,只有图片编号的uri.这就导致接下来无法根据图片路径来裁剪的步骤了.
     * */
    public void openAlbum(int requestCode){
        //method 1
//        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//        intent.setType("image/*");

        //method 2
        Intent intent = new Intent(Intent.ACTION_PICK);
        //intent.setType("image*//*");
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");

        instance.startActivityForResult(intent,requestCode);//打开相册
    }



    /**
     * 从相机拍照的图片剪裁
     * */
    public void cropRawByCamera(int requestCode){
        LogUtil.i("cropRawByCamera inputUri="+ takePhotoUri.toString());
        Intent intent = getCropImgIntent();
        //输入原始图片:来源于拍照
        if(takePhotoUri != null){
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(takePhotoUri, "image/*");
        }
        instance.startActivityForResult(intent, requestCode);
    }

    /**
     * 从相册图片剪裁
     * */
    public void cropRawByAlbum(Uri inputUri, int requestCode){
        LogUtil.i("cropRawByAlbum inputUri="+ inputUri.toString());
        Intent intent = getCropImgIntent();
        //输入原始图片:来源于相册
        if(inputUri != null){
            //intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent.setDataAndType(inputUri, "image/*");
        }
        instance.startActivityForResult(intent, requestCode);
    }


    /**
     * 裁剪原始的图片意图
     */
    private Intent getCropImgIntent(){
        //剪裁action
        Intent intent = new Intent("com.android.camera.action.CROP");

        // 设置裁剪
        intent.putExtra("crop", "true");

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

        // outputX , outputY : 裁剪图片宽高
        intent.putExtra("outputX", output_X);
        intent.putExtra("outputY", output_Y);
        intent.putExtra("scale",true);

        // 返回类型和文件指向
        // return data is bitmap , maybe oom
        //intent.putExtra("return-data", true);             //返回bitmap
        intent.putExtra("return-data", false);  //返回uri

        //输出剪裁图片
        File cropFile = new File(localUserCropImgPath);
        try {
            if (cropFile.exists()){
                cropFile.delete();//删除
            }
            cropFile.createNewFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        localUserIconUri = Uri.fromFile(cropFile);
        if (localUserIconUri != null) {
            //intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, localUserIconUri);
        }

        //取消人脸识别
        intent.putExtra("noFaceDetection", true);
        //压缩图片
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());

        return intent;

    }

    /**
     * 获取剪切后的bitmap
     * */
    public Bitmap getCropBitmap(){
        try {
            return BitmapFactory.decodeStream(instance.getContentResolver().openInputStream(localUserIconUri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    //获取剪裁后的路径
    public String getLocalUserCropImgPath() {
        return localUserCropImgPath;
    }
}
3、使用
  • 3.1、各种回调请求的code
/**
     * 权限访问请求code
     * */
    public static final int REQUEST_PERMISSION_CODE = 1;
    public static final int REQUEST_PERMISSION_CODE_1 = 2;

    /**
     * ActivityResult回调的请求code
     * */
    public static final int REQUEST_TACKPIC_CODE = 10001;           //相机
    public static final int REQUEST_CHOOSE_PHOTO_CODE = 10002;      //相册
    public static final int REQUEST_CROP_IMAGE_CODE = 10003;        //剪裁图片
  • 3.2、使用相机和相册的权限询问
  • 3.3、按照【1】所说的步骤来使用
  • 3.4、图片最后使用了AvatarImageView来加载图片,这个剪裁图片加载就不再这里写了,可以使用的方法巨多...
//剪裁帮助类
    private CropPhotoHelper cropPhotoHelper;
    @BindView(R.id.avatar_img) AvatarImageView avatar_img;

    @Override
    protected void init() {
        //剪裁帮助类:原始图片,剪裁图片,父目录
        cropPhotoHelper = new CropPhotoHelper(instance,
                "img_org.jpg",
                "img_crop.jpg",
                Constant.IMG_DIR);
    }

    //拍照取图
    public void OnSelectCamera(View v){
        if (EasyPermissions.hasPermissions(instance, permissions_camear)) {
            cropPhotoHelper.takePhoto(false, Constant.REQUEST_TACKPIC_CODE);
        }else{
            EasyPermissions.requestPermissions(instance, "App 运行需要获取权限",Constant.REQUEST_PERMISSION_CODE, permissions_camear);
        }
    }
    //相册取图
    public void OnSelectAlbum(View v){
        if (EasyPermissions.hasPermissions(instance, permissions_album)) {
            cropPhotoHelper.openAlbum(Constant.REQUEST_CHOOSE_PHOTO_CODE);
        }else{
            EasyPermissions.requestPermissions(instance, "App 运行需要获取权限",Constant.REQUEST_PERMISSION_CODE_1, permissions_album);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                //拍照回调
                case Constant.REQUEST_TACKPIC_CODE:
                    LogUtil.d("take picture success");
                    cropPhotoHelper.cropRawByCamera(Constant.REQUEST_CROP_IMAGE_CODE);
                    break;

                //选择照片回调
                case Constant.REQUEST_CHOOSE_PHOTO_CODE:
                    LogUtil.d("choose picture success");
                    if (data != null) {
                        //不同api下获取相册图片的path
                        //String photoPath = AppUtil.getPhotoPath(instance, data);
                        cropPhotoHelper.cropRawByAlbum(data.getData(),Constant.REQUEST_CROP_IMAGE_CODE);
                    } else {
                        ToastUtil.showShort(instance,"相册无返回值!");
                    }
                    break;

                //剪裁完
                case Constant.REQUEST_CROP_IMAGE_CODE:
                    Bitmap bitmap = cropPhotoHelper.getCropBitmap();
                    if(bitmap != null){
                        avatar_img.setBitmap(bitmap);
                    }else{
                        ToastUtil.showShort(instance,"剪裁失败!");
                    }
                    break;

                default:
                    break;
            }
        }

    }



    //=============================================权限部分=====================================================
    //相机权限
    private String[] permissions_camear = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.CAMERA
    };
    //文件权限
    private String[] permissions_album = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }
    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        LogUtil.i("onPermissionsGranted requestCode="+requestCode);
        if(Constant.REQUEST_PERMISSION_CODE == requestCode){
            cropPhotoHelper.takePhoto(false, Constant.REQUEST_TACKPIC_CODE);
        }
        else if(Constant.REQUEST_PERMISSION_CODE_1 == requestCode){
            cropPhotoHelper.openAlbum(Constant.REQUEST_CHOOSE_PHOTO_CODE);
        }

    }
    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        DialogUtil.errorDialog(instance,"拒绝开启,请前往APP应用设置中打开此权限");
    }
哈哈,朝CV工程师在进一步!!!!
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容