資源展示 | 相冊(cè)選擇 |
---|---|
圓形裁剪 | 方形裁剪 |
---|---|
[圖片上傳失敗...(image-c9ea40-1576230531404)]
一、需求來(lái)源
初識(shí)知乎團(tuán)隊(duì)Matisse伺糠,很是喜歡。但剥啤,由于與本身項(xiàng)目UI風(fēng)格差異較大,于是便基于知乎團(tuán)隊(duì)Matisse稍作改動(dòng)奄喂,稍作擴(kuò)展铐殃,最終推出Matisse-Kotlin方便自己使用。
本項(xiàng)目為知乎原項(xiàng)目kotlin改寫(xiě)版本(2018/9月版本)跨新,對(duì)知乎團(tuán)隊(duì)Matisse進(jìn)行Kotlin翻譯富腊,主要對(duì)原項(xiàng)目進(jìn)行部分UI層面改寫(xiě)、已發(fā)現(xiàn)bug的修改域帐、新功能添加赘被。
二、感謝
Matisse核心功能:https://github.com/zhihu/Matisse
裁剪提供者:廖子堯 github地址:https://github.com/jeasonlzy
圖片壓縮提供者:https://github.com/nanchen2251
三肖揣、主要新增功能
- 優(yōu)化相冊(cè)選擇UI - 改變?cè)许敳繌棾鱿鄡?cè)選擇方式民假,底部彈出操作區(qū)域更大
- 優(yōu)化單選策略 - 原項(xiàng)目未明確區(qū)分單選/多選,此處單選可替換選中
- 優(yōu)化選中刷新 - 去除原notifyDataSetChanged列表刷新方式
- 添加圓形與方形裁剪
- 修復(fù)視頻龙优、圖片混合選擇
- 添加圖片選擇后壓縮羊异,不失真條件下高比率壓縮,支持外部實(shí)現(xiàn)
- 增加主題修改彤断,基本可保證定制成與自身項(xiàng)目風(fēng)格一致
- 支持設(shè)置狀態(tài)欄顏色 需依賴ImmersionBar野舶,支持外部實(shí)現(xiàn)
- 遷移到androidx、Kotlin
- 抽取拍照功能宰衙,可單獨(dú)使用
- 提示方式可外部定制樣式 - Matisse中提示方式可使用自身項(xiàng)目中自定義提示樣式
- 支持拍照完后停留在照片選擇界面界面
- 支持進(jìn)入界面默認(rèn)選中指定資源
四平道、引入、配置
第1步. 網(wǎng)絡(luò)依賴
? 主項(xiàng)目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+需處理權(quán)限
The library requires two permissions:
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.CAMERA
第4步. 適配7.0
項(xiàng)目manifest的privider標(biāo)簽下 paths文件中添加文件名稱為file_paths_public(名字隨意取供炼,但需與AndroidManifest.xml中引用保持一致)一屋,以共享文件目錄
<external-path name="my_images" path="Pictures"/>
第5步. 配置gradle
// 具體版本需自行配置(最外層build.gradle)窘疮,可修改對(duì)應(yīng)版本號(hào)
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)
- 修復(fù)mimeType為空情況
- 修復(fù)spanSize和gridExceptedSize同時(shí)設(shè)置沖突
注:同時(shí)設(shè)置時(shí)冀墨,讀取gridExceptedSize值
2019-11-10 (2.0.1)
- 修復(fù)裁剪結(jié)果尺寸異常
2019-11-4 (2.0)
- 相機(jī)單獨(dú)提取
- 支持默認(rèn)選中闸衫,可傳入上次選中的項(xiàng)(通過(guò)圖片cursor id或uri string對(duì)比)
注:不支持裁剪帶回的圖片,裁剪帶回的圖片無(wú)id和uri
.setLastChoosePicturesIdOrUri(selectedPathIds as ArrayList<String>?)
- 修復(fù)壓縮為空帶回崩潰
2019-10-29 (1.2.3)
- 修復(fù)相冊(cè)彈窗高度不準(zhǔn)確問(wèn)題
- 支持壓縮配置轧苫,外部添加開(kāi)關(guān) api:[isInnerCompress]
- 完善未選中資源時(shí)各按鈕點(diǎn)擊添加提示
- 修復(fù)不同設(shè)備返回的媒體類型表示不一致(如:JPEG image/jpeg)
- 去除[api setStatusIsDark]楚堤。外部處理狀態(tài)欄,見(jiàn)[api setStatusBarFuture]
2019-10-28 (持續(xù)更新 待發(fā)布)
- 支持相機(jī)拍照完成后多選
- 擴(kuò)展提示方法含懊,支持使用外部自定義彈窗
- 支持外部處理狀態(tài)欄,去除項(xiàng)目中原[ImmersionBar]庫(kù)
.setStatusBarFuture(object : MFunction<BaseActivity> {
override fun accept(params: BaseActivity, view: View?) {
// view為頂部標(biāo)題欄
// 外部設(shè)置狀態(tài)欄
ImmersionBar.with(params)?.run {
statusBarDarkFont(isDarkStatus)
view?.apply { titleBar(this) }
init()
}
// 外部可隱藏Matisse界面中的View
view?.visibility = if (isDarkStatus) View.VISIBLE else View.GONE
}
})
- 按官方方式適配Android Q(未真機(jī)測(cè)試)
2019-10-21 (1.2.2)
- 修復(fù)方形裁剪圖片變形問(wèn)題
- 優(yōu)化單選/多選刷新問(wèn)題
2019-10-18 (1.2.0)
- 遷移到androidx
- 修復(fù)并支持圖片與視頻混合選擇
設(shè)置選擇單一類型媒體衅胀,示例如下
SelectionCreator.choose(MimeTypeManager.ofAll())
.maxSelectable(3)
或者
SelectionCreator.choose(MimeTypeManager.ofAll(), true)
.maxSelectable(3)
設(shè)置選擇混合類型媒體岔乔,示例如下
SelectionCreator.choose(MimeTypeManager.ofAll(), false)
.maxSelectablePerMediaType(4, 2)
說(shuō)明:
mediaTypeExclusive true 單一媒體類型選擇
讀取maxSelectable屬性作為最大值
mediaTypeExclusive false
讀取maxImageSelectable和maxVideoSelectable屬性分別作為最大值
- 修改單/多選邏輯
- 單選支持重新選定,不支持計(jì)數(shù)方式
- 多選不支持重新選定滚躯,選滿外部給出提示方式雏门,支持計(jì)數(shù)與選中方式
- 提示方式外部實(shí)現(xiàn)
SelectionCreator.setNoticeConsumer(object : Consumer<String> {
override fun accept(params: String) {
// 提示方式。 可使用與自己項(xiàng)目相同樣式的Toast或其他
showToast(params)
}
})
2019-10-16
- 完善主題擴(kuò)展掸掏,并提供圖片說(shuō)明
六茁影、重點(diǎn)Api詳細(xì)說(shuō)明
入口:Matisse
, 配置類:SelectionCreator
.from(activity/fragment)
:綁定到當(dāng)前activity/fragment
.choose(...)
:
- @params matisse:用于獲得綁定的activity/fragment
- @params mimeTypes:獲取所需展示的媒體類型丧凤。提供類型有三種MimeTypeManager.ofAll()募闲、MimeTypeManager.ofImage()、MimeTypeManager.ofVideo()或自定義類型MimeTypeManager.of(MimeType.PNG...)
- @params mediaTypeExclusive:是否選擇單一資源
false = 可同時(shí)選擇視頻和圖片
ture = 僅能選擇視頻或者僅能選擇圖片
.countable(countable: Boolean)
:
選中控件是否按數(shù)字編號(hào)愿待,<font color=red>說(shuō)明:?jiǎn)芜x模式下不支持?jǐn)?shù)字編號(hào)</font>
.maxSelectable(maxSelectable: Int)
:
單類型選擇模式下浩螺,最多可選中的數(shù)量,<font color=red>說(shuō)明:maxSelectable屬性在單類型模式下才生效(即mediaTypeExclusive = true)</font>
.maxSelectablePerMediaType(maxImage: Int, maxVideo: Int)
:
混合類型選擇模式下仍侥,視頻和圖片各自最大選擇數(shù)量要出。<font color=red>說(shuō)明:對(duì)應(yīng)兩屬性在混合類型選擇模式下才生效(即mediaTypeExclusive = false)</font>
.addFilter(filter: Filter)
:
添加選中過(guò)濾器,需外部繼承實(shí)現(xiàn)Filter類农渊。指定類型資源結(jié)合指定規(guī)則給出相應(yīng)提示患蹂。例如:不可選擇大于5M的圖片,并吐司提示
.capture(enable: Boolean)
:
是否包含拍照功能砸紊。<font color=red>設(shè)置包含拍照功能時(shí)传于,需設(shè)置strategy</font>
.captureStrategy(captureStrategy: CaptureStrategy)
:
包含拍照功能時(shí)需設(shè)置strategy,7.0必須添加批糟,同時(shí)需在Androidmanifest.xml中注冊(cè)
.restrictOrientation(@ScreenOrientation orientation: Int)
:
強(qiáng)制設(shè)置橫豎屏顯示
.spanCount(spanCount: Int)
:
圖片顯示列數(shù)格了。當(dāng)設(shè)置圖片尺寸屬性(gridExpectedSize)時(shí),則忽略該屬性
.gridExpectedSize(size: Int)
:
設(shè)置圖片期望尺寸徽鼎。根據(jù)圖片期望尺寸計(jì)算圖片展示列數(shù)
.thumbnailScale(scale: Float)
:
設(shè)置圖片期望展示壓縮比
.imageEngine(imageEngine: ImageEngine)
:
設(shè)置圖片加載器盛末,目前暫不支持Fresco
.isCrop(crop: Boolean)
:
是否開(kāi)啟裁剪
.isCropSaveRectangle(cropSaveRectangle: Boolean)
:
圓形裁剪模式下弹惦,裁剪結(jié)果是否以正方形保存
.cropFocusWidthPx(cropFocusWidth: Int)
:
裁剪框px寬度。圓形裁剪寬高取較小值作為直徑
.cropFocusHeightPx(cropFocusHeight: Int)
:
裁剪框px高度悄但。圓形裁剪寬高取較小值作為直徑
.cropStyle(cropStyle: CropImageView.Style)
:
裁剪類型棠隐。圓形裁剪(CropImageView.Style.CIRCLE),方形裁剪(CropImageView.Style.RECTANGLE)
.cropCacheFolder(cropCacheFolder: File)
:
可設(shè)置裁剪后圖片保存的文件
.setNoticeConsumer(noticeConsumer: Consumer<String>?)
:
接口形式實(shí)現(xiàn)提示方式檐嚣,外部提示方式可任意定制
.forResult(requestCode: Int)
:
最后通過(guò)activityForResult方式拿到圖片選擇結(jié)果
.setStatusBarFuture(statusBarFunction: MFunction<BaseActivity>?)
:
設(shè)置狀態(tài)欄相關(guān)助泽,設(shè)置狀態(tài)欄透明、修改狀態(tài)欄字體顏色等
.setLastChoosePicturesIdOrUri(preSelected: ArrayList<String>?)
:
指定資源嚎京,打開(kāi)Matisse后默認(rèn)選中
七嗡贺、部分功能點(diǎn)解讀
* 資源混合選擇
入口處通過(guò)Matisse.choose
方法指定需展示的資源,同時(shí)指定資源選擇方式鞍帝,最終初始化SelectionCreator
構(gòu)造器诫睬,設(shè)置selectionSpec.mediaTypeExclusive
值
Matisse.kt
/**
* @params mimeTypes 需展示的媒體類型
* @params mediaTypeExclusive 設(shè)置資源混合選擇開(kāi)關(guān)
*/
fun choose(mimeTypes: Set<MimeType>, mediaTypeExclusive: Boolean): SelectionCreator {
return SelectionCreator(this, mimeTypes, mediaTypeExclusive)
}
此處主要講支持資源混合選擇的情況,即 selectionSpec.mediaTypeExclusive = false
判斷邏輯主要在SelectedItemCollection.kt
中帕涌,為支持資源混合選擇摄凡,同時(shí)兩類資源分別計(jì)數(shù),此處引入兩個(gè)集合分別裝載圖片與視頻資源imageItems蚓曼、videoItems
具體操作分為三個(gè)階段:
階段一:選中前判斷是否達(dá)到上限亲澡。混合選擇模式下需分開(kāi)判斷已選資源數(shù)量纫版,代碼如下:
/**
* @params item 當(dāng)前選定的資源item
**/
fun maxSelectableReached(item: Item?): Boolean {
// 是否為混合選擇模式
if (!spec.isMediaTypeExclusive()) {
// 根據(jù)當(dāng)前選定資源類型床绪,分別取對(duì)應(yīng)集合與對(duì)應(yīng)最大值對(duì)比
if (item?.isImage() == true) {
return spec.maxImageSelectable == imageItems?.size
} else if (item?.isVideo() == true) {
return spec.maxVideoSelectable == videoItems?.size
}
}
// 非混合選擇模式,直接對(duì)比總集合items
return spec.maxSelectable == items.size
}
// 是否為單一資源選擇模式
fun isMediaTypeExclusive() = mediaTypeExclusive && (maxImageSelectable + maxVideoSelectable == 0)
階段二:將選中的資源添加到總集合items
中捎琐,同時(shí)根據(jù)類型添加到視頻/圖片imageItems/videoItems
集合
fun add(item: Item?): Boolean {
...
val added = items.add(item)
addImageOrVideoItem(item)
...
}
// 根據(jù)當(dāng)前選定的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)
}
}
階段三:取消選中時(shí)会涎,從總集合items
中移除,同時(shí)瑞凑,根據(jù)選擇模式判斷從圖片/視頻集合中移除
private fun removeImageOrVideoItem(item: Item) {
if (item.isImage()) {
imageItems?.remove(item)
} else if (item.isVideo()) {
videoItems?.remove(item)
}
}
* 單選/多選策略
首先定義兩個(gè)方法:
// 是否可單選末秃,混合選擇模式與單一選擇模式均判斷
fun isSingleChoose() = maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1)
// 是否可計(jì)數(shù)
fun isCountable() = countable && !isSingleChoose()
選中操作邏輯如下:
單選模式,由于只會(huì)選中一個(gè)Item籽御,因此去除計(jì)數(shù)支持
單選:
a.選中:刷新當(dāng)前項(xiàng)與上次選擇項(xiàng)
b.取消選中:刷新當(dāng)前項(xiàng)
多選:
1. 按序號(hào)計(jì)數(shù)
a.選中:僅刷新選中的item
b.取消選中:
取消最后一位:僅刷新當(dāng)前操作的item
取消非最后一位:刷新所有選中的item
2. 無(wú)序號(hào)計(jì)數(shù)
a.選中:僅刷新選中的item
b.取消選中:僅刷新選中的item
override fun onCheckViewClicked(
checkView: CheckView, item: Item, holder: RecyclerView.ViewHolder
) {
if (selectionSpec.isSingleChoose()) {
notifySingleChooseData(item)
} else {
notifyMultiChooseData(item)
}
}
/**
* 單選刷新數(shù)據(jù)
*/
private fun notifySingleChooseData(item: Item) {
if (selectedCollection.isSelected(item)) {
// 選中時(shí)练慕,再次點(diǎn)擊則取消選中,只刷新當(dāng)前item位置即可
selectedCollection.remove(item)
notifyItemChanged(item.positionInList)
} else {
// 未選中時(shí)技掏,再次點(diǎn)擊選中铃将,刷新當(dāng)前item,與上次選中的Item
// 刷新上次選中Item哑梳,存在已選中Item則去除選中并刷新
notifyLastItem()
// 添加Item 并刷新當(dāng)前Item位置
if (!addItem(item)) return
notifyItemChanged(item.positionInList)
}
}
多選的選中與反選稍復(fù)雜些劲阎,需支持選中計(jì)數(shù)。計(jì)數(shù)條件下鸠真,選中操作只需添加數(shù)據(jù)刷新列表當(dāng)前Item即可悯仙;取消選中操作時(shí)龄毡,計(jì)數(shù)順序已經(jīng)改變,需根據(jù)當(dāng)前取消Item在選中數(shù)據(jù)集中位置決定需要刷新的列表?xiàng)l目锡垄,當(dāng)取消選中Item為最后一個(gè)Item時(shí)沦零,此時(shí)計(jì)數(shù)順序未改變,因此只需移除Item并刷新最后一個(gè)item即可货岭。反之路操,計(jì)數(shù)順序已經(jīng)改變,則需刷新所有選中Item千贯,重新設(shè)置序號(hào)屯仗。具體代碼如下:
/**
* 多選刷新數(shù)據(jù)
*/
private fun notifyMultiChooseData(item: Item) {
if (selectionSpec.isCountable()) {
// 計(jì)數(shù)模式處理選中刷新
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)
// 取消選中中間序號(hào)時(shí),刷新所有選中item
if (checkedNum != selectedCollection.count() + 1) {
selectedCollection.asList().forEach {
notifyItemChanged(it.positionInList)
}
}
// 別忘了刷新當(dāng)前Item
notifyItemChanged(item.positionInList)
}
return false
}
* 過(guò)濾器
通過(guò)設(shè)置過(guò)濾器搔谴,可在點(diǎn)擊時(shí)根據(jù)過(guò)濾定義的條件決定是否可選中資源祭钉。
過(guò)濾器實(shí)現(xiàn)主要繼承Filter
類。類中提供兩個(gè)抽象方法己沛,一個(gè)供子類調(diào)用的是否攔截方法。
-
abstract fun constraintTypes(): Set<MimeType>
:設(shè)置需過(guò)濾資源的類型距境,返回資源集合 -
abstract fun filter(context: Context, item: Item?): IncapableCause?
:設(shè)定過(guò)濾條件申尼,及提示方式 -
open fun needFiltering(context: Context, item: Item?): Boolean
:判斷item集合是否屬于方法1中定義的集合
例如:添加過(guò)濾器,限定只能選擇小于500kb的圖片
首先需定義一個(gè)過(guò)濾器
/**
* desc:不允許選擇大于itemSize字節(jié)的圖片</br>
* time: 2019/10/23-10:12</br>
* author:Leo </br>
* since V 1.2.2 </br>
*/
class ImageSizeFilter(private var itemSize: Long) : Filter() {
// 設(shè)置需過(guò)濾的資源類型,此處過(guò)濾類型為圖片
override fun constraintTypes() = MimeTypeManager.ofImage()
override fun filter(context: Context, item: Item?): IncapableCause? {
// 1. 判斷當(dāng)前選中的item是否屬于constraintTypes中定義的圖片資源
if (!needFiltering(context, item)) return null
if (item?.size ?: 0 > itemSize) {
// IncapableCause具體實(shí)現(xiàn)在setNoticeConsumer方法中定義
return IncapableCause("需選擇小于${PhotoMetadataUtils.getSizeInMB(itemSize)}M的圖片")
}
return null
}
}
攔截原理:
選中item時(shí)垫桂,會(huì)調(diào)用SelectedItemCollection.kt
類中的isAcceptable(item: Item?)
方法师幕,該方法有三類攔截:選中數(shù)量達(dá)到最大值、單一資源選擇類型下選擇混合資源诬滩、遍歷外部定義過(guò)濾器攔截
fun isAcceptable(item: Item?): IncapableCause? {
if (maxSelectableReached(item)) {
// 類型1霹粥,選中數(shù)量達(dá)到最大
...
return IncapableCause(causeString)
} else if (typeConflict(item)) {
// 類型2,單一資源選擇類型下選擇混合資源疼鸟,給出提示
return IncapableCause(context.getString(R.string.error_type_conflict))
}
// 類型3后控,遍歷外部定義的過(guò)濾器
return PhotoMetadataUtils.isAcceptable(context, item)
}
/**
* 遍歷外部自定義過(guò)濾器
* PhotoMetadataUtils.kt
*/
fun isAcceptable(context: Context, item: Item?): IncapableCause? {
if (!isSelectableType(context, item))
return IncapableCause(context.getString(R.string.error_file_type))
// 遍歷外部自定義的過(guò)濾器
if (SelectionSpec.getInstance().filters != null) {
SelectionSpec.getInstance().filters?.forEach {
return it.filter(context, item)
}
}
return null
}
* 主題
為保證Matisse UI風(fēng)格完全符合主項(xiàng)目,由此擴(kuò)展主題空镜,可在style.xml中修改Matisse庫(kù)中更多控件屬性浩淘。庫(kù)內(nèi)提供了一個(gè)完整的Style作為父類,具體如下:
<style name="Matisse.Default" parent="Theme.AppCompat.Light.NoActionBar">
<!--狀態(tài)欄相關(guān)-->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<!--頂部導(dǎo)航條高度-->
<item name="navigation.height">@dimen/navigation_height</item>
<!--頂部導(dǎo)航條背景色-->
<item name="navigation.background">@color/navigation_bg</item>
<!--頂部導(dǎo)航條返回按鈕資源圖-->
<item name="navigation.backRes">@drawable/icon_arrow_right_white</item>
<!--圖片預(yù)覽界面背景色-->
<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>
<!--返回按鈕文字 當(dāng)無(wú)文字是可設(shè)置為空字符串 如下-->
<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>
<!--確認(rèn)按鈕文字大小-->
<item name="Media.Sure.textSize">@dimen/text_sure</item>
<item name="Media.Preview.text">@string/button_preview</item>
<!--預(yù)覽按鈕顏色-->
<item name="Media.Preview.textColor">@color/text_preview</item>
<!--預(yù)覽按鈕文字大小-->
<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>
<!--查看全部相冊(cè)文件夾按鈕顏色-->
<item name="Media.Album.textColor">@color/text_album</item>
<!--查看全部相冊(cè)按鈕文字大小-->
<item name="Media.Album.textSize">@dimen/text_album</item>
<!--相冊(cè)文件夾內(nèi)部列表item顏色-->
<item name="Media.Album.Item.textColor">@color/text_item_album</item>
<!--查看全部相冊(cè)內(nèi)item文字大小-->
<item name="Media.Album.Item.textSize">@dimen/text_item_album</item>
<!--列表中可拍照狀態(tài)下相機(jī)item文字顏色-->
<item name="Media.Camera.textColor">@color/text_camera</item>
<!--列表中可拍照狀態(tài)下相機(jī)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>
<!--選中控件圓環(huán)邊框顏色-->
<item name="item.checkCircle.borderColor">@color/item_checkCircle_borderColor</item>
<!--原圖radio控件顏色-->
<item name="item.checkRadio">@color/item_checkRadio</item>
<!--空狀態(tài)文字顏色-->
<item name="Media.Empty.textColor">@color/text_empty</item>
<!--空狀態(tài)文字大小-->
<item name="Media.Empty.textSize">@dimen/text_empty</item>
<item name="Media.Empty.text">@string/empty_text</item>
<!--空狀態(tài)資源圖-->
<item name="Media.Empty.resource">@drawable/ic_empty_zhihu</item>
<!--圖片加載占位圖-->
<item name="item.placeholder">@color/zhihu_item_placeholder</item>
</style>
需自定義其他屬性吴攒,可另寫(xiě)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>
屬性對(duì)照?qǐng)D1 | 屬性對(duì)照?qǐng)D2 |
---|---|
* 裁剪
裁剪功能提供了裁剪開(kāi)關(guān)(isCrop)、裁剪類型(cropStyle)洼怔、裁剪框尺寸(cropFocusWidthPx署惯、cropFocusHeightPx)、圓形裁剪支持保存正方形圖(isCropSaveRectangle)镣隶、裁剪圖保存地址(cropCacheFolder)等幾個(gè)屬性极谊。
注:裁剪有效的前提必須是開(kāi)啟裁剪同時(shí)最大選擇數(shù)量為1诡右。
// 是否可單選
fun isSingleChoose() =
maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1)
// 是否可裁剪
fun openCrop() = isCrop && isSingleChoose()
裁剪后不返回uri地址,只返回file path路徑
八怀酷、具體調(diào)用
Matisse api眾多稻爬,須根據(jù)需要自行調(diào)用,大致例子如下
kotlin項(xiàng)目調(diào)用
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項(xiàng)目調(diào)用
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) // 開(kāi)啟裁剪
.cropFocusWidthPx(400) // 設(shè)置裁剪后保存圖片的寬高
.cropFocusHeightPx(400) // 設(shè)置裁剪后保存圖片的寬高
.cropStyle(CropImageView.Style.RECTANGLE) // 方形裁剪CIRCLE為圓形裁剪
.isCropSaveRectangle(true) // 裁剪后保存方形(只對(duì)圓形裁剪有效)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K)) // 篩選數(shù)據(jù)源可選大小限制
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.thumbnailScale(0.8f)
.setStatusIsDark(true) // 設(shè)置狀態(tài)欄文字顏色 需依賴ImmersionBar庫(kù)
.imageEngine(new GlideEngine()) // 加載庫(kù)需外部實(shí)現(xiàn)
.forResult(REQUEST_CODE_CHOOSE);
返回結(jié)果獲得:
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返回值 裁剪結(jié)果不返回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)
// 壓縮后的文件 (多個(gè)文件壓縮可以循環(huán)壓縮)
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"
}
}