Media3 提供了一个默认的 PlayerView
,它提供了一些 自定义选项。对于任何进一步的自定义,应用程序开发者需要实现他们自己的 UI 组件。
最佳实践
在实现连接到 Media3 Player
(例如 ExoPlayer
、MediaController
或自定义 Player
实现)的媒体 UI 时,建议应用程序遵循以下最佳实践以获得最佳的 UI 体验。
播放/暂停按钮
播放和暂停按钮并不直接对应于单个播放器状态。例如,即使播放器未处于暂停状态,用户也应该能够在播放结束或失败后重新开始播放。
为了简化实现,Media3 提供了实用方法来确定显示哪个按钮(Util.shouldShowPlayButton
)以及处理按钮按下事件(Util.handlePlayPauseButtonAction
)。
Kotlin
val shouldShowPlayButton: Boolean = Util.shouldShowPlayButton(player) playPauseButton.setImageDrawable(if (shouldShowPlayButton) playDrawable else pauseDrawable) playPauseButton.setOnClickListener { Util.handlePlayPauseButtonAction(player) }
Java
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player); playPauseButton.setImageDrawable(shouldShowPlayButton ? playDrawable : pauseDrawable); playPauseButton.setOnClickListener(view -> Util.handlePlayPauseButtonAction(player));
监听状态更新
UI 组件需要添加一个 Player.Listener
来接收需要相应 UI 更新的状态更改通知。有关详细信息,请参见 监听播放事件。
刷新 UI 可能很耗费资源,并且多个播放器事件通常会同时到达。为了避免在短时间内过于频繁地刷新 UI,通常最好只监听 onEvents
并从中触发 UI 更新。
Kotlin
player.addListener(object : Player.Listener{ override fun onEvents(player: Player, events: Player.Events){ if (events.containsAny( Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) { updatePlayPauseButton() } if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) { updateRepeatModeButton() } } })
Java
player.addListener(new Player.Listener() { @Override public void onEvents(Player player, Player.Events events) { if (events.containsAny( Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) { updatePlayPauseButton(); } if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) { updateRepeatModeButton(); } } });
处理可用命令
可能需要与不同 Player
实现一起工作的通用 UI 组件应该检查可用的播放器命令,以便显示或隐藏按钮并避免调用不支持的方法。
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
第一帧快门和图像显示
当 UI 组件显示视频或图像时,它通常使用一个占位符快门视图,直到真正的第一帧或图像可用。此外,混合视频和图像播放需要在适当的时候隐藏和显示图像视图。
处理这些更新的常见模式是监听 Player.Listener.onEvents
以获取所选轨道(EVENT_TRACKS_CHANGED
)的任何更改以及第一个视频帧已渲染(EVENT_RENDERED_FIRST_FRAME
)的时间,以及 ImageOutput.onImageAvailable
以获取新图像可用时的时间。
Kotlin
override fun onEvents(player: Player, events: Player.Events) { if (events.contains(Player.EVENT_TRACKS_CHANGED)) { // If no video or image track: show shutter, hide image view. // Otherwise: do nothing to wait for first frame or image. } if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) { // Hide shutter, hide image view. } } override fun onImageAvailable(presentationTimeUs: Long, bitmap: Bitmap) { // Show shutter, set image and show image view. }
Java
@Override public void onEvents(Player player, Events events) { if (events.contains(Player.EVENT_TRACKS_CHANGED)) { // If no video or image track: show shutter, hide image view. // Otherwise: do nothing to wait for first frame or image. } if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) { // Hide shutter, hide image view. } } @Override public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) { // Show shutter, set image and show image view. }