최근 회사에서 MotionLayout을 아주 간~단하게 적용하고 있는데, 나는 비교적 최근에 MotionLayout을 공부하고 사용했었다.
하지만 동료들은 그렇지 않았고, 일정이 넉넉하지 않은 팀 사정 상 MotionLayout을 사용하는 것은 오직 나 혼자 뿐이었다.
사용하는 데에 다소 러닝커브가 있다면, 쉽게 사용할 수 있는 방법을 찾아보자
애니메이션을 구현하는 방법은 여러가지이며, 그 중 xml파일을 정의해서 사용하는 방식이 있다.
이 방법에서 정의된 애니메이션은 사용하고자 모든 곳에서 재사용된다.
따라서, MotionLayout에서도 애니메이션을 이루는 xml인 MotionScene을 단 1번만 정의하고 각각의 MotionLayout에서 정의된 Scene을 참조한다면 모든 MotionLayout에서 동일한 애니메이션을 얻을 수 있다고 생각했다.
이 방법이 유효할 경우, 한 번 정의된 Scene에 대해서는 그 뒤엔 매우 쉽게 적용이 가능할 것이다.
2개의 액티비티에서 동일한 구조와 id를 가진 MotionLayout과 그 자식들을 작성한다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="5"
app:layoutDescription="@xml/activity_main_scene"
tools:context=".MainActivity">
<TextView
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</androidx.constraintlayout.motion.widget.MotionLayout>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="5"
app:layoutDescription="@xml/activity_main_scene">
<TextView
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TEST!!!!!" />
</androidx.constraintlayout.motion.widget.MotionLayout>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
각각의 MotionLayout에서 공동으로 참조할 Scene을 작성한다.
간단하게, 화면을 클릭했을 때 TextView가 화면 상단에서 하단으로 움직이는 애니메이션이다.
activity_main_scene.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
<OnClick motion:clickAction="toggle"/>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</ConstraintSet>
</MotionScene>
하나의 Scene을 두 개의 MotionLayout이 참조하고 있는 상황에서 빌드하여 애니메이션이 정상 적용되는지 확인한다.

두 곳 모두에서 동일한 애니메이션이 적용되었다!
하지만, 이 방법에는 크게 두 가지 제한이 있다.
Scene을 공유하는 MotionLayout의 자식들은 모두 같은 id를 가져야 한다.
위 예시 상황에서 MotionLayout의 자식으로 있는 TextView의 경우, 모두 같은 id를 가진다 (@id/test)
이 이유는, 공유하는 Scene의 ConstraintSet에 포함된 Constraint의 id와 MotionLayout의 자식의 id는 같아야 하기 때문이다.
Scene을 공유하는 MotionLayout은 같은 구조로 자식을 가져야 한다.
1번과 같은 이유에서, 동일한 애니메이션을 적용하기 위해서는 Scene의 ConstraintSet에 포함된 Constraint의 id와 MotionLayout의 자식의 id는 같아야 한다.
ex) Scene의 Constraint id가 header, body라면 MotionLayout은 header와 body만을 가진다.
→ 하지만, header와 body는 각각 어떤 자식을 가지는지는 상관이 없다.
한 레이아웃 파일에서 단 1개의 MotionLayout만이 Scene을 공유할 수 있다.
2번과 마찬가지로 1번에서 이어지는 내용으로, 한 레이아웃에서 동일한 id를 사용할 수 없기 때문에 Scene과 같은 id를 가진 MotionLayout은 한 레이아웃 파일당 단 1개만 존재할 수 있다.
이미 작성된 Scene이 있고, 위 3가지 제한을 어기지 않았다면 아주 간단하게 애니메이션을 적용할 수 있다.
app:layoutDescription 을 이미 작성된 Scene을 참조하도록 한다.이 방법이 틀린 방법일 수 있고, 위에 적어놓은 3가지의 제한이 있지만 그래도 꽤 괜찮은 방법이라고 생각된다.
추후에 저 3가지 제한을 초월하는 방법이 떠오르면 추가로 작성해보도록…….해보겠다.