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

Una transición personalizada te 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 de 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 integrados, 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. Es posible que también desees definir un subconjunto de vistas objetivo para tu animación.

En esta página, se explica 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 usan el sistema de animación de propiedades que se describe en Descripción general de la animación de propiedades. Las animaciones de propiedades cambian una propiedad de vista de un valor inicial a un valor final durante un período especificado, por lo que el framework debe tener los valores inicial y final de la propiedad para construir la animación.

Sin embargo, en general, una animación de propiedad solo necesita un pequeño subconjunto de todos los valores de propiedad de la vista. Por ejemplo, una animación de color necesita valores de propiedad de color, mientras que una animación de movimiento necesita valores de propiedad de posición. Dado que los valores de propiedad necesarios para una animación son específicos de una transición, el framework de transiciones no proporciona todos los valores de propiedad de una transición. En cambio, el framework 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 framework.

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 de Map en la que puedes almacenar los valores de vista que desees. En tu implementación, recupera estos valores de propiedad y pásalos de vuelta al framework almacenándolos en el mapa.

Para asegurarte de que la clave de un valor de propiedad no entre en conflicto con otras claves de 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 captureValues() recupera 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, esta pasa a la vista raíz de la escena y a los objetos TransitionValues que contienen los valores de inicio y finalización que capturaste.

La cantidad de veces que el framework llama a la función createAnimator() depende de los cambios que ocurren entre las escenas inicial y final.

Por ejemplo, considera una animación de fundido de salida o entrada implementada como una transición personalizada. Si la escena inicial tiene cinco objetivos, de los cuales dos se quitan de la escena final, y la escena final tiene los tres objetivos de la escena inicial más un objetivo nuevo, el framework llama a createAnimator() seis veces. Tres de las llamadas animan el fundido de salida y la entrada de los objetivos que permanecen en ambos objetos de escena. Dos llamadas más animan el fundido de salida de los objetivos que se quitaron de la escena final. Una llamada anima el fundido de entrada 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 una muestra 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.