如何在安卓新版Photo Picker中限定复数个MIMEtype
关于安卓的新Photo Picker
从 Android 13 开始,Android 系统内置了一个功能更为强大的图片选择器,可以让应用更加灵活地访问设备中的媒体资源,且无需拥有查看设备上所有媒体文件的权限。此外,虽然官方将其命名为图片选择器,但实际上也支持选择设备中的视频文件。
引用 Google 官方的描述:
照片选择器提供了一个可浏览、可搜索的界面,其中按日期从最近到最早的顺序向用户呈现其媒体库中的文件。此工具为用户提供了一种安全的内置图片和视频选择方式,让其无需向应用授予对整个媒体库的访问权限。
如果您允许系统配置照片选择器,则该工具适用于满足以下条件的设备(Android Go 设备除外):
- 搭载 Android 11(API 级别 30)或更高版本
- 通过 Google 系统更新接收对模块化系统组件的更改
此外,如果您允许系统配置照片选择器,该工具会自动更新,并随着时间推移为应用的用户提供扩展的功能,而无需对代码进行任何更改。

如果你想了解更多,可以参考下面谷歌的官方文档,这里就不多做解释了。
照片选择器 | Android 开发者 | Android Developers (google.cn)
Photo Picker不方便的地方
那么如果你已经初步开始使用了Photo Picker,你应该已经知道了可以通过限制MIME种类的方式,来限制其供用户选择的文件种类。
如果你不知道MIME是什么,简单来说它是一个互联网的邮件类型标准,用来对文件类型做划分,下面是谷歌官方的参照列表,可以看到MIME本身就是一个字符串。
MimeTypes | Android Developers (google.cn)
那么如果要限制MIME种类来限制文件类型,就像下面的代码,这里我使用Java,我比较熟悉Java。
1 | // Launch the photo picker and let the user choose only images/videos of a |
你可能已经发现,不仅看起来只能写一个mimeType,而且对应方法也叫做SingleMimeType,明显也是只能添加一个mimeType字符串,查看源码发现也没有方法支持字符串数组。
那么如果有如下这种场景,你想让用户上传头像,但是你不想在用户选择的时候出现动图,例如GIF类型,但是你又想同时筛选出JPEG类型和PNG类型的图片供用户选择,这时你需要填写两个mimeType了,那么该怎么办呢?
可能有人跟我一样看到上面的联级调用,认为谷歌不会做这种奇怪的鸡肋操作,所以在读源码之前先尝试了如下的写法:
1 | // Launch the photo picker and let the user choose only images/videos of a |
那么,显而易见,看看命名就能知道,这里只是set了两次mediaType,换句话说,你用png的筛选覆盖了jpeg的筛选,这自然没法达到我们的要求,那么该怎么实现上面的需求呢?
我们先看一下关于PickVisualMedia类如何createIntent的一部分代码。
1 |
|
稍微读了一下源码我发现,安卓的开发者在系统的Picker可用的情况下,用了MediaStore类的一个Intent,直接把我们设定的mediaType丢给它来进行筛选,下面则是一些低版本适配代码,但读到最后关于太旧系统,以及系统不含PhotoPicker的情况下,使用ACTION_OPEN_DOCUMENT这个Intent兼容图片选择功能的时候,发现在下面type为空的时候,有一句putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*")),给到我启发,这里将type置为*/*之后,往Intent中放了一组字符串数组,那么我们是不是也可以依葫芦画瓢,重载对应的PickVisualMedia类的createIntent方法,来实现我们所需要的功能呢。
重载PickVisualMedia类的createIntent
PickVisualMedia类有不少createIntent,这里我们要处理上面对应的和PickVisualMediaRequest类相关的createIntent,虽然源码使用Kotlin,但由于对于Kotlin的不熟悉,我还是使用Java做重载。
1 |
|
这里我们先调用父类的createIntent,拿到中间的Intent,然后可以有样学样,调用Intent类的putExtra,加上我们的mimeType字符串数组,再把修改好的Intent传递给下一个步骤,这也是很常用的一种重载包装方法,在父类的处理层次上,加上一点我们自己的处理。
那么还有一点需要注意,安卓官方关于常用Intent的文档里面提到:
EXTRA_MIME_TYPES
An array of MIME types corresponding to the types of files your app is requesting. When you use this extra, you must set the primary MIME type in
setType()to"*/*".
简单来说就是,使用这个附加Intent的时候,需要把主要的MIME种类置为*/*,这也解释了上面官方代码的写法。
那也很简单,在SetMediaType的时候调整就行了。
最后给出两段修改之后的调用业务代码,首先这里记得把PickVisualMedia()改成我们重载之后的子类PickImage()。
1 | pickMedia = registerForActivityResult(new PickImage(), uri -> { |
然后对应把launch方法里面的MediaType改成*/*即可。
1 | pickMedia.launch(new PickVisualMediaRequest.Builder() |
最后调试运行一下,应该就能看到我们所需要的jpeg和png并存的效果了。
后记
最后还是感慨搞安卓开发果然还是应该转Kotlin,特别是在做官方代码自定义的时候。安卓开发Java和Kotlin并存的情况应该还会不可避免地持续很长一段时间,毕竟安卓还有很多第三方Java库,而谷歌的官方库则是全面转向Kotlin,开发项目的时候自然很难去完全倒向任何一边。
不过现在流行跨平台的时代还在搞安卓,恐怕是真没啥前途了吧(笑





![[COCI 2025/2026 #2] Natjecanje 题解](/img/natjecanje/header.jpg)
