图片选择器

Photo picker dialog appears with media files on your device. Select a photo to share with the app.
图 1. 图片选择器提供直观的界面,用于与您的应用共享照片。

图片选择器提供了一个可浏览的界面,向用户展示其媒体库,按日期从新到旧排序。如隐私最佳实践 Codelab 中所示,图片选择器提供了一种安全、内置的方式,让用户可以授予您的应用仅访问选定图片和视频的权限,而不是整个媒体库的权限。

设备上拥有符合条件的云媒体提供商的用户也能够从远程存储的照片和视频中进行选择。了解有关云媒体提供商的更多信息

该工具会自动更新,随着时间的推移为您的应用用户提供扩展功能,而无需任何代码更改。

使用 Jetpack Activity contract

为了简化图片选择器集成,请包含 androidx.activity 库的 1.7.0 或更高版本。

使用以下 activity result contract 启动图片选择器

如果图片选择器在设备上不可用,库会自动调用 ACTION_OPEN_DOCUMENT Intent 操作作为替代。此 Intent 支持运行 Android 4.4 (API 级别 19) 或更高版本的设备。您可以通过调用 isPhotoPickerAvailable() 来验证图片选择器在给定设备上是否可用。

选择单个媒体项

要选择单个媒体项,请使用 PickVisualMedia activity result contract,如以下代码段所示

视图

// Registers a photo picker activity launcher in single-select mode.
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: $uri")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Launch the photo picker and let the user choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
val mimeType = "image/gif"
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType(mimeType)))

视图

// Registers a photo picker activity launcher in single-select mode.
ActivityResultLauncher<PickVisualMediaRequest> pickMedia =
        registerForActivityResult(new PickVisualMedia(), uri -> {
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: " + uri);
    } else {
        Log.d("PhotoPicker", "No media selected");
    }
});

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageAndVideo.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only images.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageOnly.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.VideoOnly.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
String mimeType = "image/gif";
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(new PickVisualMedia.SingleMimeType(mimeType))
        .build());

Compose

// Registers a photo picker activity launcher in single-select mode.
val pickMedia = rememberLauncherForActivityResult(PickVisualMedia()) { uri ->
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: $uri")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Launch the photo picker and let the user choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
val mimeType = "image/gif"
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType(mimeType)))

选择多个媒体项

要选择多个媒体项,请设置可选媒体文件的最大数量,如以下代码段所示。

视图

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
val pickMultipleMedia =
        registerForActivityResult(PickMultipleVisualMedia(5)) { uris ->
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (uris.isNotEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

视图

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
ActivityResultLauncher<PickVisualMediaRequest> pickMultipleMedia =
        registerForActivityResult(new PickMultipleVisualMedia(5), uris -> {
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (!uris.isEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: " + uris.size());
    } else {
        Log.d("PhotoPicker", "No media selected");
    }
});

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageAndVideo.INSTANCE)
        .build());

Compose

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
val pickMultipleMedia =
        rememberLauncherForActivityResult(PickMultipleVisualMedia(5)) { uris ->
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (uris.isNotEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

平台限制了您可以在图片选择器中要求用户选择的最大文件数量。要访问此限制,请调用 getPickImagesMaxLimit()。在不支持图片选择器的设备上,此限制将被忽略。

设备可用性

图片选择器适用于符合以下条件的设备

运行 Android 4.4 (API 级别 19) 到 Android 10 (API 级别 29) 的旧设备以及支持 Google Play 服务的 Android Go 设备(运行 Android 11 或 12)可以安装图片选择器的回溯版本。要通过 Google Play 服务自动安装回溯图片选择器模块,请在应用的清单文件的 <application> 标记中添加以下条目

<!-- Trigger Google Play services to install the backported photo picker module. -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
         android:enabled="false"
         android:exported="false"
         tools:ignore="MissingClass">
    <intent-filter>
        <action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
    </intent-filter>
    <meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>

保留媒体文件访问权限

默认情况下,系统授予您的应用访问媒体文件的权限,直到设备重启或您的应用停止。如果您的应用执行长时间运行的任务(例如在后台上传大文件),您可能需要此访问权限持续更长时间。为此,请调用 takePersistableUriPermission() 方法

Kotlin

val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flag)

Java

int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
context.contentResolver.takePersistableUriPermission(uri, flag);

通过转码处理 HDR 视频

Android 13 (API 33) 引入了捕获高动态范围 (HDR) 视频的功能。虽然 HDR 提供了更丰富的视觉体验,但一些较旧的应用可能无法处理这些较新的格式,导致播放期间出现不自然的色彩渲染问题(例如脸部泛绿)。为了解决这种兼容性差距,图片选择器提供了一项转码功能,可以自动将 HDR 视频转换为标准动态范围 (SDR) 格式,然后再将其提供给请求的应用。

图片选择器转码的主要目标是确保在更广泛的应用范围内提供一致且视觉准确的媒体体验,即使是那些尚未明确支持 HDR 的应用。通过将 HDR 视频转码为 SDR,图片选择器旨在提高应用兼容性并提供无缝的用户体验。

图片选择器转码的工作原理

图片选择器 HDR 转码默认未启用。要启用此功能,您的应用需要在启动图片选择器时明确声明其媒体格式处理能力。

您的应用将媒体处理能力提供给图片选择器。这可以通过使用 AndroidX Activity 库启动图片选择器时,将 mediaCapabilities 添加到 PickVisualMediaRequest.Builder 来完成。一个新的 API setMediaCapabilitiesForTranscoding(capabilities: MediaCapabilities?) 已添加到 PickVisualMediaRequest.Builder 以实现此功能。

您可以使用 MediaCapabilities 类控制 HDR 转码行为。提供一个 MediaCapabilities 对象,精确指定您的应用支持哪些 HDR 类型(例如,TYPE_HLG10TYPE_HDR10TYPE_HDR10_PLUSTYPE_DOLBY_VISION)。

要完全禁用转码,请为 MediaCapabilities 传入 null。您的提供能力中明确列出的任何 HDR 类型都将被视为不支持。此 API 支持 Android 13 (API 级别 33) 及更高版本,并已使用 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 进行注解。

import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.annotation.RequiresApi
import android.os.Build
import android.util.Log
import android.provider.MediaStore

// Registers a photo picker activity launcher.
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    // Callback invoked after media selected or picker activity closed.
    if (uri != null) {
        Log.d("photo picker", "Selected URI: $uri")
    } else {
        Log.d("photo picker", "No media selected")
    }
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun launchPhotoPickerWithTranscodingSupport() {
    val mediaCapabilities = MediaCapabilities.Builder()
        .addSupportedHdrType(MediaCapabilities.HdrType.TYPE_HLG10)
        .build()

    // Launch the photo picker and let the user choose only videos with
    // transcoding enabled.
    pickMedia.launch(PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.VideoOnly)
        .setMediaCapabilitiesForTranscoding(mediaCapabilities)
        .build())
}

图片选择器执行的转码基于应用的媒体功能和所选视频。如果执行转码,则返回转码视频的 URI。

HDR 转码的重要注意事项

  • 性能和存储:转码需要处理时间并创建新文件,这会占用存储空间。
  • 视频长度限制:为了平衡用户体验和存储限制,视频长度限制为 1 分钟。
  • 缓存文件管理:在空闲维护期间会定期清除缓存的转码文件,以防止过度使用存储空间。
  • 设备可用性:图片选择器转码支持 Android 13 (API 级别 33) 及更高版本
  • AndroidX activity 集成:确保您正在使用 AndroidX Activity 库的 1.11.0-alpha01 或更高 alpha/beta/RC/稳定版本,因为它包含必要的 setMediaCapabilitiesForTranscoding API。