A Media3 oferece um PlayerView
padrão com algumas opções de personalização.
Substituir drawables
O PlayerView
usa PlayerControlView
para mostrar os controles de reprodução e a barra de progresso. Os elementos gráficos usados por PlayerControlView
podem ser substituídos por
elementos gráficos com os mesmos nomes definidos no seu aplicativo. Consulte a documentação
PlayerControlView
para ver uma lista de elementos gráficos de controle que podem
ser substituídos.
Para mais personalizações, os desenvolvedores de apps precisam implementar os próprios componentes de interface. No entanto, confira algumas práticas recomendadas que podem ajudar você a começar.
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
), os apps precisam seguir estas práticas recomendadas para ter a melhor experiência de UI.
Botão "Reproduzir/pausar"
O botão de iniciar e pausar não corresponde diretamente a um único estado do player. Por exemplo, um 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, a Media3 oferece métodos utilitários para decidir qual
botão mostrar (Util.shouldShowPlayButton
) e para 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 UI precisa adicionar um Player.Listener
para ser informado sobre mudanças de estado
que exigem uma atualização correspondente da UI. Consulte Ouvir eventos de
reprodução para mais detalhes.
Atualizar a interface pode ser caro, 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 de lá:
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 de uso geral que pode precisar trabalhar com diferentes implementações de Player
deve 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 imagens
Quando um componente de UI mostra vídeo ou imagens, ele geralmente 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 mista de vídeo e imagem exige ocultar e mostrar a visualização da imagem em momentos adequados.
Um padrão comum para processar essas atualizações é ficar atento a
Player.Listener.onEvents()
para qualquer mudança nas faixas selecionadas
(EVENT_TRACKS_CHANGED
) e quando o primeiro frame de vídeo é renderizado
(EVENT_RENDERED_FIRST_FRAME
), além de ImageOutput.onImageAvailable()
para quando uma nova imagem está 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. }