Cómo crear una animación de transición personalizada

Una transición personalizada permite crear una animación que no está disponible en ninguna de las clases de transición integradas. Por ejemplo, puedes definir una transición personalizada que cambie el color en primer plano de los campos de texto y entrada a gris para indicar que los campos están inhabilitados en la pantalla nueva. Este tipo de cambio ayuda a los usuarios a ver los campos que inhabilitaste.

Una transición personalizada, al igual que uno de los tipos de transición integradas, aplica animaciones a las vistas secundarias de las escenas inicial y final. Sin embargo, a diferencia de los tipos de transición integrados, debes proporcionar el código que captura los valores de propiedad y genera animaciones. Quizás también debas definir un subconjunto de vistas objetivo para la animación.

En esta página, se detalla cómo capturar valores de propiedad y generar animaciones para crear transiciones personalizadas.

Cómo extender la clase de transición

Para crear una transición personalizada, agrega una clase a tu proyecto que extienda la clase Transition y anula las funciones que se muestran en el siguiente fragmento:

Kotlin

    class CustomTransition : Transition() {

        override fun captureStartValues(transitionValues: TransitionValues) {}

        override fun captureEndValues(transitionValues: TransitionValues) {}

        override fun createAnimator(
            sceneRoot: ViewGroup,
            startValues: TransitionValues?,
            endValues: TransitionValues?
        ): Animator? {}

    }
    

Java

    public class CustomTransition extends Transition {

        @Override
        public void captureStartValues(TransitionValues values) {}

        @Override
        public void captureEndValues(TransitionValues values) {}

        @Override
        public Animator createAnimator(ViewGroup sceneRoot,
                                       TransitionValues startValues,
                                       TransitionValues endValues) {}
    }
    

En las siguientes secciones, se explica cómo anular estas funciones.

Cómo capturar valores de propiedades de vista

Las animaciones de transición utilizan el sistema de animación de propiedades que se describe en Animación de propiedades. Las animaciones de propiedades cambian una propiedad de vista entre un valor inicial y uno final durante un período específico, por lo que el marco de trabajo debe tener el valor inicial y el final de la propiedad para crear la animación.

Sin embargo, una animación de propiedad suele requerir solo un pequeño subconjunto de todos los valores de propiedad de la vista. Por ejemplo, una animación de color requiere valores de propiedad de color, mientras que una animación de movimiento requiere valores de propiedad de posición. Como los valores de propiedad necesarios para una animación son específicos de una transición, el marco de trabajo de transiciones no proporciona todos los valores de propiedad para una transición. En cambio, el marco de trabajo invoca funciones de devolución de llamada que permiten que una transición capture solo los valores de propiedad que necesita y los almacene en el marco de trabajo.

Cómo capturar valores iniciales

Para pasar los valores de vista iniciales al framework, implementa la función captureStartValues(transitionValues). El framework llama a esta función para cada vista en la escena inicial. El argumento de la función es un objeto TransitionValues que contiene una referencia a la vista y una instancia Map en la que puedes almacenar los valores de vista que desees. En tu implementación, recupera esos valores de propiedad y pásalos de framework de trabajo almacenándolos en el mapa.

Para asegurarte de que la clave de un valor de propiedad no entre en conflicto con otras claves TransitionValues, usa el siguiente esquema de nombres:

    package_name:transition_name:property_name
    

El siguiente fragmento muestra una implementación de la función captureStartValues():

Kotlin

    class CustomTransition : Transition() {

        // Define a key for storing a property value in
        // TransitionValues.values with the syntax
        // package_name:transition_class:property_name to avoid collisions
        private val PROPNAME_BACKGROUND = "com.example.android.customtransition:CustomTransition:background"

        override fun captureStartValues(transitionValues: TransitionValues) {
            // Call the convenience method captureValues
            captureValues(transitionValues)
        }

        // For the view in transitionValues.view, get the values you
        // want and put them in transitionValues.values
        private fun captureValues(transitionValues: TransitionValues) {
            // Get a reference to the view
            val view = transitionValues.view
            // Store its background property in the values map
            transitionValues.values[PROPNAME_BACKGROUND] = view.background
        }

        ...

    }
    

Java

    public class CustomTransition extends Transition {

        // Define a key for storing a property value in
        // TransitionValues.values with the syntax
        // package_name:transition_class:property_name to avoid collisions
        private static final String PROPNAME_BACKGROUND =
                "com.example.android.customtransition:CustomTransition:background";

        @Override
        public void captureStartValues(TransitionValues transitionValues) {
            // Call the convenience method captureValues
            captureValues(transitionValues);
        }

        // For the view in transitionValues.view, get the values you
        // want and put them in transitionValues.values
        private void captureValues(TransitionValues transitionValues) {
            // Get a reference to the view
            View view = transitionValues.view;
            // Store its background property in the values map
            transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());
        }
        ...
    }
    

Cómo capturar valores finales

El framework llama a la función captureEndValues(TransitionValues) una vez para cada vista de destino en la escena final. En todos los demás aspectos, captureEndValues() funciona igual que captureStartValues().

En el siguiente fragmento de código, se muestra una implementación de la función captureEndValues():

Kotlin

    override fun captureEndValues(transitionValues: TransitionValues) {
        captureValues(transitionValues)
    }
    

Java

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }
    

En este ejemplo, las funciones captureStartValues() y captureEndValues() invocan a captureValues() para recuperar y almacenar valores. La propiedad de vista que recupera captureValues() es la misma, pero tiene valores diferentes en las escenas inicial y final. El framework mantiene mapas separados para los estados inicial y final de una vista.

Cómo crear un animador personalizado

Para animar los cambios en una vista entre su estado en la escena inicial y su estado en la escena final, proporciona un animador anulando la función createAnimator(). Cuando el framework llama a esta función, pasa en la vista raíz de la escena y los objetos TransitionValues que contienen los valores inicial y final que capturaste.

La cantidad de veces que el framework llama a la función createAnimator() depende de los cambios que se producen entre las escenas inicial y final. Por ejemplo, considera una animación de fundido de salida/entrada implementada como una transición personalizada. Si la escena inicial tiene cinco objetivos y se quitan dos de la escena final, y la escena final tiene los tres objetivos de la escena inicial más un nuevo objetivo, el framework llama a createAnimator() seis veces: tres de las llamadas animan el desvanecimiento y la aparición gradual de los objetivos que permanecen en ambos objetos de la escena; otras dos llamadas animan el desvanecimiento de los objetivos que se quitaron de la escena final, y una llamada anima la aparición gradual del nuevo objetivo en la escena final.

En el caso de las vistas de destino que existen en las escenas inicial y final, el framework proporciona un objeto TransitionValues para los argumentos startValues y endValues. En el caso de las vistas de destino que solo existen en la escena inicial o final, el framework proporciona un objeto TransitionValues para el argumento correspondiente y null para el otro.

Para implementar la función createAnimator(ViewGroup, TransitionValues, TransitionValues) cuando creas una transición personalizada, usa los valores de propiedad de vista que capturaste para crear un objeto Animator y devolverlo al framework. Para ver un ejemplo de implementación, consulta la clase ChangeColor en el ejemplo de CustomTransition. Si quieres obtener más información sobre los animadores de propiedades, consulta Animación de propiedades.

Cómo aplicar una transición personalizada

Las transiciones personalizadas funcionan igual que las transiciones integradas. Puedes aplicar una transición personalizada con un administrador de transiciones, como se describe en Aplica una transición.