Android 多图选择上传

PictureSelector

项目地址: LuckSiege/PictureSelector
简介:最近项目中用到多图选择上传的需求,随后百度了一下用了别人写的 demo,发现在很多机型上各种不适,闪退等问题,严复影响使用,后面我自己写了一个,公司 20 几款手机全部通过,在腾讯云测中也使用了 4,50 款手机测试,没有发现问题,特分享出来。
功能特点: 
1.适配 android7.0 系统
2.解决部分机型裁剪闪退问题
4.解决图片过大 oom 闪退问题
5.动态获取系统权限,避免闪退
6.支持相片 or 视频的单选和多选
7.支持裁剪比例设置,如常用的 1:1、3:4、3:2、16:9 默认为图片大小
8.支持视频预览
9.支持 gif 图片
10.支持一些常用场景设置:如:是否裁剪、是否预览图片、是否裁剪、是否显示相机等
用得着的人点个赞吧~ 项目会一直维护,发现问题欢迎提出~
更多: 作者    提 Bug    示例 APK   
标签:
android多图选择 视频- 图片选择,适配-

一款针对 android 平台下的图片选择器,支持从相册或拍照选择图片或视频、音频,支持动态权限获取、裁剪(单图 or 多图裁剪)、压缩、主题自定义配置等功能、适配 android 6.0+系统的开源图片选择框架。

项目会一直维护(有 bug 修复完成,一般周末会更新),有 bug 请描述清楚,并请 Issues 会第一时间修复,Android 开发 QQ 交流群619458861,个人 QQ 893855882@qq.com 希望用得着的朋友点个 star。

PictureSelector 2.0 无裁剪功能版

我的博客地址

 PRs Welcome CSDN I Star

目录

-功能特点
-集成方式
-常见错误
-功能配置
-缓存清除
-主题配置
-常用功能
-结果回调
-更新日志
-混淆配置
-兼容性测试
-演示效果

功能特点

  • 1.适配 android6.0+系统
  • 2.解决部分机型裁剪闪退问题
  • 3.解决图片过大 oom 闪退问题
  • 4.动态获取系统权限,避免闪退
  • 5.支持相片 or 视频的单选和多选
  • 6.支持裁剪比例设置,如常用的 1:1、3:4、3:2、16:9 默认为图片大小
  • 7.支持视频预览
  • 8.支持 gif 图片
  • 9.支持.webp 格式图片
  • 10.支持一些常用场景设置:如:是否裁剪、是否预览图片、是否显示相机等
  • 11.新增自定义主题设置
  • 12.新增图片勾选样式设置
  • 13.新增图片裁剪宽高设置
  • 14.新增图片压缩处理
  • 15.新增录视频最大时间设置
  • 16.新增视频清晰度设置
  • 17.新增 QQ 选择风格,带数字效果
  • 18.新增自定义 文字颜色 背景色让风格和项目更搭配
  • 19.新增多图裁剪功能
  • 20.新增 LuBan 多图压缩
  • 21.新增单独拍照功能
  • 22.新增压缩大小设置
  • 23.新增 Luban 压缩档次设置
  • 24.新增圆形头像裁剪
  • 25.新增音频功能查询

**那些遇到拍照闪退问题的同学,请记得看清下面适配 6.0 的配置~**

重要的事情说三遍记得添加权限

  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.CAMERA" />

**注:适配 android6.0 以上拍照问题,请在 AndroidManifest.xml 中添加标签**

<provider 
   android:name="android.support.v4.content.FileProvider"
   android:authorities="${applicationId}.provider"
   android:exported="false"
   android:grantUriPermissions="true">
     <meta-data
         android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/file_paths" />
</provider>

集成方式

方式一 compile 引入

dependencies {
    compile 'com.github.LuckSiege.PictureSelector:picture_library:v2.1.1'
}

项目根目录 build.gradle 加入

allprojects {
   repositories {
      jcenter()
      maven { url 'https://jitpack.io' }
   }
}

方式二 maven 引入

step 1.

<repositories>
       <repository>
       <id>jitpack.io</id>
    <url>https://jitpack.io</url>
       </repository>
 </repositories>

step 2.

<dependency>
      <groupId>com.github.LuckSiege.PictureSelector</groupId>
      <artifactId>picture_library</artifactId>
      <version>v2.1.1</version>
</dependency>

常见错误

 问题一:
 rxjava 冲突:在 app build.gradle 下添加
 packagingOptions {
   exclude 'META-INF/rxjava.properties'
 }  

 问题二:
 java.lang.NullPointerException: 
 Attempt to invoke virtual method 'android.content.res.XmlResourceParser 
 android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)'
 on a null object reference

 application 下添加如下节点:

 <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${applicationId}.provider"
      android:exported="false"
      android:grantUriPermissions="true">
       <meta-data
         android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/file_paths" />
</provider>

注意:如已添加其他 sdk 或项目中已使用过 provider 节点,
[请参考我的博客](http://blog.csdn.net/luck_mw/article/details/54970105)的解决方案

问题三:
PhotoView 库冲突,可以删除自己项目中引用的,Picture_library 中已经引用过 
com.github.chrisbanes.photoview:library:1.2.4

问题四:
经测试在小米部分低端机中,Fragment 调用 PictureSelector 2.0 拍照有时内存不足会暂时回收 activity,
导致其 fragment 会重新创建 建议在 fragment 所依赖的 activity 加上如下代码:
if (savedInstanceState == null) {
      // 添加显示第一个 fragment
          fragment = new PhotoFragment();
              getSupportFragmentManager().beginTransaction().add(R.id.tab_content, fragment,
                    PictureConfig.FC_TAG).show(fragment)
                    .commit();
     } else {
          fragment = (PhotoFragment) getSupportFragmentManager()
          .findFragmentByTag(PictureConfig.FC_TAG);
}
这里就是如果是被回收时,则不重新创建 通过 tag 取出 fragment 的实例。

问题五:
glide 冲突
由于 PictureSelector 2.0 引入的是最新的 glide 4.0.0,所以将项目中老版本的 glide 删除,并且将报错代码换成如下写法:
RequestOptions options = new RequestOptions();
options.placeholder(R.drawable.image);
Glide.with(context).load(url).apply(options).into(imageView);

功能配置

// 进入相册 以下是例子:用不到的 api 可以不写
 PictureSelector.create(MainActivity.this)
     .openGallery()//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()
     .theme()//主题样式(不设置为默认样式) 也可参考 demo values/styles 下 例如:R.style.picture.white.style
     .maxSelectNum()// 最大图片选择数量 int
     .minSelectNum()// 最小选择数量 int
    .imageSpanCount(4)// 每行显示个数 int
     .selectionMode()// 多选 or 单选 PictureConfig.MULTIPLE or PictureConfig.SINGLE
     .previewImage()// 是否可预览图片 true or false
     .previewVideo()// 是否可预览视频 true or false
    .enablePreviewAudio() // 是否可播放音频 true or false
     .compressGrade()// luban 压缩档次,默认 3 档 Luban.THIRD_GEAR、Luban.FIRST_GEAR、Luban.CUSTOM_GEAR
     .isCamera()// 是否显示拍照按钮 true or false
    .isZoomAnim(true)// 图片列表点击 缩放效果 默认 true
    .sizeMultiplier(0.5f)// glide 加载图片大小 0~1 之间 如设置 .glideOverride()无效
    .setOutputCameraPath("/CustomPath")// 自定义拍照保存路径,可不填
     .enableCrop()// 是否裁剪 true or false
     .compress()// 是否压缩 true or false
     .compressMode()//系统自带 or 鲁班压缩 PictureConfig.SYSTEM_COMPRESS_MODE or LUBAN_COMPRESS_MODE
     .glideOverride()// int glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度
     .withAspectRatio()// int 裁剪比例 如 16:9 3:2 3:4 1:1 可自定义
     .hideBottomControls()// 是否显示 uCrop 工具栏,默认不显示 true or false
     .isGif()// 是否显示 gif 图片 true or false
     .freeStyleCropEnabled()// 裁剪框是否可拖拽 true or false
     .circleDimmedLayer()// 是否圆形裁剪 true or false
     .showCropFrame()// 是否显示裁剪矩形边框 圆形裁剪时建议设为 false   true or false
     .showCropGrid()// 是否显示裁剪矩形网格 圆形裁剪时建议设为 false    true or false
     .openClickSound()// 是否开启点击声音 true or false
     .selectionMedia()// 是否传入已选图片 List<LocalMedia> list
     .previewEggs()// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中) true or false
     .cropCompressQuality()// 裁剪压缩质量 默认 90 int
     .compressMaxKB()//压缩最大值 kb compressGrade()为 Luban.CUSTOM_GEAR 有效 int 
     .compressWH() // 压缩宽高比 compressGrade()为 Luban.CUSTOM_GEAR 有效  int 
     .cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效 int 
     .rotateEnabled() // 裁剪是否可旋转图片 true or false
     .scaleEnabled()// 裁剪是否可放大缩小图片 true or false
     .videoQuality()// 视频录制质量 0 or 1 int
     .videoSecond()// 显示多少秒以内的视频 or 音频也可适用 int 
    .recordVideoSecond()//视频秒数录制 默认 60s int
     .forResult(PictureConfig.CHOOSE_REQUEST);//结果回调 onActivityResult code

缓存清除

 //包括裁剪和压缩后的缓存,要在上传成功后调用,注意:需要系统 sd 卡权限 
 PictureFileUtils.deleteCacheDirFile(MainActivity.this);

主题配置

<!--默认样式 注意* 样式只可修改,不能删除任何一项 否则报错-->
    <style name="picture.default.style" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <!--标题栏背景色-->
        <item name="colorPrimary">@color/bar_grey</item>
        <!--状态栏背景色-->
        <item name="colorPrimaryDark">@color/bar_grey</item>
        <!--是否改变图片列表界面状态栏字体颜色为黑色-->
        <item name="picture.statusFontColor">false</item>
        <!--返回键图标-->
        <item name="picture.leftBack.icon">@drawable/picture_back</item>
        <!--标题下拉箭头-->
        <item name="picture.arrow_down.icon">@drawable/arrow_down</item>
        <!--标题上拉箭头-->
        <item name="picture.arrow_up.icon">@drawable/arrow_up</item>
        <!--标题文字颜色-->
        <item name="picture.title.textColor">@color/white</item>
        <!--标题栏右边文字-->
        <item name="picture.right.textColor">@color/white</item>
        <!--图片列表勾选样式-->
        <item name="picture.checked.style">@drawable/checkbox_selector</item>
        <!--开启图片列表勾选数字模式-->
        <item name="picture.style.checkNumMode">false</item>
        <!--选择图片样式 0/9-->
        <item name="picture.style.numComplete">false</item>
        <!--图片列表底部背景色-->
        <item name="picture.bottom.bg">@color/color_fa</item>
        <!--图片列表预览文字颜色-->
        <item name="picture.preview.textColor">@color/tab_color_true</item>
        <!--图片列表已完成文字颜色-->
        <item name="picture.complete.textColor">@color/tab_color_true</item>
        <!--图片已选数量圆点背景色-->
        <item name="picture.num.style">@drawable/num_oval</item>
        <!--预览界面标题文字颜色-->
        <item name="picture.ac_preview.title.textColor">@color/white</item>
        <!--预览界面已完成文字颜色-->
        <item name="picture.ac_preview.complete.textColor">@color/tab_color_true</item>
        <!--预览界面标题栏背景色-->
        <item name="picture.ac_preview.title.bg">@color/bar_grey</item>
        <!--预览界面底部背景色-->
        <item name="picture.ac_preview.bottom.bg">@color/bar_grey_90</item>
        <!--预览界面状态栏颜色-->
        <item name="picture.status.color">@color/bar_grey_90</item>
        <!--预览界面返回箭头-->
        <item name="picture.preview.leftBack.icon">@drawable/picture_back</item>
        <!--是否改变预览界面状态栏字体颜色为黑色-->
        <item name="picture.preview.statusFontColor">false</item>
        <!--裁剪页面标题背景色-->
        <item name="picture.crop.toolbar.bg">@color/bar_grey</item>
        <!--裁剪页面状态栏颜色-->
        <item name="picture.crop.status.color">@color/bar_grey</item>
        <!--裁剪页面标题文字颜色-->
        <item name="picture.crop.title.color">@color/white</item>
        <!--相册文件夹列表选中图标-->
        <item name="picture.folder_checked_dot">@drawable/orange_oval</item>
    </style>

常用功能

**启动相册并拍照**

 PictureSelector.create(MainActivity.this)
       .openGallery(PictureMimeType.ofImage())
       .forResult(PictureConfig.CHOOSE_REQUEST);

**单独启动拍照或视频 根据 PictureMimeType 自动识别**

  PictureSelector.create(MainActivity.this)
       .openCamera(PictureMimeType.ofImage())
       .forResult(PictureConfig.CHOOSE_REQUEST);

**预览图片**

// 预览图片 可自定长按保存路径
PictureSelector.create(MainActivity.this).externalPicturePreview(position, "/custom_file", selectList);
PictureSelector.create(MainActivity.this).externalPicturePreview(position, selectList);

**预览视频**

PictureSelector.create(MainActivity.this).externalPictureVideo(video_path);

结果回调

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case PictureConfig.CHOOSE_REQUEST:
                    // 图片选择结果回调
                    selectList = PictureSelector.obtainMultipleResult(data);
                    // 例如 LocalMedia 里面返回三种 path
                    // 1.media.getPath(); 为原图 path
                    // 2.media.getCutPath();为裁剪后 path,需判断 media.isCut();是否为 true
                    // 3.media.getCompressPath();为压缩后 path,需判断 media.isCompressed();是否为 true
                    // 如果裁剪并压缩了,以取压缩路径为准,因为是先裁剪后压缩的
                    adapter.setList(selectList);
                    adapter.notifyDataSetChanged();
                    DebugUtil.i(TAG, "onActivityResult:" + selectList.size());
                    break;
            }
        }
    }

更新日志

当前版本:

  • v2.1.1
  • 升级 glide 4.0 为正式版
  • 修复 7.1.1 系统 PopupWindow 弹出位置错误 bug

历史版本:

  • v2.1.0
  • 修复裁剪速度慢的问题

  • v2.0.9

  • 修复直接播放视频闪退 bug
  • 升级 glide 为 4.0.0 rc1
  • 新增图片列表点击缩放效果 api

  • v2.0.7

  • 修复已知 bug

  • v2.0.6

  • 新增自定拍照保存路径
  • 修复录音不显示时长问题

项目使用第三方库:

  • glide:4.0.0 RC1
  • rxjava:2.0.5
  • rxandroid:2.0.1
  • PhotoView:1.2.4
  • luban
  • 裁剪使用 ucrop

混淆配置

#PictureSelector 2.0
-keep class com.luck.picture.lib.** { *; }

-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }

 #rxjava
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
 long producerIndex;
 long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
 rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
 rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

#rxandroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
   long producerIndex;
   long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule

兼容性测试

**腾讯优测-深度测试-通过率达到 100%**

image

演示效果

image imageimageimageimage imageimage image image imageimageimage imageimage

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值