资源展示 | 相册选择 |
---|---|
圆形裁剪 | 方形裁剪 |
---|---|
[图片上传失败...(image-c9ea40-1576230531404)]
一、需求来源
初识知乎团队Matisse,很是喜欢。但,由于与本身项目UI风格差异较大,于是便基于知乎团队Matisse稍作改动,稍作扩展,最终推出Matisse-Kotlin方便自己使用。
本项目为知乎原项目kotlin改写版本(2018/9月版本),对知乎团队Matisse进行Kotlin翻译,主要对原项目进行部分UI层面改写、已发现bug的修改、新功能添加。
二、感谢
Matisse核心功能:https://github.com/zhihu/Matisse
裁剪提供者:廖子尧 github地址:https://github.com/jeasonlzy
图片压缩提供者:https://github.com/nanchen2251
三、主要新增功能
- 优化相册选择UI - 改变原有顶部弹出相册选择方式,底部弹出操作区域更大
- 优化单选策略 - 原项目未明确区分单选/多选,此处单选可替换选中
- 优化选中刷新 - 去除原notifyDataSetChanged列表刷新方式
- 添加圆形与方形裁剪
- 修复视频、图片混合选择
- 添加图片选择后压缩,不失真条件下高比率压缩,支持外部实现
- 增加主题修改,基本可保证定制成与自身项目风格一致
- 支持设置状态栏颜色 需依赖ImmersionBar,支持外部实现
- 迁移到androidx、Kotlin
- 抽取拍照功能,可单独使用
- 提示方式可外部定制样式 - Matisse中提示方式可使用自身项目中自定义提示样式
- 支持拍照完后停留在照片选择界面界面
- 支持进入界面默认选中指定资源
四、引入、配置
第1步. 网络依赖
主项目build.gradle中添加 implementation 'com.nfleo:MatisseKotlin:2.0.2'
第2步. 配置AndroidManifest.xml
<font color=red>注:注意provider name androidx的差别</font>
AndroidManifest.xml中添加以下代码:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths_public"/>
</provider>
第3步. 6.0+需处理权限
The library requires two permissions:
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.CAMERA
第4步. 适配7.0
项目manifest的privider标签下 paths文件中添加文件名称为file_paths_public(名字随意取,但需与AndroidManifest.xml中引用保持一致),以共享文件目录
<external-path name="my_images" path="Pictures"/>
第5步. 配置gradle
// 具体版本需自行配置(最外层build.gradle),可修改对应版本号
ext {
compileSdkVersion = 28
minSdkVersion = 19
targetSdkVersion = 28
appcompat = '1.1.0'
material = '1.0.0'
recyclerview = '1.0.0'
constraintlayout = '1.1.3'
}
五、更新记录
2019-12-10 (2.0.2)
- 修复mimeType为空情况
- 修复spanSize和gridExceptedSize同时设置冲突
注:同时设置时,读取gridExceptedSize值
2019-11-10 (2.0.1)
- 修复裁剪结果尺寸异常
2019-11-4 (2.0)
- 相机单独提取
- 支持默认选中,可传入上次选中的项(通过图片cursor id或uri string对比)
注:不支持裁剪带回的图片,裁剪带回的图片无id和uri
.setLastChoosePicturesIdOrUri(selectedPathIds as ArrayList<String>?)
- 修复压缩为空带回崩溃
2019-10-29 (1.2.3)
- 修复相册弹窗高度不准确问题
- 支持压缩配置,外部添加开关 api:[isInnerCompress]
- 完善未选中资源时各按钮点击添加提示
- 修复不同设备返回的媒体类型表示不一致(如:JPEG image/jpeg)
- 去除[api setStatusIsDark]。外部处理状态栏,见[api setStatusBarFuture]
2019-10-28 (持续更新 待发布)
- 支持相机拍照完成后多选
- 扩展提示方法,支持使用外部自定义弹窗
- 支持外部处理状态栏,去除项目中原[ImmersionBar]库
.setStatusBarFuture(object : MFunction<BaseActivity> {
override fun accept(params: BaseActivity, view: View?) {
// view为顶部标题栏
// 外部设置状态栏
ImmersionBar.with(params)?.run {
statusBarDarkFont(isDarkStatus)
view?.apply { titleBar(this) }
init()
}
// 外部可隐藏Matisse界面中的View
view?.visibility = if (isDarkStatus) View.VISIBLE else View.GONE
}
})
- 按官方方式适配Android Q(未真机测试)
2019-10-21 (1.2.2)
- 修复方形裁剪图片变形问题
- 优化单选/多选刷新问题
2019-10-18 (1.2.0)
- 迁移到androidx
- 修复并支持图片与视频混合选择
设置选择单一类型媒体,示例如下
SelectionCreator.choose(MimeTypeManager.ofAll())
.maxSelectable(3)
或者
SelectionCreator.choose(MimeTypeManager.ofAll(), true)
.maxSelectable(3)
设置选择混合类型媒体,示例如下
SelectionCreator.choose(MimeTypeManager.ofAll(), false)
.maxSelectablePerMediaType(4, 2)
说明:
mediaTypeExclusive true 单一媒体类型选择
读取maxSelectable属性作为最大值
mediaTypeExclusive false
读取maxImageSelectable和maxVideoSelectable属性分别作为最大值
- 修改单/多选逻辑
- 单选支持重新选定,不支持计数方式
- 多选不支持重新选定,选满外部给出提示方式,支持计数与选中方式
- 提示方式外部实现
SelectionCreator.setNoticeConsumer(object : Consumer<String> {
override fun accept(params: String) {
// 提示方式。 可使用与自己项目相同样式的Toast或其他
showToast(params)
}
})
2019-10-16
- 完善主题扩展,并提供图片说明
六、重点Api详细说明
入口:Matisse
, 配置类:SelectionCreator
.from(activity/fragment)
:绑定到当前activity/fragment
.choose(...)
:
- @params matisse:用于获得绑定的activity/fragment
- @params mimeTypes:获取所需展示的媒体类型。提供类型有三种MimeTypeManager.ofAll()、MimeTypeManager.ofImage()、MimeTypeManager.ofVideo()或自定义类型MimeTypeManager.of(MimeType.PNG...)
- @params mediaTypeExclusive:是否选择单一资源
false = 可同时选择视频和图片
ture = 仅能选择视频或者仅能选择图片
.countable(countable: Boolean)
:
选中控件是否按数字编号,<font color=red>说明:单选模式下不支持数字编号</font>
.maxSelectable(maxSelectable: Int)
:
单类型选择模式下,最多可选中的数量,<font color=red>说明:maxSelectable属性在单类型模式下才生效(即mediaTypeExclusive = true)</font>
.maxSelectablePerMediaType(maxImage: Int, maxVideo: Int)
:
混合类型选择模式下,视频和图片各自最大选择数量。<font color=red>说明:对应两属性在混合类型选择模式下才生效(即mediaTypeExclusive = false)</font>
.addFilter(filter: Filter)
:
添加选中过滤器,需外部继承实现Filter类。指定类型资源结合指定规则给出相应提示。例如:不可选择大于5M的图片,并吐司提示
.capture(enable: Boolean)
:
是否包含拍照功能。<font color=red>设置包含拍照功能时,需设置strategy</font>
.captureStrategy(captureStrategy: CaptureStrategy)
:
包含拍照功能时需设置strategy,7.0必须添加,同时需在Androidmanifest.xml中注册
.restrictOrientation(@ScreenOrientation orientation: Int)
:
强制设置横竖屏显示
.spanCount(spanCount: Int)
:
图片显示列数。当设置图片尺寸属性(gridExpectedSize)时,则忽略该属性
.gridExpectedSize(size: Int)
:
设置图片期望尺寸。根据图片期望尺寸计算图片展示列数
.thumbnailScale(scale: Float)
:
设置图片期望展示压缩比
.imageEngine(imageEngine: ImageEngine)
:
设置图片加载器,目前暂不支持Fresco
.isCrop(crop: Boolean)
:
是否开启裁剪
.isCropSaveRectangle(cropSaveRectangle: Boolean)
:
圆形裁剪模式下,裁剪结果是否以正方形保存
.cropFocusWidthPx(cropFocusWidth: Int)
:
裁剪框px宽度。圆形裁剪宽高取较小值作为直径
.cropFocusHeightPx(cropFocusHeight: Int)
:
裁剪框px高度。圆形裁剪宽高取较小值作为直径
.cropStyle(cropStyle: CropImageView.Style)
:
裁剪类型。圆形裁剪(CropImageView.Style.CIRCLE),方形裁剪(CropImageView.Style.RECTANGLE)
.cropCacheFolder(cropCacheFolder: File)
:
可设置裁剪后图片保存的文件
.setNoticeConsumer(noticeConsumer: Consumer<String>?)
:
接口形式实现提示方式,外部提示方式可任意定制
.forResult(requestCode: Int)
:
最后通过activityForResult方式拿到图片选择结果
.setStatusBarFuture(statusBarFunction: MFunction<BaseActivity>?)
:
设置状态栏相关,设置状态栏透明、修改状态栏字体颜色等
.setLastChoosePicturesIdOrUri(preSelected: ArrayList<String>?)
:
指定资源,打开Matisse后默认选中
七、部分功能点解读
* 资源混合选择
入口处通过Matisse.choose
方法指定需展示的资源,同时指定资源选择方式,最终初始化SelectionCreator
构造器,设置selectionSpec.mediaTypeExclusive
值
Matisse.kt
/**
* @params mimeTypes 需展示的媒体类型
* @params mediaTypeExclusive 设置资源混合选择开关
*/
fun choose(mimeTypes: Set<MimeType>, mediaTypeExclusive: Boolean): SelectionCreator {
return SelectionCreator(this, mimeTypes, mediaTypeExclusive)
}
此处主要讲支持资源混合选择的情况,即 selectionSpec.mediaTypeExclusive = false
判断逻辑主要在SelectedItemCollection.kt
中,为支持资源混合选择,同时两类资源分别计数,此处引入两个集合分别装载图片与视频资源imageItems、videoItems
具体操作分为三个阶段:
阶段一:选中前判断是否达到上限。混合选择模式下需分开判断已选资源数量,代码如下:
/**
* @params item 当前选定的资源item
**/
fun maxSelectableReached(item: Item?): Boolean {
// 是否为混合选择模式
if (!spec.isMediaTypeExclusive()) {
// 根据当前选定资源类型,分别取对应集合与对应最大值对比
if (item?.isImage() == true) {
return spec.maxImageSelectable == imageItems?.size
} else if (item?.isVideo() == true) {
return spec.maxVideoSelectable == videoItems?.size
}
}
// 非混合选择模式,直接对比总集合items
return spec.maxSelectable == items.size
}
// 是否为单一资源选择模式
fun isMediaTypeExclusive() = mediaTypeExclusive && (maxImageSelectable + maxVideoSelectable == 0)
阶段二:将选中的资源添加到总集合items
中,同时根据类型添加到视频/图片imageItems/videoItems
集合
fun add(item: Item?): Boolean {
...
val added = items.add(item)
addImageOrVideoItem(item)
...
}
// 根据当前选定的item记录到指定集合
private fun addImageOrVideoItem(item: Item) {
if (item.isImage()) {
if (imageItems == null)
imageItems = linkedSetOf()
imageItems?.add(item)
} else if (item.isVideo()) {
if (videoItems == null)
videoItems = linkedSetOf()
videoItems?.add(item)
}
}
阶段三:取消选中时,从总集合items
中移除,同时,根据选择模式判断从图片/视频集合中移除
private fun removeImageOrVideoItem(item: Item) {
if (item.isImage()) {
imageItems?.remove(item)
} else if (item.isVideo()) {
videoItems?.remove(item)
}
}
* 单选/多选策略
首先定义两个方法:
// 是否可单选,混合选择模式与单一选择模式均判断
fun isSingleChoose() = maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1)
// 是否可计数
fun isCountable() = countable && !isSingleChoose()
选中操作逻辑如下:
单选模式,由于只会选中一个Item,因此去除计数支持
单选:
a.选中:刷新当前项与上次选择项
b.取消选中:刷新当前项
多选:
1. 按序号计数
a.选中:仅刷新选中的item
b.取消选中:
取消最后一位:仅刷新当前操作的item
取消非最后一位:刷新所有选中的item
2. 无序号计数
a.选中:仅刷新选中的item
b.取消选中:仅刷新选中的item
override fun onCheckViewClicked(
checkView: CheckView, item: Item, holder: RecyclerView.ViewHolder
) {
if (selectionSpec.isSingleChoose()) {
notifySingleChooseData(item)
} else {
notifyMultiChooseData(item)
}
}
/**
* 单选刷新数据
*/
private fun notifySingleChooseData(item: Item) {
if (selectedCollection.isSelected(item)) {
// 选中时,再次点击则取消选中,只刷新当前item位置即可
selectedCollection.remove(item)
notifyItemChanged(item.positionInList)
} else {
// 未选中时,再次点击选中,刷新当前item,与上次选中的Item
// 刷新上次选中Item,存在已选中Item则去除选中并刷新
notifyLastItem()
// 添加Item 并刷新当前Item位置
if (!addItem(item)) return
notifyItemChanged(item.positionInList)
}
}
多选的选中与反选稍复杂些,需支持选中计数。计数条件下,选中操作只需添加数据刷新列表当前Item即可;取消选中操作时,计数顺序已经改变,需根据当前取消Item在选中数据集中位置决定需要刷新的列表条目,当取消选中Item为最后一个Item时,此时计数顺序未改变,因此只需移除Item并刷新最后一个item即可。反之,计数顺序已经改变,则需刷新所有选中Item,重新设置序号。具体代码如下:
/**
* 多选刷新数据
*/
private fun notifyMultiChooseData(item: Item) {
if (selectionSpec.isCountable()) {
// 计数模式处理选中刷新
if (notifyMultiCountableItem(item)) return
} else {
if (selectedCollection.isSelected(item)) {
selectedCollection.remove(item)
} else {
if (!addItem(item)) return
}
notifyItemChanged(item.positionInList)
}
}
/**
* @return 是否拦截 true=拦截 false=不拦截
*/
private fun notifyMultiCountableItem(item: Item): Boolean {
val checkedNum = selectedCollection.checkedNumOf(item)
if (checkedNum == CheckView.UNCHECKED) {
if (!addItem(item)) return true
notifyItemChanged(item.positionInList)
} else {
selectedCollection.remove(item)
// 取消选中中间序号时,刷新所有选中item
if (checkedNum != selectedCollection.count() + 1) {
selectedCollection.asList().forEach {
notifyItemChanged(it.positionInList)
}
}
// 别忘了刷新当前Item
notifyItemChanged(item.positionInList)
}
return false
}
* 过滤器
通过设置过滤器,可在点击时根据过滤定义的条件决定是否可选中资源。
过滤器实现主要继承Filter
类。类中提供两个抽象方法,一个供子类调用的是否拦截方法。
-
abstract fun constraintTypes(): Set<MimeType>
:设置需过滤资源的类型,返回资源集合 -
abstract fun filter(context: Context, item: Item?): IncapableCause?
:设定过滤条件,及提示方式 -
open fun needFiltering(context: Context, item: Item?): Boolean
:判断item集合是否属于方法1中定义的集合
例如:添加过滤器,限定只能选择小于500kb的图片
首先需定义一个过滤器
/**
* desc:不允许选择大于itemSize字节的图片</br>
* time: 2019/10/23-10:12</br>
* author:Leo </br>
* since V 1.2.2 </br>
*/
class ImageSizeFilter(private var itemSize: Long) : Filter() {
// 设置需过滤的资源类型,此处过滤类型为图片
override fun constraintTypes() = MimeTypeManager.ofImage()
override fun filter(context: Context, item: Item?): IncapableCause? {
// 1. 判断当前选中的item是否属于constraintTypes中定义的图片资源
if (!needFiltering(context, item)) return null
if (item?.size ?: 0 > itemSize) {
// IncapableCause具体实现在setNoticeConsumer方法中定义
return IncapableCause("需选择小于${PhotoMetadataUtils.getSizeInMB(itemSize)}M的图片")
}
return null
}
}
拦截原理:
选中item时,会调用SelectedItemCollection.kt
类中的isAcceptable(item: Item?)
方法,该方法有三类拦截:选中数量达到最大值、单一资源选择类型下选择混合资源、遍历外部定义过滤器拦截
fun isAcceptable(item: Item?): IncapableCause? {
if (maxSelectableReached(item)) {
// 类型1,选中数量达到最大
...
return IncapableCause(causeString)
} else if (typeConflict(item)) {
// 类型2,单一资源选择类型下选择混合资源,给出提示
return IncapableCause(context.getString(R.string.error_type_conflict))
}
// 类型3,遍历外部定义的过滤器
return PhotoMetadataUtils.isAcceptable(context, item)
}
/**
* 遍历外部自定义过滤器
* PhotoMetadataUtils.kt
*/
fun isAcceptable(context: Context, item: Item?): IncapableCause? {
if (!isSelectableType(context, item))
return IncapableCause(context.getString(R.string.error_file_type))
// 遍历外部自定义的过滤器
if (SelectionSpec.getInstance().filters != null) {
SelectionSpec.getInstance().filters?.forEach {
return it.filter(context, item)
}
}
return null
}
* 主题
为保证Matisse UI风格完全符合主项目,由此扩展主题,可在style.xml中修改Matisse库中更多控件属性。库内提供了一个完整的Style作为父类,具体如下:
<style name="Matisse.Default" parent="Theme.AppCompat.Light.NoActionBar">
<!--状态栏相关-->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<!--顶部导航条高度-->
<item name="navigation.height">@dimen/navigation_height</item>
<!--顶部导航条背景色-->
<item name="navigation.background">@color/navigation_bg</item>
<!--顶部导航条返回按钮资源图-->
<item name="navigation.backRes">@drawable/icon_arrow_right_white</item>
<!--图片预览界面背景色-->
<item name="preview.background">@color/preview_bg</item>
<!--底部工具栏背景色-->
<item name="bottomToolbar.bg">@color/bottomTool_bg</item>
<!--底部工具栏高度-->
<item name="bottomToolbar.height">@dimen/bottom_tool_height</item>
<!--返回按钮文字 当无文字是可设置为空字符串 如下-->
<item name="Media.Back.text">@string/button_null</item>
<!--返回按钮颜色-->
<item name="Media.Back.textColor">@color/color_base</item>
<!--返回按钮文字大小-->
<item name="Media.Back.textSize">@dimen/text_back</item>
<item name="Media.Sure.text">@string/button_sure</item>
<!--确定按钮颜色-->
<item name="Media.Sure.textColor">@color/color_base</item>
<!--确认按钮文字大小-->
<item name="Media.Sure.textSize">@dimen/text_sure</item>
<item name="Media.Preview.text">@string/button_preview</item>
<!--预览按钮颜色-->
<item name="Media.Preview.textColor">@color/text_preview</item>
<!--预览按钮文字大小-->
<item name="Media.Preview.textSize">@dimen/text_preview</item>
<item name="Media.Original.text">@string/button_original</item>
<!--原图选择控件文字颜色-->
<item name="Media.Original.textColor">@color/text_original</item>
<!--原图按钮文字大小-->
<item name="Media.Original.textSize">@dimen/text_original</item>
<item name="Media.Album.text">@string/album_name_all</item>
<!--查看全部相册文件夹按钮颜色-->
<item name="Media.Album.textColor">@color/text_album</item>
<!--查看全部相册按钮文字大小-->
<item name="Media.Album.textSize">@dimen/text_album</item>
<!--相册文件夹内部列表item颜色-->
<item name="Media.Album.Item.textColor">@color/text_item_album</item>
<!--查看全部相册内item文字大小-->
<item name="Media.Album.Item.textSize">@dimen/text_item_album</item>
<!--列表中可拍照状态下相机item文字颜色-->
<item name="Media.Camera.textColor">@color/text_camera</item>
<!--列表中可拍照状态下相机item文字大小-->
<item name="Media.Camera.textSize">@dimen/text_camera</item>
<item name="Preview.Back.text">@string/button_back</item>
<item name="Preview.Confirm.text">@string/button_sure_default</item>
<!--选中控件背景色-->
<item name="item.checkCircle.backgroundColor">@color/item_checkCircle_backgroundColor</item>
<!--选中控件圆环边框颜色-->
<item name="item.checkCircle.borderColor">@color/item_checkCircle_borderColor</item>
<!--原图radio控件颜色-->
<item name="item.checkRadio">@color/item_checkRadio</item>
<!--空状态文字颜色-->
<item name="Media.Empty.textColor">@color/text_empty</item>
<!--空状态文字大小-->
<item name="Media.Empty.textSize">@dimen/text_empty</item>
<item name="Media.Empty.text">@string/empty_text</item>
<!--空状态资源图-->
<item name="Media.Empty.resource">@drawable/ic_empty_zhihu</item>
<!--图片加载占位图-->
<item name="item.placeholder">@color/zhihu_item_placeholder</item>
</style>
需自定义其他属性,可另写style,修改所需属性即可:
// parent为Matisse.Default
<style name="CustomMatisseStyle" parent="Matisse.Default">
<item name="Media.Back.text">@string/back</item>
<item name="Media.Sure.text">@string/sure</item>
<item name="Media.Preview.text">@string/preview</item>
<item name="Media.Original.text">@string/original</item>
<item name="Media.Album.text">@string/album</item>
<item name="Media.Camera.text">@string/camera</item>
</style>
属性对照图1 | 属性对照图2 |
---|---|
* 裁剪
裁剪功能提供了裁剪开关(isCrop)、裁剪类型(cropStyle)、裁剪框尺寸(cropFocusWidthPx、cropFocusHeightPx)、圆形裁剪支持保存正方形图(isCropSaveRectangle)、裁剪图保存地址(cropCacheFolder)等几个属性。
注:裁剪有效的前提必须是开启裁剪同时最大选择数量为1。
// 是否可单选
fun isSingleChoose() =
maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1)
// 是否可裁剪
fun openCrop() = isCrop && isSingleChoose()
裁剪后不返回uri地址,只返回file path路径
八、具体调用
Matisse api众多,须根据需要自行调用,大致例子如下
kotlin项目调用
Matisse.from(MainActivity.this)
.choose(MimeTypeManager.ofAll(), true)
.countable(true)
.maxSelectable(9)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.thumbnailScale(0.85f)
.imageEngine(new GlideEngine())
.forResult(REQUEST_CODE_CHOOSE);
java项目调用
Matisse.Companion.from(MainActivity.this)
.choose(MimeTypeManager.Companion.ofAll(), false)
.countable(true)
.maxSelectablePerMediaType(3, 3)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.thumbnailScale(0.85f)
.imageEngine(new GlideEngine())
.forResult(REQUEST_CODE_CHOOSE);
Matisse.from(SampleActivity.this)
.choose(MimeTypeManager.ofAll(), true) // 展示所有类型文件(图片 视频 gif)
.capture(true) // 可拍照
.countable(true) // 记录文件选择顺序
.captureStrategy(CaptureStrategy(true, "${Platform.getPackageName(this@ExampleActivity)}.fileprovider"))
.maxSelectable(1) // 最多选择一张
.isCrop(true) // 开启裁剪
.cropFocusWidthPx(400) // 设置裁剪后保存图片的宽高
.cropFocusHeightPx(400) // 设置裁剪后保存图片的宽高
.cropStyle(CropImageView.Style.RECTANGLE) // 方形裁剪CIRCLE为圆形裁剪
.isCropSaveRectangle(true) // 裁剪后保存方形(只对圆形裁剪有效)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K)) // 筛选数据源可选大小限制
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.thumbnailScale(0.8f)
.setStatusIsDark(true) // 设置状态栏文字颜色 需依赖ImmersionBar库
.imageEngine(new GlideEngine()) // 加载库需外部实现
.forResult(REQUEST_CODE_CHOOSE);
返回结果获得:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == ConstValue.REQUEST_CODE_CHOOSE && resultCode == Activity.RESULT_OK) {
var string = ""
// 获取uri返回值 裁剪结果不返回uri
val uriList = Matisse.obtainResult(data)
// 获取文件路径返回值
val strList = Matisse.obtainPathResult(data)
// 原文件
val file = FileUtil.getFileByPath(Matisse.obtainPathResult(data)[0])
Glide.with(this).load(file).into(iv_image)
// 压缩后的文件 (多个文件压缩可以循环压缩)
val file1 = CompressHelper.getDefault(applicationContext)?.compressToFile(file)
string += PhotoMetadataUtils.getSizeInMB(file.length()).toString() + " PK " + PhotoMetadataUtils.getSizeInMB(file1?.length() ?: 0)
string = "\n\n$string"
text.text = "\n\n$string"
}
}