我的项目1.0版本最近刚上线了,里面涉及到了发布日志的功能,用户可以发布纯文字、图片或视频。所以呢…又是一个耗大的工程。
首先来说选择图片、视频和拍摄吧
本来我是要用知乎的Matisse框架的,毕竟用的人数多,稳定性和兼容性都是值得信任的。我导入到项目中完美的运行了起来,具体的使用方法在这里就不说了,可以去上面的链接自行查看,反正就是傻瓜式用法,很方便快捷。
可是直到快上线前3、4天左右,PM突然跟我说要有拍摄视频的功能,我看了看Matisse是没有拍摄视频功能的,自己去做拍摄的话可能时间不够了,并且稳定性和兼容性可能照顾不周。我就开始在github继续翻阅开源库(哈哈,我真是名副其实的代码搬运工)。
终于找到了PictureSelector这个框架,用法和Matisse一样的,添加了许多自定义配置,并且是内置UCrop(著名的图片裁剪框架)、内置视频拍摄的。
换框架
添加依赖
dependencies {
implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'
}
项目根目录build.gradle加入
allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }
}
}
声明权限
用法
// 进入相册 以下是例子:用不到的api可以不写
PictureSelector.create(MainActivity.this)
.openGallery()//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
.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
.isCamera()// 是否显示拍照按钮 true or false
.imageFormat(PictureMimeType.PNG)// 拍照保存图片格式后缀,默认jpeg
.isZoomAnim(true)// 图片列表点击 缩放效果 默认true
.sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效
.setOutputCameraPath("/CustomPath")// 自定义拍照保存路径,可不填
.enableCrop()// 是否裁剪 true or false
.compress()// 是否压缩 true or false
.glideOverride()// int glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度
.withAspectRatio()// int 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
.hideBottomControls()// 是否显示uCrop工具栏,默认不显示 true or false
.isGif()// 是否显示gif图片 true or false
.compressSavePath(getPath())//压缩图片保存地址
.freeStyleCropEnabled()// 裁剪框是否可拖拽 true or false
.circleDimmedLayer()// 是否圆形裁剪 true or false
.showCropFrame()// 是否显示裁剪矩形边框 圆形裁剪时建议设为false true or false
.showCropGrid()// 是否显示裁剪矩形网格 圆形裁剪时建议设为false true or false
.openClickSound()// 是否开启点击声音 true or false
.selectionMedia()// 是否传入已选图片 List list
.previewEggs()// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中) true or false
.cropCompressQuality()// 裁剪压缩质量 默认90 int
.minimumCompressSize(100)// 小于100kb的图片不压缩
.synOrAsy(true)//同步true或异步false 压缩 默认同步
.cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效 int
.rotateEnabled() // 裁剪是否可旋转图片 true or false
.scaleEnabled()// 裁剪是否可放大缩小图片 true or false
.videoQuality()// 视频录制质量 0 or 1 int
.videoMaxSecond(15)// 显示多少秒以内的视频or音频也可适用 int
.videoMinSecond(10)// 显示多少秒以内的视频or音频也可适用 int
.recordVideoSecond()//视频秒数录制 默认60s int
.isDragFrame(false)// 是否可拖动裁剪框(固定)
.forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code
结果回调
@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:
// 图片、视频、音频选择结果回调
List 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();
break;
}
}
}
如果需要图片压缩的话,只需要修改参数即可
.compress(false)// 是否压缩 true or false
.synOrAsy(true)//同步true或异步false 压缩 默认同步
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {
List selectList = PictureSelector.obtainMultipleResult(data);
File file = null;
if (selectList.get(0).isCut()) {
file = new File(selectList.get(0).getCutPath());
} else {
file = new File(selectList.get(0).getPath());
}
//取得时候注意,如果这个图片.isCut()的情况再取getCutPath(),否则为空
}
}
项目最初是采用ffmpeg框架的,但是ffmpeg一个so库就7、8M,并且压缩速度实在是太慢了,实测30M的视频压缩到3.8M用了30多秒,但是压缩后的视频质量很可观,没有严重的丢帧和马赛克。这边奉上ffmpeg进行视频压缩的详细步骤 T_T操作步骤
后来经过我的疯狂百度、Google。终于找到了这个框架VideoCompressor
实测30M到3M用时10s,并且有low、middle、high三种质量可以选择,但是在high压缩的时候可能会出现压缩后比原视频体积还大的情况。最终我们选择了Medium,速度与清晰度都达到要求。
先去Github下载module,导入到自己的项目中。
VideoCompress.compressVideoMedium("源视频路径", "视频生成路径", new VideoCompress.CompressListener() {
@Override
public void onStart() {
Logutils.i(TAG, "---onStart");
}
@Override
public void onSuccess() {
//压缩成功则开始上传视频
Logutils.i(TAG, "---onSuccess");
}
@Override
public void onFail() {
Logutils.i(TAG, "---onFail");
}
@Override
public void onProgress(float percent) {
Logutils.i(TAG, "---onProgress:" + percent);
}
});
VideoCompress提供了三种视频压缩比率,分别是High、Medium和Low,使用时只需要将compressVideoMedium方法修改一下即可。
媒体上传我倒是没有做太多的处理,就是直接放在MultipartBody里面。
不过项目遇到了个小问题,就是视频上传的时候因为要压缩,所以导致用户上传视频时等待时间过长,体验不好。
最后的解决方案是,用户点击上传的时候我们只创建纯文字的日志。服务器返回日志ID,然后我们存入到本地数据库中,并且唤醒一个foregroundService进行上传操作,不影响用户使用。创建成功后先在列表展示本地的数据,service开启线程上传媒体资源,然后如果用户刷新列表并且日志媒体资源上传成功的情况下,才会展示网络数据,清掉数据库。不知道这样的解决方案合理性如何,各位大神如果有好的解决方案可以直接评论在下方,感谢感谢