Android-图片的选择,裁剪,压缩,适配高版本

趁着国庆有时间我们来聊一聊最常用的选取用户图片一系列的功能,go!

效果展示 效果展示链接

1.图片的选取

拍照

我们之前设置拍照保存的文件地址的Uri,都是直接Intent.putExtra(MediaStore.EXTRA_OUTPUT,文件保存的Uri路径),但是7.0之后,对用户权限提高了保护,之前那种方式行不通了,所以我们要做7.0的判断,用FileProvider获取设置保存的文件Uri,然后放到Intent.putExtra(MediaStore.EXTRA_OUTPUT,文件保存的Uri路径)中,代码如下:

 //相机拍照的一个标识,后面用
                TAKEPAHTO = 1;
                // 启动系统相机
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                //7.0以下设置保存图片的地址
                Uri norTakePhotoSaveAdr;
                // 判断7.0android系统
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    //临时添加一个拍照权限
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    //  //通过FileProvider获取保存图片file的uri(先创建file,然后获取File的Uri)
                    takePhotoSaveAdr = FileProvider.getUriForFile(MainActivity.this,
                            "com.hxzk.bj.photodemo", new File(Environment.getExternalStorageDirectory(), "savephoto.jpg"));
                    //MediaStore.EXTRA_OUTPUT-此设置需要一个保存视频的路径和文件名的Uri
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, takePhotoSaveAdr);
                } else {
                    norTakePhotoSaveAdr = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "savephoto.jpg"));
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, norTakePhotoSaveAdr);
                }
                //PHOTO_TAKEPHOTO,相机的一个请求码,返回时要用
                startActivityForResult(intent, PHOTO_TAKEPHOTO);

相册

相比较拍照,相册要简单一点,代码中都有注释,直接看:

  //拍照的一个表示
                TAKEPAHTO = 0;
                //调用系统图库,选择图片
                //Intent.ACTION_PICK 意思是选择数据,其具体表达有:
                // Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                //intent.setType("image/*"); 获取本地图片
                // intent.setType("video/*");  获取本地视频
                //intent.setType("audio/*")  获取本地音乐

                // Intent intent = new Intent(Intent.ACTION_PICK);
                //  intent.setType(ContactsContract.Contacts.CONTENT_TYPE); //获取联系人
                // startActivityForResult(intent, PICK_CONTACT);

                //第二种写法
                Intent intent = new Intent(Intent.ACTION_PICK, null);
                //其中External为sdcard下的多媒体文件,Internal为system下的多媒体文件。
                //使用INTERNAL_CONTENT_URI只能显示存储在内部的照片
                intent.setDataAndType(
                        MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
                //返回结果和标识
                startActivityForResult(intent, PHOTO_PHOTOALBUM);

.拍照或选取图片的返回结果

不论是拍照还是选取图片,我们都要在Activity的onActivityResult中去获取返回结果,然后进行下一步操作。这里提一下,我们在跳转相册或拍照都有一个requestCode,如图: startActivityForResult(intent, PHOTO_PHOTOALBUM); 这个,我们全局定义了三个,分别是拍照,相册,裁剪的标识:

 //三个常量全局标识
    //图库
    private static final int PHOTO_PHOTOALBUM = 0;
    //拍照
    private static final int PHOTO_TAKEPHOTO = 1;
    //裁剪
    private static final int PHOTO_PHOTOCLIP = 2;

在onActivityResult中就要用来区分了:

image.png

没啥说的!

2. 图片的裁剪和压缩

图片的裁剪

图片的裁剪我们主要看一下starPhotoZoom()这个裁剪方法,代码如下:

 public void startPhotoZoom(Uri uri) {
        Log.e("uri=====", "" + uri);
        //com.android.camera.action.CROP,这个action是调用系统自带的图片裁切功能
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");//裁剪的图片uri和图片类型
        intent.putExtra("crop", "true");//设置允许裁剪,如果不设置,就会跳过裁剪的过程,还可以设置putExtra("crop", "circle")
        intent.putExtra("aspectX", 1);//裁剪框的 X 方向的比例,需要为整数
        intent.putExtra("aspectY", 1);//裁剪框的 Y 方向的比例,需要为整数
        intent.putExtra("outputX", 60);//返回数据的时候的X像素大小。
        intent.putExtra("outputY", 60);//返回数据的时候的Y像素大小。
        //uritempFile为Uri类变量,实例化uritempFile
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (TAKEPAHTO == 1) {//如果是7.0的拍照
                //开启临时访问的读和写权限
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                //针对7.0以上的操作
                intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, uri));
                uriClipUri = uri;
            } else {//如果是7.0的相册
                //设置裁剪的图片地址Uri
                uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg");
            }

        } else {
            uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg");
        }
        Log.e("uriClipUri=====", "" + uriClipUri);
        //Android 对Intent中所包含数据的大小是有限制的,一般不能超过 1M,否则会使用缩略图 ,所以我们要指定输出裁剪的图片路径
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uriClipUri);
        intent.putExtra("return-data", false);//是否将数据保留在Bitmap中返回
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//输出格式,一般设为Bitmap格式及图片类型
        intent.putExtra("noFaceDetection", true);//人脸识别功能
        startActivityForResult(intent, PHOTO_PHOTOCLIP);//裁剪完成的标识

    }

图片的压缩

我直接上方法吧:

    /**
     * 图片压缩的方法
     */
    public void compressPhto(File mFile){
//        BitmapFactory这个类就提供了多个解析方法(decodeResource、decodeStream、decodeFile等)用于创建Bitmap。
//        比如如果图片来源于网络,就可以使用decodeStream方法;
//        如果是sd卡里面的图片,就可以选择decodeFile方法;
//        如果是资源文件里面的图片,就可以使用decodeResource方法等
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 获取当前图片的边界大小
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;
        String outMimeType = options.outMimeType;
        options.inJustDecodeBounds = false;
        //inSampleSize的作用就是可以把图片的长短缩小inSampleSize倍,所占内存缩小inSampleSize的平方
        options.inSampleSize = caculateSampleSize(options, 10, 10);
        Bitmap bitmap = BitmapFactory.decodeFile(mFile.getPath(),options);
        ivUserPhoto.setImageBitmap(bitmap);
    }

    /**
     * 计算出所需要压缩的大小
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int sampleSize = 1;
        int picWidth = options.outWidth;
        int picHeight = options.outHeight;
        if (picWidth > reqWidth || picHeight > reqHeight) {
            int halfPicWidth = picWidth / 2;
            int halfPicHeight = picHeight / 2;
            while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) {
                sampleSize *= 2;
            }
        }
        return sampleSize;
    }

3.图片的适配

7.0适配问题

不知道,大家注意裁剪方法里的这一段代码没?

image.png

还有拍照的这一段代码:

image.png

为什么要这么写呢? 原来7.0一下版本我们,直接调用相机获取的图片地址是: file:///storage/emulated/0/temp.jpg的文件 然而7.0之后就变成: content://........文件,使用 content://代替了 file:/// 这是因为:Android 为了提高私有文件的安全性,从 7.0 开始对外传递file://类型的uri会触发FileUriExposedException。因此,在分享私有文件时必须使用FileProvider。 那么如果在使用之前的方法就会报错,我们要给程序在manifest文件中加入FileProvider:

image.png

我们看看provider_paths这个文件中都有啥?

image.png

这注解写的,不会,就转行吧。

裁剪完成

直接加载图片显示

image.png

4.图片的上传

上传图片 但还有中情况是我们要上传加载的图片,我也给大家提供了方法:

Bitmap photoBitmap;
File file;

/**
 * 上传图片
 */
public void upDateFile() {
    try {
        //裁剪后的图像转成BitMap
        photoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriClipUri));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //创建路径
    String path = Environment.getExternalStorageDirectory()
            .getPath() + "/Pic";
    //获取外部储存目录
    file = new File(path);
    //创建新目录, 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
    file.mkdirs();
    //以当前时间重新命名文件
    long time = System.currentTimeMillis();
    //生成新的文件
    file = new File(file.toString() + "/" + time+ ".png");
    //创建输出流
    OutputStream out = null;
    try {
        out = new FileOutputStream(file.getPath());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //压缩文件,返回结果,true说明成功了
   boolean bCompress = photoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
}

4.其他知识点

  • 点击用户头像弹窗不是我们常用的PupUpWindow,而是自定义的BottomSheetDialog(待优化).
  • 用户头像被我做成了圆角,这个也是自定义ImageView,大家可以下载查看源码。

暂时这么多,以后补充! Github地址

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

React Native控件之ListView

概述 ListView作为核心组件之一,主要用于高效地显示一个可以垂直滚动的变化的数据列表。经过自定义组装,我们还可以用它实现九宫格等页面效果。 在Reac...

29570
来自专栏QQ音乐技术团队的专栏

轻听变色之谜

轻听是一款小而美的 Android 本地音乐播放器,而它的特点之一就是拥有多彩的外衣,那么轻听是如何实现变色的呢?

42400
来自专栏Android Note

Android上的自定义字体 - 通过XML进行动态字体选择

14260
来自专栏Bingo的深度学习杂货店

HTML5新特性

本章的主要内容有: ---- [1] 用于媒体回放的 video 和audio 元素 [2] HTML5拖放 [3] canvas简单应用 [4] Web存储:...

57050
来自专栏Android开发指南

10.多媒体

41180
来自专栏24K纯开源

Android开发中的全屏背景显示方案

引子     不管是Android还是iOS平台中,都可以看到一些应用在启动的时候会先出现一个启动画面(Splash Activity),如QQ、微信等。这个启...

27650
来自专栏程序员的诗和远方

在React Native中优雅的使用iconfont

React Native大火大热,其中为了解决图标,易于修改,换颜色,高清等需求,iconfont的应用更是必不可少。 React Native中的ic...

62840
来自专栏iOSer成长记录

iOS-CoreImage滤镜效果预览

17030
来自专栏游戏杂谈

as3与php 上传多张图片demo

2、在一次添加的图片中如果超出最大上传数,忽略本次选中的所有图片(又得重新选一次,此现象普通存在于目前各大网站的flash批量上传中)

21820
来自专栏阮一峰的网络日志

HTML代码简写法:Emmet和Haml

HTML代码写起来很费事,因为它的标签多。 ? 一种解决方法是采用模板, 在别人写好的骨架内,填入自己的内容。还有一种就是我今天想要介绍的方法----简写法。 ...

45550

扫码关注云+社区

领取腾讯云代金券