O Media3 oferece um PlayerView padrão que oferece algumas opções de
personalização.
Substituir drawables
PlayerView usa PlayerControlView para mostrar os controles de mídia e
a barra de progresso. Os drawables usados por PlayerControlView podem ser substituídos por
drawables com os mesmos nomes definidos no aplicativo. Consulte a
documentação do PlayerControlView para ver uma lista de drawables de controle que podem
ser substituídos.
Para qualquer outra personalização, os desenvolvedores de apps precisam implementar os próprios componentes de interface. No entanto, aqui estão 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), recomendamos que os apps
sigam estas práticas recomendadas para ter a melhor experiência de interface.
Botão para reproduzir ou 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, o Media3 oferece métodos utilitários 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 de IU precisa adicionar um Player.Listener para ser informado sobre mudanças de estado
que exigem uma atualização correspondente da IU. Consulte Detectar eventos de
reprodução para mais detalhes.
Atualizar a IU pode ser caro, e vários eventos do player geralmente chegam
juntos. Para evitar a atualização da interface com muita frequência em um curto período, geralmente é
melhor detectar apenas onEvents e acionar as atualizações da interface a partir daí:
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 precisa funcionar 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 incompatíveis:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));
Obturador do primeiro frame e exibição de imagem
Quando um componente de interface mostra vídeos 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 que a visualização da imagem seja ocultada e mostrada nos momentos apropriados.
Um padrão comum para lidar com essas atualizações é detectar
Player.Listener.onEvents() para qualquer mudança nas faixas selecionadas
(EVENT_TRACKS_CHANGED) e quando o primeiro frame de vídeo for renderizado
(EVENT_RENDERED_FIRST_FRAME), bem como ImageOutput.onImageAvailable()
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, 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. }