A partir do Android 8.0 (API de nível 26), o Android permite que as atividades sejam iniciadas no modo picture-in-picture. Picture-in-picture é um tipo especial de modo de várias janelas usado principalmente para reprodução de vídeos. Ele permite que o usuário assista a um vídeo em uma janela pequena fixada em um canto da tela enquanto navega entre apps ou pelo conteúdo na tela principal.
O modo picture-in-picture aproveita as APIs de várias janelas disponíveis no Android 7.0 para fornecer a janela fixa de sobreposição de vídeo. Para adicionar o picture-in-picture ao seu app, você precisa registrar suas atividades compatíveis com esse modo, alternar sua atividade para ele conforme necessário e verificar se os elementos da IU estão ocultos e se a reprodução continua quando a atividade está no modo picture-in-picture.
A janela picture-in-picture aparece na camada superior da tela, em um canto escolhido pelo sistema.
O PiP também tem suporte a dispositivos compatíveis com o SO Android TV que executam o Android 14 (nível 34 da API) ou versões mais recentes. Embora haja muitas semelhanças, há outras considerações ao usar o PIP na TV.
Como os usuários podem interagir com a janela picture-in-picture
É possível arrastar a janela de picture-in-picture para outro local. A partir do Android 12, os usuários também podem fazer o seguinte:
Tocar na janela para exibir um botão de alternância de tela cheia, um botão "Fechar", um botão de configurações e ações personalizadas fornecidas pelo seu app, por exemplo, controles de reprodução.
Toque duas vezes na janela para alternar entre o tamanho do picture-in-picture atual e o tamanho máximo ou mínimo. Por exemplo, tocar duas vezes em uma janela maximizada a minimiza. O contrário também é válido.
Para guardar a janela, arraste-a para a borda esquerda ou direita. Para retomar a janela, toque na parte visível da janela oculta ou arraste-a para fora.
Redimensionar a janela de picture-in-picture usando o gesto de pinça para aplicar zoom.
Seu app controla quando a atividade atual entra no modo picture-in-picture. Veja alguns exemplos:
Uma atividade pode entrar no modo picture-in-picture quando o usuário toca no botão home ou desliza para cima até a tela inicial. É assim que o Google Maps continua mostrando rotas enquanto o usuário executa outra atividade ao mesmo tempo.
Seu app pode colocar um vídeo no modo picture-in-picture quando o usuário sai do vídeo para procurar outro conteúdo.
O app pode alternar um vídeo para o modo picture-in-picture enquanto um usuário assiste o final de um episódio de conteúdo. A tela principal exibe informações promocionais ou resumidas sobre o próximo episódio da série.
Seu app pode oferecer uma forma de os usuários colocarem outros conteúdos em fila enquanto assistem um vídeo. O vídeo continua sendo reproduzido no modo picture-in-picture enquanto a tela principal mostra uma atividade de seleção de conteúdo.
Declarar suporte a picture-in-picture
Por padrão, o sistema não é automaticamente compatível com o picture-in-picture para apps. Se você quiser
oferecer suporte ao picture-in-picture no seu app, registre a atividade de vídeo no manifesto
definindo android:supportsPictureInPicture
como true
. Especifique também que a
atividade processa as mudanças de configuração de layout para que ela não
seja reiniciada quando essas mudanças ocorrerem durante as transições do modo picture-in-picture.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Mudar sua atividade para o modo picture-in-picture
A partir do Android 12, você pode mudar a atividade para o modo picture-in-picture definindo
a flag setAutoEnterEnabled
como true
. Com essa configuração, uma atividade
passa automaticamente para o modo picture-in-picture conforme necessário, sem precisar chamar explicitamente
enterPictureInPictureMode()
em onUserLeaveHint
. Isso tem o
benefício adicional de fornecer transições muito mais suaves. Para saber mais, consulte Suavizar
transições no modo picture-in-picture pela navegação por gestos.
Se você estiver segmentando o Android 11 ou versões anteriores, uma atividade precisará chamar
enterPictureInPictureMode()
para alternar para o modo picture-in-picture. Por exemplo, o código a seguir alterna uma atividade para
o modo picture-in-picture quando o usuário clica em um botão dedicado a isso na IU do app:
Kotlin
override fun onActionClicked(action: Action) { if (action.id.toInt() == R.id.lb_control_picture_in_picture) { activity?.enterPictureInPictureMode() return } }
Java
@Override public void onActionClicked(Action action) { if (action.getId() == R.id.lb_control_picture_in_picture) { getActivity().enterPictureInPictureMode(); return; } ... }
É possível incluir uma lógica que alterna uma atividade para o modo picture-in-picture em vez
de colocá-la em segundo plano. Por exemplo, o Google Maps alternará para o modo picture-in-picture se o usuário pressionar o botão "Início" ou "Recentes"
enquanto o app estiver navegando. Para usar esse
caso, modifique
onUserLeaveHint()
:
Kotlin
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Java
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Recomendado: ofereça aos usuários uma experiência de transição do PiP aprimorada
O Android 12 adicionou melhorias estéticas significativas às transições animadas entre a tela cheia e as janelas picture-in-picture. É altamente recomendável implementar todas as mudanças aplicáveis. Depois disso, essas mudanças são dimensionadas automaticamente para telas grandes, como dobráveis e tablets, sem a necessidade de mais trabalho.
Se o app não incluir atualizações aplicáveis, as transições picture-in-picture ainda vão estar funcionais, mas as animações serão menos refinadas. Por exemplo, a transição de tela cheia para o modo PiP pode fazer com que a janela PiP desapareça durante a transição antes de reaparecer quando a transição for concluída.
Essas mudanças envolvem o seguinte:
- Suavizar transições no modo picture-in-picture pela navegação por gestos
- Como definir um
sourceRectHint
adequado para entrar e sair do modo picture-in-picture - Como desativar o redimensionamento contínuo de conteúdos que não são de vídeo
Consulte o exemplo do Android Kotlin PictureInPicture como referência para ativar uma experiência de transição aprimorada.
Suavizar transições no modo picture-in-picture pela navegação por gestos
No Android 12 e versões mais recentes, a flag setAutoEnterEnabled
oferece uma animação
muito mais suave para a transição para conteúdo de vídeo no modo picture-in-picture usando a navegação
por gestos, por exemplo, ao deslizar da tela cheia para cima.
Conclua as etapas a seguir para fazer essa mudança e consulte este exemplo como referência:
Use
setAutoEnterEnabled
para criarPictureInPictureParams.Builder
:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Chame
setPictureInPictureParams
usando osPictureInPictureParams
atualizados anteriormente. O app não espera o callbackonUserLeaveHint
, como faria no Android 11.Por exemplo, talvez você queira chamar
setPictureInPictureParams
na primeira reprodução e em qualquer reprodução seguinte se a proporção mudar.Chame
setAutoEnterEnabled(false)
, mas só se for necessário. Por exemplo, você provavelmente não vai querer inserir o picture-in-picture se a reprodução atual estiver em um estado pausado.
Definir um sourceRectHint
adequado para entrar e sair do modo PiP
A partir da introdução do PiP no Android 8.0, setSourceRectHint
indicava a área da atividade que fica visível após a transição para
picture-in-picture, por exemplo, os limites de visualização de vídeo em um player de vídeo.
Com o Android 12, o sistema usa sourceRectHint
para implementar uma animação muito mais suave
ao entrar e sair do modo picture-in-picture.
Para definir corretamente sourceRectHint
para entrar e sair do modo picture-in-picture:
Crie
PictureInPictureParams
usando os limites adequados comosourceRectHint
. Recomendamos também anexar um listener de mudança de layout ao player de vídeo:Kotlin
val mOnLayoutChangeListener = OnLayoutChangeListener { v: View?, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop: Int, newRight: Int, newBottom: Int -> val sourceRectHint = Rect() mYourVideoView.getGlobalVisibleRect(sourceRectHint) val builder = PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) setPictureInPictureParams(builder.build()) } mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
Java
private final View.OnLayoutChangeListener mOnLayoutChangeListener = (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> { final Rect sourceRectHint = new Rect(); mYourVideoView.getGlobalVisibleRect(sourceRectHint); final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint); setPictureInPictureParams(builder.build()); }; mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
Se necessário, atualize a
sourceRectHint
antes que o sistema inicie a transição de saída. Quando o sistema está prestes a sair do modo picture-in-picture, a hierarquia de visualização da atividade é destinada à configuração de destino (por exemplo, tela cheia). O app pode anexar um listener de mudança de layout à visualização raiz ou de destino (como a visualização do player de vídeo) para detectar o evento e atualizar osourceRectHint
antes do início da animação.Kotlin
// Listener is called immediately after the user exits PiP but before animating. playerView.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. val sourceRectHint = Rect() playerView.getGlobalVisibleRect(sourceRectHint) setPictureInPictureParams( PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build() ) } }
Java
// Listener is called right after the user exits PiP but before animating. playerView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. final Rect sourceRectHint = new Rect(); playerView.getGlobalVisibleRect(sourceRectHint); setPictureInPictureParams( new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build()); } });
Desativar o redimensionamento contínuo de conteúdos que não são de vídeo
O Android 12 adiciona a flag setSeamlessResizeEnabled
, que oferece uma animação de esmaecimento
muito mais suave ao redimensionar conteúdo que não é de vídeo na janela
picture-in-picture. Antes, o redimensionamento de conteúdo que não era de vídeo em uma janela picture-in-picture poderia criar
artefatos visuais desagradáveis.
Para desativar o redimensionamento contínuo de conteúdos que não são de vídeo:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build());
Processar a IU durante o PiP
Quando a atividade entra ou sai do modo picture-in-picture (PiP), o sistema chama Activity.onPictureInPictureModeChanged()
ou Fragment.onPictureInPictureModeChanged()
.
O Android 15 apresenta mudanças que garantem uma transição ainda mais suave ao entrar no modo picture-in-picture. Isso é benéfico para apps que têm elementos de interface sobrepostos à interface principal, que entra no PiP.
Os desenvolvedores usam o callback onPictureInPictureModeChanged()
para definir a lógica que alterna a visibilidade dos elementos da interface sobrepostos.
Esse callback é acionado quando a animação de entrada ou saída do PiP é concluída.
A partir do Android 15, a classe PictureInPictureUiState
inclui um novo estado.
Com esse novo estado da interface, os apps destinados ao Android 15 observam o callback Activity#onPictureInPictureUiStateChanged()
sendo invocado com isTransitioningToPip()
assim que a animação PiP começa.
Há muitos elementos de interface que não são relevantes para o app quando ele está no modo PiP,
por exemplo, visualizações ou layouts que incluem informações como sugestões, próximos
vídeos, classificações e títulos. Quando o app entrar no modo picture-in-picture, use o callback onPictureInPictureUiStateChanged()
para ocultar esses elementos da interface. Quando o
app entrar no modo de tela cheia na janela picture-in-picture, use o callback onPictureInPictureModeChanged()
para reexibir esses elementos, conforme mostrado nos exemplos a seguir:
Kotlin
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Java
@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Kotlin
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Java
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Essa alternância rápida de visibilidade de elementos irrelevantes da interface (para uma janela picture-in-picture) ajuda a garantir uma animação de entrada no modo picture-in-picture mais suave e sem piscar.
Substitua esses callbacks para redesenhar os elementos de interface da atividade. Lembre-se de que, no modo picture-in-picture, sua atividade é mostrada em uma pequena janela. Os usuários não podem interagir com os elementos da interface do app quando ele está no modo picture-in-picture, e os detalhes de pequenos elementos da interface podem ser difíceis de ver. As atividades de reprodução de vídeo com IU mínima oferecem a melhor experiência do usuário.
Se o app precisar oferecer ações personalizadas para picture-in-picture, consulte Adicionar controles nesta página. Remova outros elementos da interface antes que sua atividade entre no picture-in-picture e restaure-os quando ela voltar à tela cheia.
Adicionar controles
A janela de picture-in-picture pode exibir controles quando o usuário abre o menu da janela, tocando nela em um dispositivo móvel ou selecionando o menu no controle remoto da TV.
Se um app tiver uma sessão de mídia ativa, os controles de tocar, pausar, próxima e anterior serão exibidos.
Também é possível especificar ações personalizadas de forma explícita criando
PictureInPictureParams
com
PictureInPictureParams.Builder.setActions()
antes de entrar no modo picture-in-picture, além de transmitir os parâmetros ao entrar nesse modo usando
enterPictureInPictureMode(android.app.PictureInPictureParams)
ou
setPictureInPictureParams(android.app.PictureInPictureParams)
.
Tenha cuidado. Se você tentar adicionar mais do que
getMaxNumPictureInPictureActions()
,
conseguirá apenas o número máximo.
Como continuar a reprodução de vídeo no modo picture-in-picture
Quando sua atividade alterna para o modo picture-in-picture, o sistema coloca a atividade no estado
pausado e chama o método
onPause()
da atividade. A reprodução
de vídeo não poderá ser pausada. Em vez disso, continue se a atividade for
pausada durante a transição para o modo picture-in-picture.
No Android 7.0 e versões mais recentes, pause e retome a reprodução do vídeo quando o
sistema chamar o onStop()
e
onStart()
da atividade. Ao fazer isso,
você evita a necessidade de verificar se o app está no modo picture-in-picture em onPause()
e
continua a reprodução de forma explícita.
Se você não tiver definido a flag setAutoEnterEnabled
como true
e precisar
pausar a reprodução na implementação de onPause()
, verifique o modo PiP chamando
isInPictureInPictureMode()
e gerencie a reprodução de maneira adequada. Exemplo:
Kotlin
override fun onPause() { super.onPause() // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode) { // Continue playback. } else { // Use existing playback logic for paused activity behavior. } }
Java
@Override public void onPause() { // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode()) { // Continue playback. ... } else { // Use existing playback logic for paused activity behavior. ... } }
Quando a atividade sai do modo picture-in-picture e volta para o modo de tela cheia, o sistema
retoma a atividade e chama o método
onResume()
.
Usar uma única atividade de reprodução para picture-in-picture
No seu app, um usuário pode selecionar um novo vídeo ao navegar pelo conteúdo na tela principal, enquanto uma atividade de reprodução de vídeo estiver no modo picture-in-picture. Reproduza o novo vídeo na atividade de reprodução existente no modo de tela cheia em vez de iniciar uma nova atividade que pode confundir o usuário.
Para garantir que uma única atividade seja usada para solicitações de reprodução de vídeo e ativada ou desativada
no modo picture-in-picture conforme necessário, defina o android:launchMode
da atividade como
singleTask
no manifesto:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
Na sua atividade, substitua
onNewIntent()
e gerencie o novo vídeo, interrompendo qualquer reprodução de vídeo já existente, se necessário.
Práticas recomendadas
O modo picture-in-picture pode ser desativado em dispositivos com pouca RAM. Antes que seu app use o modo picture-in-picture,
confira se ele está disponível chamando
hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
O modo picture-in-picture é destinado a atividades que exibem vídeos em tela cheia. Ao mudar sua atividade para o modo picture-in-picture, evite exibir conteúdo que não seja de vídeo. Acompanhe quando sua atividade entra no modo picture-in-picture e oculte elementos da interface, conforme descrito em Como gerenciar a interface durante o picture-in-picture.
Quando uma atividade está no modo picture-in-picture, por padrão, ela não recebe o foco de entrada. Para
receber eventos de entrada no modo picture-in-picture, use
MediaSession.setCallback()
.
Para saber mais sobre como usar setCallback()
, consulte Mostrar um card "Tocando
agora".
Quando o app está no modo picture-in-picture, a reprodução de vídeo na janela picture-in-picture pode causar interferência de áudio com outro app, como um player de música ou um app de pesquisa por voz. Para evitar isso, solicite a seleção de áudio quando começar a reproduzir o vídeo e processe as notificações de mudança de seleção de áudio, conforme descrito em Gerenciar a seleção de áudio. Se você receber uma notificação sobre perda de seleção de áudio quando estiver no modo picture-in-picture, pause ou pare a reprodução do vídeo.
Quando seu app estiver prestes a entrar no modo picture-in-picture, apenas a atividade principal vai entrar
no modo picture-in-picture. Em algumas situações, como em dispositivos com várias janelas, é
possível que a atividade abaixo seja exibida e se torne visível novamente junto com
a atividade do modo picture-in-picture. Gerencie esse caso adequadamente, incluindo a
atividade abaixo, que recebe um callback onResume()
ou onPause()
. Também é
possível que o usuário interaja com a atividade. Por exemplo, se uma atividade
de lista de vídeos estiver sendo exibida e a atividade de reprodução de vídeo estiver no modo picture-in-picture, o
usuário poderá selecionar um novo vídeo da lista, e essa atividade será atualizada
de acordo.
Outra amostra de código
Para fazer o download de um app de exemplo criado em Kotlin, consulte Amostra PictureInPicture do Android (Kotlin) (link em inglês).