Tras la llegada de Material Design han sido muchas las librerías que se han añadido para aplicar animaciones cada vez más refinadas tanto a los elementos de la interfaz como en las transiciones entre pantallas. Ejemplos como el CoordinatorLayout, un tipo de layout que proporciona un nivel adicional de control sobre eventos táctiles entre vistas secundarias, o el ConstraintLayout capaz de crear layouts complejos con una jerarquía de vistas plana, es decir, sin grupos de vistas anidadas.
Pues bien, de estas nuevas incorporaciones nace en 2018 MotionLayout, una clase que extiende de la ya comentada ConstraintLayout y que nos permite administrar las animaciones de movimiento y widgets en nuestra app.
Si quieres saber más sobre cómo animar layouts con MotionLayout, sus características y su potencial para ponerlo en práctica en cualquier aplicación, en este post te explicaremos las claves para dar tus primeros pasos con este potente tipo de vista.
¿Qué es Motion Layout?
Como decíamos, MotionLayout es una subclase de ConstraintLayout que ayuda a administrar animaciones de movimiento y widgets en nuestra app. Grosso modo, MotionLayout está diseñado para mover, cambiar el tamaño y animar los elementos de la IU con los que interactúan los usuarios.
Como parte de la biblioteca de ConstraintLayout, MotionLayout está disponible como una biblioteca de compatibilidad y es compatible con versiones anteriores a la API nivel 14.
Al ser MotionLayout una subclase de ConstraintLayout es posible transformar cualquier ConstraintLayout existente en un MotionLayout simplemente reemplazando el nombre de la clase en tu archivo.
Para llevar a cabo estas animaciones MotionLayout admite transiciones entre varios estados (ConstraintSet) que previamente han sido definidos en MotionScenes.
¿Cómo se usa?
1. Incluir la dependencia al proyecto
dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' }
2. Añadimos el componente MotionLayout incluyendo la referencia al MotionScene.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/scene_01"> </androidx.constraintlayout.motion.widget.MotionLayout>
3. Añadimos el MotionScene: un archivo XML incluido en la carpeta de recursos que contiene todas las descripciones de movimientos para el diseño correspondiente. En este archivo deberemos de prestar atención a lo siguiente:
<Transition>
tiene la definición básica del movimiento. En esta parte incluiremos las referencias a los extremos del movimiento:motion:constraintSetStart
ymotion:constraintSetEnd
. También incluiremos el tiempo de gesto al que queremos que reaccione nuestra animación, en el ejemplo<OnSwipe>
, junto con la referencia a la vista (motion:touchAnchorId
):
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition app:constraintSetEnd="@id/end" app:constraintSetStart="@id/start" app:duration="1000"> <OnSwipe app:dragDirection="dragUp" app:touchAnchorId="@id/button" app:touchAnchorSide="top" /> </Transition> </MotionScene>
4. En el mismo archivo de MotionScene añadimos los <ConstraintSet>
donde se definirán las diversas restricciones que describen tu movimiento.
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition app:constraintSetEnd="@id/end" app:constraintSetStart="@id/start" app:duration="1000"> <OnSwipe app:dragDirection="dragUp" app:touchAnchorId="@id/button" app:touchAnchorSide="top" /> </Transition> <ConstraintSet android:id="@id/start"> <Constraint android:id="@id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent"/> </ConstraintSet> <ConstraintSet android:id="@id/end"> <Constraint android:id="@id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent"/> </ConstraintSet> </MotionScene>
Los elementos ConstraintSet pueden contener atributos adicionales que se interpolan durante la transición. Además de la posición y los límites, MotionLayout interpola los siguientes atributos:
- alpha
- visibility
- elevation
- rotation, rotationX, rotationY
- translationX, translationY, translationZ
- scaleX, scaleY
CustomAttribute
En los ConstraintsSets de inicio y fin es posible añadir un elemento <CustomAttribute>
en donde se puede especificar una transición para los atributos que no están simplemente relacionados con la posición o los atributos View. El <CustomAttribute>
contiene dos atributos propios:
motion:attributeName
: es obligatorio y debe coincidir con un objeto que tenga métodos get y set.- El otro atributo que debes proporcionar se basa en el tipo de valor. Elige entre los siguientes tipos admitidos (
customColorValue, customIntegerValue, customFloatvalue
,customStringValue
.customDimension
,customBoolean
)
KeyFrame
Otro atributo interesante de los MotionScences es la posibilidad de usar KeyFrames. Los <KeyFrame>
, también llamados “fotogramas clave”, permiten especificar un cambio en un momento determinado durante la transición.
Gracias a los KeyFrames es posible tener un estado intermedio: un estado por el que pasar, pero no un estado al que pertenecer (como los ConstraintSets de start/end).
De forma parecida al <CustomAttribute>
, los <KeyFrames>
también permiten varios tipos:
- De posición:
KeyPosition
- De atributo:
KeyAttribute
- De KeyCycle:
KeyCycle
- De TimeCycle:
KeyTimeCycle
Todos los tipos tienen una serie de atributos comunes:
motion:framePosition
: define cuándo se aplica el keyFrame durante la transición (de 0 a 100)motion:target
: define el objeto que se ve afectado por este keyFramemotion:transitionEasing
: se define el tipo de curva de aceleración (el valor predeterminado es lineal)- motion:curveFit: spline (predeterminado) o lineal: la curva de interpolación que se ajusta a los fotogramas clave. El valor predeterminado es una curva spline monótona, lo que hace que las transiciones sean más suaves, pero puede decidir tener segmentos lineales en su lugar.
¿Por qué keyFrames si los ConstraintSets permiten mover cualquier widget de una forma muy flexible?
- Los keyFrames definen una modificación transitoria, mientras que los ConstraintSets definen un estado de «reposo».
- Los keyFrames son más ligeros que los ConstraintSet porque definen solo el atributo que queremos modificar.
- Los keyFrames de tipo posición permiten manipular la ruta de movimiento de un widget; los ConstraintSets definen la posición de un widget, pero en relación con otros widgets.
¿Dónde podrían usarse?
MotionLayout puede funcionar por sí mismo o combinándolos con otros componentes como:
- CoordinatorLayout
- DrawerLayout
- ViewPager
Aunque realmente MotionLayout podría imitar el comportamiento de alguno de estos componentes. Combinarlos sirve, sobre todo, para dar calidad a componentes que ya están planteados y estructurados.
Con un gran potencial
Como hemos visto en el post, MotionLayout es uno de los tipos de vistas más potentes en relación al uso y gestión de las animaciones.
Tanto en la versión básica (utilizando constraintsSets start y end) como añadiendo keyframes que enriquezcan la animación, las vistas con este tipo de Layout quedarán especialmente vistosas y en base a los principios de Material Design.
« Evolución tecnológica y uso de internet en España en 2020 NFC y Beacons: una capa adicional de interacción con los móviles »