O Media3 oferece um PlayerView
padrão com algumas
opções de personalização. Para qualquer outra personalização, os desenvolvedores de apps precisam
implementar os próprios componentes de interface.
Práticas recomendadas
Ao implementar uma interface de mídia que se conecta a um Player
do Media3 (por exemplo,
ExoPlayer
, MediaController
ou uma implementação personalizada de Player
), é recomendável
que os apps sigam estas práticas recomendadas para ter a melhor experiência de interface.
Botão "Reproduzir/pausar"
O botão de reprodução e pausa não corresponde diretamente ao estado de um único jogador. Por exemplo, o usuário precisa conseguir reiniciar a reprodução depois que ela terminar ou falhar, mesmo que o player não esteja pausado.
Para simplificar a implementação, o Media3 oferece métodos úteis para decidir qual
botão mostrar (Util.shouldShowPlayButton
) e processar pressionamentos de botão
(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));
Detectar atualizações de estado
O componente da interface precisa adicionar um Player.Listener
para ser informado sobre mudanças
de estado que exigem uma atualização correspondente da interface. Consulte
Ouvir eventos de reprodução para mais detalhes.
A atualização da interface pode ser cara, e vários eventos de jogadores geralmente chegam
juntos. Para evitar a atualização da interface com muita frequência em um curto período, geralmente é
melhor ouvir apenas onEvents
e acionar as atualizações da interface:
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(); } } });
Processar comandos disponíveis
Um componente de interface geral que pode precisar trabalhar com diferentes implementações
de Player
precisa verificar os comandos do player disponíveis para mostrar ou ocultar
botões e evitar chamar métodos sem suporte:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Obturador do primeiro frame e exibição de imagem
Quando um componente da interface exibe vídeos ou imagens, ele normalmente usa uma visualização de obturador de marcador de posição até que o primeiro frame ou imagem real esteja disponível. Além disso, a reprodução de vídeo e imagem mista exige que a visualização de imagem seja ocultada e mostrada nos momentos adequados.
Um padrão comum para processar essas atualizações é detectar
Player.Listener.onEvents
para qualquer mudança nas faixas selecionadas
(EVENT_TRACKS_CHANGED
) e quando o primeiro frame do vídeo foi
renderizado (EVENT_RENDERED_FIRST_FRAME
), além de
ImageOutput.onImageAvailable
para quando uma nova imagem estiver disponível:
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. }