【媒体文件选择器】知乎 Matisse 全功能解析:Android 开发者必看的图片与视频选择器指南

AI助手已提取文章相关产品:

知乎 Matisse 全功能解析:Android 开发者必看的图片与视频选择器指南


前言

在 Android 应用开发中,从相册选择图片/视频 是几乎所有内容型应用都会遇到的需求,例如:

  • 社交 App 发布动态上传图片
  • IM 应用发送图片/视频
  • 电商 App 上传商品照片
  • 企业级应用拍照并上传材料

虽然原生 API 也能实现媒体选择,但界面复杂、设备兼容性差,图片压缩/旋转/格式解析都需开发者自行处理。

为此,知乎开源了一个极其优秀的媒体选择器库 —— Matisse

Matisse 是一个高度易用、可扩展、风格统一、兼容性很好的媒体选择框架,支持:

  • 图片选择
  • 视频选择
  • 混合选择
  • 多选
  • 拍照
  • 过滤器
  • 原图模式
  • 大图预览
  • 自定义图片加载引擎(Glide/Coil/Picasso)

本文将为你完整解析 Matisse 的功能、使用方式、源码结构及最佳实践,从入门到进阶,一文掌握。


1. Matisse 是什么?

Matisse 是知乎团队开发的 Android 媒体选择器,提供统一、优雅的 UI,并极大简化媒体读取流程。

github地址:https://github.com/zhihu/Matisse

它的优点包括:

统一的媒体选择 UI

无需自己写 Adapter、Loader、Thumbnail,直接使用 Material 风格界面。

支持图片、视频、混合选择

可自行决定支持哪些 MIME 类型。

支持多选、计数模式

如朋友圈发动态那样的“1/9、2/9”。

支持从相册直接调用“拍一张”

并支持 FileProvider 权限适配。

极强的扩展性

可接入 Glide、Picasso、Coil 等任意图片库。

完整的结果回调

提供 Uri、绝对路径、Original 状态,方便上传处理。

简而言之,如果你需要一个既好看又好用、可定制性强的媒体选择器,Matisse 是最成熟的选择之一。


2. 如何集成 Matisse?

Gradle 添加依赖:

repositories {
    jcenter()
}

dependencies {
    implementation 'com.zhihu.android:matisse:$latest_version'
}

如果使用 其它版本(fork 版本),保持 API 接口一致。

另外,使用拍照功能时必须注册 FileProvider:

<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"/>
</provider>

3. 基础使用示例(最常用的媒体选择方式)

调用 Matisse:

Matisse.from(activity)
        .choose(MimeType.ofImage())
        .countable(true)
        .maxSelectable(9)
        .imageEngine(new GlideEngine())
        .forResult(REQUEST_CODE_CHOOSE);

onActivityResult 接收结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {
        List<Uri> uris = Matisse.obtainResult(data);
        List<String> paths = Matisse.obtainPathResult(data);
    }
}

到这里,你已经可以像微信公众号、微博、知乎一样选择图片了。


4. 主题与 UI 配置

Matisse 内置两种主题:

系统默认主题(亮色)

.theme(R.style.Matisse_Zhihu)

Dracula(暗色主题)

.theme(R.style.Matisse_Dracula)

想要自定义?
直接重写主题中的:

  • Toolbar 颜色
  • Bottom Action Bar 颜色
  • 状态栏模式(亮/暗)

完全可高度定制 UI 风格。

示例:

    //====================================== Theme Zhihu ===========================================

    <style name="Matisse.Zhihu" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/zhihu_primary</item>
        <item name="colorPrimaryDark">@color/zhihu_primary_dark</item>
        <item name="toolbar">@style/Toolbar.Zhihu</item>
        <item name="album.dropdown.title.color">@color/zhihu_album_dropdown_title_text</item>
        <item name="album.dropdown.count.color">@color/zhihu_album_dropdown_count_text</item>
        <item name="album.element.color">@android:color/white</item>
        <item name="album.thumbnail.placeholder">@color/zhihu_album_dropdown_thumbnail_placeholder</item>
        <item name="album.emptyView">@drawable/ic_empty_zhihu</item>
        <item name="album.emptyView.textColor">@color/zhihu_album_empty_view</item>
        <item name="item.placeholder">@color/zhihu_item_placeholder</item>
        <item name="item.checkCircle.backgroundColor">@color/zhihu_item_checkCircle_backgroundColor</item>
        <item name="item.checkCircle.borderColor">@color/zhihu_item_checkCircle_borderColor</item>
        <item name="page.bg">@color/zhihu_page_bg</item>
        <item name="bottomToolbar.bg">@color/zhihu_bottom_toolbar_bg</item>
        <item name="bottomToolbar.preview.textColor">@color/zhihu_bottom_toolbar_preview</item>
        <item name="bottomToolbar.apply.textColor">@color/zhihu_bottom_toolbar_apply</item>
        <item name="preview.bottomToolbar.back.textColor">@color/zhihu_preview_bottom_toolbar_back_text</item>
        <item name="preview.bottomToolbar.apply.textColor">@color/zhihu_preview_bottom_toolbar_apply</item>
        <item name="listPopupWindowStyle">@style/Popup.Zhihu</item>
        <item name="capture.textColor">@color/zhihu_capture</item>
    </style>

5. 拍照功能(Capture)

Matisse 提供一个“拍照”按钮,会启动系统相机。

启用拍照:

.capture(true)
.captureStrategy(new CaptureStrategy(true, applicationId + ".fileprovider"))

各fork分支大同小异

.capture(AlbumMediaLoader.Capture.Image)

它会在相册第一行添加:

拍一张

点击后打开系统相机,照片自动写入 FileProvider 目录。

注意

Matisse 不提供自定义相机 UI,仅调用系统相机。


6. 拍照存储策略 CaptureStrategy

new CaptureStrategy(
    true,                          // 是否公开存储目录(public)
    applicationId + ".fileprovider",
    "Pictures"                     // 可选,自定义目录
)

你可以控制:

  • 是否写入公共存储
  • 是否需要 Uri 权限
  • 拍照保存的路径

非常适合需要持久化图片的业务。


7. 媒体 Engine:Glide / Picasso / Coil

Matisse 不直接加载图片,可使用三方库。

使用 Glide:

.imageEngine(new GlideEngine())

也可以实现自定义 Engine:

public class CoilEngine implements ImageEngine {
    // 自己实现 loadThumbnail、loadGif 等方法
}

优点:

  • Matisse 不绑定任何图片库
  • 项目自己决定使用 Glide / Picasso / Fresco / Coil

8. 原图模式(Original)

开启“原图”按钮:

.originalEnable(true)
.maxOriginalSize(10) // 单个文件最大 10MB

界面底部会出现:

  • 原图 checkbox
  • 原图大小提示

如果文件大小超过设定值,将无法选择。

结果中会标记:

Matisse.obtainOriginalState(data);

9. Filter 过滤器(自定义筛选)

Matisse 提供 Filter 接口来过滤媒体。

示例:过滤大于 5MB 的 GIF

.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))

你可以自定义:

  • 图片大小限制
  • 分辨率限制
  • 视频时长限制
  • 禁用 GIF

示例:禁止长图(高度 > 4000px)

public class LongImageFilter extends Filter {
    @Override
    public Set<MimeType> constraintTypes() {
        return MimeType.ofImage();
    }

    @Override
    public boolean filter(Context context, Item item) {
        return item.getHeight() > 4000;
    }
}

10. 视频选择功能

Matisse 支持读取视频文件,包含:

  • 缩略图
  • 时长显示
  • 点击播放(系统播放器)

配置示例:

.choose(MimeType.ofVideo())
.maxSelectable(2)

也支持混合选择:

.choose(MimeType.ofAll())
.maxSelectablePerMediaType(3, 1) // 图片最多3,视频最多1

11. orientation 屏幕方向锁定

避免进入相册后屏幕旋转导致 Activity 重建,可以:

.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)

常用取值:

  • PORTRAIT(竖屏)
  • LANDSCAPE(横屏)
  • SENSOR(自动旋转)

如果你的 App 是横屏游戏或相机 App,这个非常关键(能避免 CameraAccessException)。


12. 支持的返回结果类型

回调中可获得:

Uri 列表(推荐 Android 10+)

Matisse.obtainResult(data)

绝对路径(Android 10 需要额外权限)

Matisse.obtainPathResult(data)

原图选择状态

Matisse.obtainOriginalState(data)

这些足够用于图片上传、压缩等业务逻辑。


13. 完整的媒体信息 Item 结构(高级扩展)

Matisse 内部使用 Item 存储媒体信息,包含:

  • id
  • MIME Type
  • Uri
  • Path
  • 宽/高
  • 文件大小
  • 视频时长
  • 是否 GIF

可以用来做:

  • 大图预览
  • 视频封面提取
  • 文件验证
  • 压缩逻辑判断

14. Matisse 的内部架构(深入理解)

Matisse 的结构大致分为:

MatisseActivity

主入口界面,展示文件夹/媒体列表。

MediaStoreCompat

封装拍照、Uri、FileProvider 写入行为。

AlbumCollection

负责从 MediaStore 加载媒体。

SelectedItemCollection

维持当前选中的媒体状态。

PreviewActivity / PreviewItemFragment

大图预览界面。

Filter

媒体过滤逻辑。

ImageEngine

图片加载引擎。

架构非常清晰,可任意扩展 UI 或行为。


15. 常见问题与实践建议

Android 13 权限适配

需要手动申请:

READ_MEDIA_IMAGES
READ_MEDIA_VIDEO

避免 CameraAccessException(相机页 → Matisse → 返回)

如果你的 Activity 是横屏相机页面,请务必:

.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)

否则 Activity 重建会导致 Camera 重启冲突。

GlideEngine 使用错误可能造成大图加载失败

确保使用:

Glide.with(context)

而不是 applicationContext。

不显示 GIF 预览?

需要在 choose 中加 GIF

.choose(MimeType.of(MimeType.JPEG, MimeType.GIF))

16. 完整 Matisse 配置示例(可直接使用)

Matisse.from(activity)
        .choose(MimeType.ofAll())       // 图片 + 视频
        .theme(R.style.Matisse_Dracula)
        .countable(true)
        .maxSelectablePerMediaType(9, 1) // 图片最多9,视频最多1
        .capture(true)
        .captureStrategy(new CaptureStrategy(true, applicationId + ".fileprovider"))
        .imageEngine(new GlideEngine())
        .originalEnable(true)
        .maxOriginalSize(10)
        .addFilter(new LongImageFilter())
        .showSingleMediaType(false)
        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
        .forResult(REQUEST_CODE_PICK);

这是适用于大多数 App 的配置。


您可能感兴趣的与本文相关内容

评论 44
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQ-雪梨蛋花汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值