Telas de apresentação

O Android 12 oferece a API SplashScreen, que ativa uma nova animação de inicialização para todos os apps. Ela inclui um movimento de entrada no app ao iniciar, uma tela de apresentação mostrando o ícone do app e uma transição para ele.

Exemplo de uma tela de apresentação
Figura 1: exemplo de uma tela de apresentação.

A nova experiência fornece elementos de design padrão a cada inicialização do app, mas também é personalizável para que os apps possam manter o branding exclusivo.

Como a tela de apresentação funciona

Quando um usuário iniciar um app enquanto o processo do app não estiver em execução (uma inicialização a frio) ou se a atividade ainda não foi criada (uma inicialização lenta), os eventos a seguir ocorrerão. A tela de apresentação nunca será exibida durante uma inicialização a quente.

  1. O sistema mostrará a tela de apresentação usando temas e todas as animações que você definiu.

  2. Quando o app estiver pronto, a tela de apresentação será dispensada e o app será exibido.

Elementos e mecânica da animação

Os elementos da animação são definidos por arquivos de recurso XML no manifesto do Android. Há versões para os modos claro e escuro.

Elas são formadas pelo segundo plano da janela, o ícone animado do app e o segundo plano do ícone:

Elementos de uma tela de apresentação
Figura 2: elementos personalizáveis de uma tela de apresentação.

Esteja ciente das seguintes considerações a respeito desses elementos:

  • O ícone do app (1) precisa ser um drawable vetorial, podendo ser estático ou animado. Embora as animações possam ter uma duração ilimitada, recomendamos que não exceda 1.000 milissegundos. Por padrão, o ícone na tela de início será usado.

  • O ícone em segundo plano (2) é opcional e é útil se mais contraste for necessário entre o ícone e o segundo plano da janela. Se você usar um ícone adaptativo, o segundo plano dele será exibido se houver contraste suficiente com o segundo plano da janela.

  • Assim como para ícones adaptativos, ⅓ do primeiro plano será mascarado (3).

  • O segundo plano da janela (4) consiste em uma única cor opaca. Se o segundo plano da janela estiver definido e tiver uma cor simples, ele será usado por padrão se o atributo não estiver definido.

A mecânica da animação da tela de apresentação consiste em animações de entrada e saída.

  • A animação de entrada consiste na visualização do sistema para a tela de apresentação. Isso é controlado pelo sistema e não pode ser personalizado.

  • A animação de saída consiste na execução da animação que oculta a tela de apresentação. Se você quiser personalizá-la, terá acesso à SplashScreenView e ao ícone dela e poderá executar qualquer animação neles, com as configurações de transformação, opacidade e cor. Nesse caso, a tela inicial precisará ser removida manualmente quando a animação for concluída.

Personalizar a tela de apresentação no app

Por padrão, a SplashScreen usa o windowBackground do tema se ele tiver uma só cor e o ícone na tela de início. A personalização da tela de apresentação é realizada adicionando atributos ao tema do app.

Você pode personalizar a tela de apresentação do app das seguintes maneiras:

  • Definindo atributos de tema para mudar a aparência

  • Mantendo a apresentação na tela por mais tempo

  • Personalizando a animação para dispensar a tela de apresentação.

Definir um tema para a tela de apresentação para mudar a aparência dela

Você pode especificar os atributos a seguir no tema da atividade para personalizar a tela de apresentação do app. Se você já tiver uma implementação de tela de apresentação legada que use atributos como o android:windowBackground, considere fornecer um arquivo de recurso alternativo para o Android 12.

  1. Use windowSplashScreenBackground para preencher o plano de fundo com uma única cor específica:

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. Use windowSplashScreenAnimatedIcon para substituir um ícone no centro da janela inicial. Se o objeto for animado e um drawable que usa AnimationDrawable e AnimatedVectorDrawable, ele também fará a animação ao exibir a janela inicial.

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. Use windowSplashScreenAnimationDuration para definir o período de exibição da tela de apresentação antes de ser dispensada. O tempo máximo é de 1.000 ms.

  4. Use windowSplashScreenIconBackground para definir um plano de fundo por trás do ícone da tela de apresentação. Isso é útil quando não há contraste suficiente entre o plano de fundo da janela e o ícone.

    <item name=”android:windowSplashScreenIconBackground”>@color/...</item>
    
  5. Se quiser, use windowSplashScreenBrandingImage para definir a imagem que será exibida na parte inferior da tela de apresentação. As diretrizes de design recomendam não usar uma imagem de marca.

    <item name=”android:windowSplashScreenBrandingImage”>@drawable/...</item>
    

Manter a apresentação na tela por mais tempo

A tela de apresentação é dispensada assim que o app exibe o primeiro frame. Se você precisar carregar uma pequena quantidade de dados, como as configurações do tema do app usando um disco local de forma assíncrona, use ViewTreeObserver.OnPreDrawListener para suspender o app e desenhar o primeiro frame.

Kotlin

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check if the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready; start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content is not ready; suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check if the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready; start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content is not ready; suspend.
                        return false;
                    }
                }
            });
}

Personalizar a animação para dispensar a tela de apresentação

Você pode personalizar ainda mais a animação da tela de apresentação usando Activity.getSplashScreen.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

        // Run your animation.
        slideUp.start();
    });
}

No início deste callback, o drawable vetorial animado na tela de apresentação foi iniciado. Dependendo da duração da inicialização do app, o drawable poderá estar no meio da animação. Use SplashScreenView.getIconAnimationStartMillis para saber quando a animação começou. Você pode calcular a duração restante da animação do ícone desta maneira:

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDurationMillis
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationDurationMillis
// Calculate the remaining duration of the animation.
val remainingDuration = (
        animationDuration - (SystemClock.uptimeMillis() - animationStart)
    ).coerceAtLeast(0L)

Java

// Get the duration of the animated vector drawable.
long animationDuration = splashScreenView.getIconAnimationDurationMillis();
// Get the start time of the animation.
long animationStart = splashScreenView.getIconAnimationStartMillis();
// Calculate the remaining duration of the animation.
long remainingDuration = Math.max(
        animationDuration - (SystemClock.uptimeMillis() - animationStart),
        0L
);