Android打开相册vs拍照获取图片的原理实现

前言:这几天在做用户登陆注册的逻辑时,遇到了要修改用户的头像问题的解决。在此把实现的原理以及实现过程中遇到的问题分享个大家...留下些许脚印

在手机的app里我们常常可用看到在个人中心页面有修改头像的功能 : 你可以选择拍照或从相册 选取图片来源。


1,拍照or从相册选择都是通过intent 的startActivityForResulr(intent,requestCode)方法来启动,在onActivityResutl()回调方法中获取图片存储路径。


2.拍照实现原理:当用户选择拍照时其可正常拍照或自拍方式来获取它想要的图片。这种方式是把拍下来的照片存储在SD卡里,通过调用系统相机创建一个Intent对象为其所在一个打开相机的action。然后在onActivityResutl()方法里获取到刚刚保存的图片的SD卡路径。然后跟即此图片路径你可以获取到他的bimmap或Picasso,Universal Image Loader.....等其他图片处理框架来设置到我们的ImageView控件上即可显示


3.从相册选择:此方式是通过Intent来打开系统相机。首先实列化一个Intent对象变设置其打开相机的action,调用startActivityForResulr(intent,requestCode)方法即可打开相册。然后在onActivityResutl()方法里通过getContentReslover()内容解析者来获取图片存储的cursor游标然后解析游标获取图片的路径,拿到图片路径后你就可以根据图片路径设置图片的显示啦。

所遇问题:

(1),在拍照时因为要把图片存储在SD卡里所以要手动用代码判断手机里是否挂载了SD卡。代码如下:

 //首先判断手机是否插入SD卡
String state = Environment.getExternalStorageState();//获取外部存储设备的存储状态
if (state.equals(Environment.MEDIA_MOUNTED)){

	//打开系统相机的相关代码....

  }else{
	Toast.makeText(getActivity(), "请确保已插入SD卡", Toast.LENGTH_SHORT).show();
  }
(2),当用户的手机系统为 android 6.0或6.0以上的系统时,需要用代码的方式动态的判断用户是否添加了读写SD卡的权限【拍照把图片写入SD卡,从相册选择相当于从SD里读取数据】,即便你在清单文件里添加了但如果你不用代码动态方式提示用户授权此权限,那头像就读取不到。而在6.0以下的系统里只需在AndroidMainfest.xml文件里添加即可。由此可见Google在不断的加强系统对安装应用的权限管理。这也是出于一种安全性的考虑。

代码如下:

//6.0系统对权限加强里管理。so 使用代码方式来实现 设置权限[6.0以下系统只需在清单文件里添加]
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //申请WRITE_EXTERNAL_STORAGE权限
            this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},22);//权限,请求码
        }

添加后可以看到此提示信息:

                                                     

4.重写activity的onRequestPermissionsResult()方法监听用户对权限的选择,在授予权限的if分支里进行拍照或打开相册的业务操作

 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户授予了此权限
            } else {
                // 用户拒绝了此权限
            }
        }
    }

点击始终允许即可显示你拍照的图片。关于Android6.0系统的权限添加相关资料大家可参考:http://www.open-open.com/lib/view/open1446693346826.html

5,最后帖上完整的代码:

 
    //弹出对话框让用户选择头像来源(拍照vs相册)
    private void showPhotoSelectDialog(){
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View view = UIUtils.inflater(R.layout.zdy_dailog);
        builder.setView(view);
        TextView paizhaoText = (TextView)view.findViewById(R.id.zdy_text1);
        TextView fromxzheText = (TextView)view.findViewById(R.id.zdy_text2);
        final AlertDialog theDialog = builder.create();
        paizhaoText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                theDialog.dismiss();
                fromPaizhao();
            }
        });
        fromxzheText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                theDialog.dismiss();
                fromXianche();
            }
        });
        theDialog.show();
    }

    private String image_from_sd_paizhao_or_xianche__path;//图片sd路径
    private boolean isuploadImage;//标记用户是否上传图片
    private String path; //sd卡路径

    /**
     * 注意:
     * 1.拍照和打开相册Android6.0系统后要动态授予应用写和读SD卡的权限,否则拍照失败/从相册获取图片失败或应用崩溃
     * 2.需重写onRequestPermissionsResult方法监听用户对权限的授予情况(拒绝vs允许),在允许的方法里执行相应的拍照或打开相册功能
     * 3.经测试,在允许了其中一个权限后下次再点击就无需申请了
     */
    private static int WRITE_SD_CODE = 1;
    private static int READ_SD_CODE = 2;
    @TargetApi(Build.VERSION_CODES.M)
    private void fromPaizhao() {
        //6.0以上系统(拍照时)动态申请写sd卡权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},WRITE_SD_CODE);//权限,请求码
        }else {
            paizhao();
            Log.v("TAG", "拍照.....");
        }
    }
    @TargetApi(Build.VERSION_CODES.M)
    private void fromXianche() {
        //6.0以上系统(打开相册时)动态申请读sd卡权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            this.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},READ_SD_CODE);
        }else {
            xianche();
            Log.v("TAG", "相册....");
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        Log.v("TAG", "权限requestCode:" + requestCode);
        if (requestCode == WRITE_SD_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                paizhao();
                Log.v("TAG", "授予权限");
            }else {
                Log.v("TAG", "拒绝权限");
            }
        }else if(requestCode==READ_SD_CODE){
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                xianche();
                Log.v("TAG", "授予权限");
            }else {
                Log.v("TAG", "拒绝权限");
            }
        }
    }
    /**
     *  解决android7.0拍照应用挂掉问题:http://www.cnblogs.com/netcorner/p/6542373.html
     * 1.安卓7.0遇到 android.os.FileUriExposedException: file:///storage/emulated.. exposed beyond app through Intent.getData()
     * 2.注意:4..2.2的系统拍照要用Uri.fromFile(file)(oppo手机测试结果),否则在拍照完成点击OK确认后在onActivityResult()方法里执行的是放弃拍照的if分支
     * 故要做系统版本判断
     */
    private void paizhao(){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        path = Environment.getExternalStorageDirectory().getPath() + "/";
        //将当前的拍照时间作为图片的文件名称
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String filename = simpleDateFormat.format(new Date()) + ".jpg";
        image_from_sd_paizhao_or_xianche__path = path + filename;
        File file = new File(image_from_sd_paizhao_or_xianche__path);
        //******************************************************************
        Uri photoURI;
        //解决三星7.x或其他7.x系列的手机拍照失败或应用崩溃的bug.解决4.2.2(oppo)/4.x系统拍照执行不到设置显示图片的bug
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ //7.0以下的系统用 Uri.fromFile(file)
            photoURI = Uri.fromFile(file);
        }else {                                            //7.0以上的系统用下面这种方案
            photoURI = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider",file);
        }
        //******************************************************************
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);//将图片文件转化为一个uri传入
        startActivityForResult(intent, 100);
    }
    //打开相册
    private void xianche(){
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, 200);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case 100://拍照
                if (resultCode==this.RESULT_OK){
                    if(image_from_sd_paizhao_or_xianche__path!=null) {
                        //UIL框架加载本地sd卡图片路径为 String imageFilePath = "file://" + image_from_sd_paizhao;
                        //Picssao用file来封装文件
                        File file = new File(image_from_sd_paizhao_or_xianche__path);
                        Picasso.with(this).load(file).into(circleImageView);
                        isuploadImage = true;
                        Log.v("TAG", "拍照获取的图片sd卡路径:" + image_from_sd_paizhao_or_xianche__path);
                    }
                }else{
                    ToastUtils.show("放弃拍照");
                }
                break;
            case 200://从相册
                if(resultCode==this.RESULT_OK) {
                    //内容解析者来操作内容提供最对数据的4方法
                    if (data!=null) {
                        Uri uri = data.getData();
                        if (uri!=null) {
                            Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                            //选择的就只是一张图片,所以cursor只有一条记录
                            if (cursor != null) {
                                if (cursor.moveToFirst()) {
                                    image_from_sd_paizhao_or_xianche__path = cursor.getString(cursor.getColumnIndex("_data"));//获取相册路径字段
                                    File file = new File(image_from_sd_paizhao_or_xianche__path);
                                    Picasso.with(this).load(file).into(circleImageView);
                                    isuploadImage = true;
                                    Log.v("TAG", "打开相册获取的图片sd卡路径:" + image_from_sd_paizhao_or_xianche__path);
                                }
                            }
                        }
                    }
                }else{
                    ToastUtils.show("放弃从相册选择");
                }
                break;
        }
    }

6,当手机系统为Android7.0. 打开相机拍照时应用会挂掉,报android.os.FileUriExposedException: file:///storage/emulated.. exposed beyond app through Intent.getData()
解决方案大家参照此篇文章: http://www.cnblogs.com/netcorner/p/6542373.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值