Prepárate para 12L, una actualización de funciones para pantallas grandes que se lanzará a principios del próximo año. Pruébala hoy.

Pantallas de presentación

En Android 12, se agrega la API de SplashScreen, que habilita una animación nueva de inicio para todas las apps. Esta animación incluye un movimiento de entrada a la app cuando se inicia, una pantalla de presentación que muestra el ícono de la app y una transición a la app en sí misma.

Ejemplo de una pantalla de presentación
Figura 1: Ejemplo de una pantalla de presentación

La experiencia nueva brinda elementos de diseño estándares para cada inicio de la app, pero también se puede personalizar de modo que esta pueda conservar su única imagen de marca.

Cómo funciona la pantalla de presentación

Cuando un usuario inicia una app mientras su proceso no se está ejecutando (un inicio en frío) o no se creó la actividad (un inicio semicaliente), se producen los siguientes eventos. (La pantalla de presentación nunca se muestra durante un inicio en caliente).

  1. El sistema muestra la pantalla de presentación con temas y cualquier animación que definiste.

  2. Cuando la app está lista, se descarta la pantalla de presentación y se muestra la app.

Elementos y mecánicas de la animación

Los elementos de la animación se definen mediante archivos de recursos XML en el manifiesto de Android. Existen versiones claras y oscuras para cada uno.

Consisten en el fondo de la ventana, el ícono animado de la app y el fondo del ícono:

Elementos de una pantalla de presentación
Figura 2: Elementos personalizables de una pantalla de presentación

Ten en cuenta las siguientes consideraciones con respecto a estos elementos:

  • El ícono de la app (1) debe ser una interfaz dibujable en vector y puede ser estático o animado. Si bien las animaciones pueden tener una duración ilimitada, te recomendamos que no exceda los 1,000 milisegundos. De forma predeterminada, se usa el ícono de selector.

  • El fondo del ícono (2) es opcional y es útil si se necesita más contraste entre el ícono y el fondo de la ventana. Si usas un ícono adaptable, su fondo se muestra si hay suficiente contraste con el fondo de la ventana.

  • Al igual que con los íconos adaptables, un tercio del primer plano está enmascarado (3).

  • El fondo de la ventana (4) consta de un único color opaco. Cuando el fondo de la ventana está configurado y tiene un solo color, se usa de forma predeterminada si no se configura el atributo.

Las mecánicas de animación de la pantalla de presentación constan de animaciones de entrada y salida.

  • La animación de entrada consiste en la vista del sistema hacia la pantalla de presentación. El sistema controla esta animación y no se puede personalizar.

  • La animación de salida consiste en la ejecución de una animación que oculta la pantalla de presentación. Si deseas personalizarla, tendrás acceso a la clase SplashScreenView y a su ícono, y podrás ejecutar cualquier animación en estos y configurar su transformación, opacidad y color. En ese caso, la pantalla de presentación deberá quitarse de forma manual cuando finalice la animación.

Cómo personalizar la pantalla de presentación de la app

De forma predeterminada, SplashScreen usa el ícono de selector y el windowBackground del tema si este tiene un solo color. Para personalizar la pantalla de presentación, agrega atributos al tema de la app.

La pantalla de presentación de la app se puede personalizar de cualquiera de las siguientes maneras:

  • Configura los atributos del tema para cambiar su apariencia

  • Muéstrala en pantalla durante períodos más largos

  • Personaliza la animación para descartar la pantalla de presentación

Cómo establecer un tema para la pantalla de presentación a fin de cambiar su apariencia

Puedes especificar los siguientes atributos en el tema de la actividad a fin de personalizar la pantalla de presentación de la app. Si ya tienes una implementación heredada de la pantalla de presentación que usa atributos, como android:windowBackground, considera proporcionar un archivo de recursos alternativo para Android 12.

  1. Usa windowSplashScreenBackground para llenar el fondo con un solo color específico:

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. Usa windowSplashScreenAnimatedIcon para reemplazar un ícono en el centro de la ventana de inicio. Si se puede animar y dibujar el objeto mediante AnimationDrawable y AnimatedVectorDrawable, también se reproducirá la animación mientras se muestre la ventana de inicio.

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. Usa windowSplashScreenAnimationDuration para establecer el tiempo que durará la pantalla de presentación antes de descartarla. El tiempo máximo es de 1,000 ms.

  4. Usa windowSplashScreenIconBackground para configurar un fondo detrás del ícono de la pantalla de presentación. Es útil si no hay suficiente contraste entre el fondo de la ventana y el ícono.

    <item name=”android:windowSplashScreenIconBackground”>@color/...</item>
    
  5. De forma opcional, puedes usar windowSplashScreenBrandingImage para establecer que una imagen se muestre en la parte inferior de la pantalla de presentación. Según los lineamientos de diseño, te recomendamos que no uses una imagen de marca.

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

Cómo mostrar la pantalla de presentación durante períodos más largos

La pantalla de presentación se descarta en cuanto la app dibuja su primer fotograma. Si necesitas cargar una pequeña cantidad de datos, como la configuración del tema en la app desde un disco local de forma asíncrona, puedes usar ViewTreeObserver.OnPreDrawListener para impedir que la app dibuje su primer fotograma.

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;
                    }
                }
            });
}

Cómo personalizar la animación para descartar la pantalla de presentación

Puedes personalizar aún más la animación de la pantalla de presentación mediante 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();
    });
}

Antes del inicio de esta devolución de llamada, comienza la interfaz animada dibujable en vector en la pantalla de presentación. Según la duración del inicio de la app, es posible que la interfaz dibujable esté en medio de su animación. Usa SplashScreenView.getIconAnimationStartMillis para saber cuándo comenzó la animación. Puedes calcular la duración restante de la animación del ícono de la siguiente manera:

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
);