1. 시작하기 전에
이 Codelab은 Kotlin 기반 Android 고급 과정의 일부입니다. Codelab을 순서대로 살펴보면 이 과정을 최대한 활용할 수 있지만 필수는 아닙니다. 모든 과정 Codelab은 Kotlin 기반 Android 고급 Codelab 방문 페이지에 나열되어 있습니다.
MotionLayout
는 Android 앱에 리치 모션을 추가할 수 있는 라이브러리입니다. ConstraintLayout,
를 기반으로 하며 ConstraintLayout
를 사용하여 빌드할 수 있는 모든 항목에 애니메이션을 적용할 수 있습니다.
MotionLayout
를 사용하여 동시에 여러 뷰의 위치, 크기, 공개 상태, 알파, 색상, 고도, 회전, 기타 속성에 애니메이션을 적용할 수 있습니다. 선언적 XML을 사용하면 코드로는 실행하기 어려운 여러 뷰가 포함된 조정된 애니메이션을 만들 수 있습니다.
애니메이션은 앱 환경을 향상시키는 좋은 방법입니다. 애니메이션을 사용하여 다음을 수행할 수 있습니다.
- 변경사항 표시: 상태 간에 애니메이션을 사용하면 사용자가 UI의 변경사항을 자연스럽게 추적할 수 있습니다.
- 관심 유도: 애니메이션을 사용하여 중요한 UI 요소로 주의를 끕니다.
- 아름다운 디자인 - 효과적인 모션 디자인으로 앱을 세련되게 만들 수 있습니다.
기본 요건
이 Codelab은 Android 개발 경험이 있는 개발자를 대상으로 합니다. 이 Codelab을 완료하기 전에 다음을 수행해야 합니다.
- 활동, 기본 레이아웃으로 앱을 만들고 Android 스튜디오를 사용하여 기기 또는 에뮬레이터에서 앱을 실행하는 방법을 알아야 합니다.
ConstraintLayout
를 숙지합니다. 제약 조건 레이아웃 Codelab을 읽고ConstraintLayout
에 관해 자세히 알아보세요.
실행할 작업
ConstraintSets
및MotionLayout
를 사용하여 애니메이션 정의- 드래그 이벤트를 기반으로 애니메이션 처리
KeyPosition
로 애니메이션 변경KeyAttribute
로 속성 변경- 코드로 애니메이션 실행
MotionLayout
로 접을 수 있는 헤더에 애니메이션 적용
필요한 항목
- Android 스튜디오 4.0 (
MotionLayout
편집기는 이 버전의 Android 스튜디오에서만 작동합니다.)
2. 시작하기
샘플 앱을 다운로드하려면 다음 중 하나를 실행하세요.
또는 다음 명령어를 사용하여 명령줄에서 GitHub 저장소를 클론합니다.
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. MotionLayout으로 애니메이션 만들기
먼저 사용자 클릭에 응답하여 화면의 상단 시작 부분에서 하단으로 뷰를 이동하는 애니메이션을 빌드합니다.
시작 코드에서 애니메이션을 만들려면 다음과 같은 주요 요소가 필요합니다.
ConstraintLayout
의 서브클래스인MotionLayout,
MotionLayout
태그 내에서 애니메이션을 적용할 모든 뷰를 지정합니다.MotionLayout.
의 애니메이션을 설명하는 XML 파일인MotionScene,
- 애니메이션 지속 시간, 트리거, 뷰 이동 방법을 지정하는
MotionScene
의 일부인Transition,
입니다. - 전환의 start 및 end 제약 조건을 모두 지정하는
ConstraintSet
입니다.
MotionLayout
부터 순서대로 하나씩 살펴보겠습니다.
1단계: 기존 코드 살펴보기
MotionLayout
는 ConstraintLayout
의 서브클래스이므로 애니메이션을 추가하는 동안 동일한 모든 기능을 지원합니다. MotionLayout
를 사용하려면 ConstraintLayout.
를 사용할 MotionLayout
뷰를 추가합니다.
res/layout
에서activity_step1.xml.
를 엽니다.ImageView
가 하나 있고 그 안에 색조가 적용된ConstraintLayout
가 있습니다.
activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
이 ConstraintLayout
에는 제약 조건이 없으므로 지금 앱을 실행하면 별표 표시가 제한되지 않은 상태로 표시됩니다. 즉, 별표 표시가 알 수 없는 위치에 배치됩니다. Android 스튜디오에 제약 조건 부족에 관한 경고가 표시됩니다.
2단계: 모션 레이아웃으로 변환
MotionLayout,
를 사용하여 애니메이션을 적용하려면 ConstraintLayout
를 MotionLayout
로 변환해야 합니다.
레이아웃이 모션 장면을 사용하려면 모션 장면을 가리켜야 합니다.
- 이렇게 하려면 디자인 화면을 엽니다. Android 스튜디오 4.0에서는 레이아웃 XML 파일을 볼 때 오른쪽 상단의 분할 아이콘 또는 디자인 아이콘을 사용하여 디자인 화면을 엽니다.
- 디자인 화면이 열리면 미리보기를 마우스 오른쪽 버튼으로 클릭하고 Convert to MotionLayout을 선택합니다.
이렇게 하면 ConstraintLayout
태그가 MotionLayout
태그로 대체되고 @xml/activity_step1_scene.
를 가리키는 MotionLayout
태그에 motion:layoutDescription
가 추가됩니다.
activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
모션 장면은 MotionLayout
의 애니메이션을 설명하는 단일 XML 파일입니다.
MotionLayout
로 변환하면 즉시 디자인 화면에 모션 편집기가 표시됩니다.
모션 편집기에는 다음과 같은 세 가지 새로운 UI 요소가 있습니다.
- 개요 – 애니메이션의 여러 부분을 선택할 수 있는 모달 선택입니다. 이 이미지에서는
start
ConstraintSet
가 선택되어 있습니다.start
및end
사이에 있는 화살표를 클릭하여 전환을 선택할 수도 있습니다. - 섹션 – 개요 아래에는 현재 선택한 개요 항목에 따라 변경되는 섹션 창이 표시됩니다. 이 이미지에서는
start
ConstraintSet
정보가 선택 창에 표시됩니다. - 속성 – 속성 패널이 표시되며 개요 또는 선택 창에서 현재 선택한 항목의 속성을 수정할 수 있습니다. 이 이미지에서는
start
ConstraintSet
의 속성을 보여줍니다.
3단계: 시작 및 종료 제약조건 정의
모든 애니메이션은 시작과 끝의 측면에서 정의할 수 있습니다. 시작 부분은 애니메이션이 실행되기 전의 화면을 설명하고 끝은 애니메이션이 완료된 후의 화면을 설명합니다. MotionLayout
는 시간 경과에 따라 시작 상태와 종료 상태 간에 애니메이션하는 방법을 파악합니다.
MotionScene
는 ConstraintSet
태그를 사용하여 시작 상태와 종료 상태를 정의합니다. ConstraintSet
은 말 그대로 뷰에 적용할 수 있는 제약 조건 세트입니다. 여기에는 너비, 높이, ConstraintLayout
제약 조건이 포함됩니다. 또한 alpha
와 같은 속성도 포함됩니다. 여기에는 뷰 자체가 포함되어 있지 않으며 이러한 뷰의 제약 조건만 포함되어 있습니다.
ConstraintSet
에 지정된 모든 제약 조건은 레이아웃 파일에 지정된 제약 조건을 재정의합니다. 레이아웃과 MotionScene
모두에서 제약 조건을 정의하면 MotionScene
의 제약 조건만 적용됩니다.
이 단계에서는 화면의 맨 위 시작 부분에서 시작하여 화면 하단 끝에서 완료되도록 별표 보기를 제한합니다.
모션 편집기를 사용하거나 activity_step1_scene.xml
의 텍스트를 직접 수정하여 이 단계를 완료할 수 있습니다.
- 개요 패널에서
start
ConstraintSet 선택
- 선택 패널에서
red_star
를 선택합니다. 현재layout
의 소스가 표시됩니다. 즉, 이ConstraintSet
에 제한되지 않습니다. 오른쪽 상단에 있는 연필 아이콘을 사용하여 제약조건 만들기
- 개요 패널에서
start
ConstraintSet
가 선택되면red_star
에start
소스가 표시되는지 확인합니다. start
ConstraintSet
에서red_star
를 선택한 Attributes 패널의 상단에 제약 조건을 추가하고 파란색 + 버튼을 클릭하여 시작합니다.
xml/activity_step1_scene.xml
을 열어 모션 편집기에서 이 제약 조건과 관련해 생성한 코드를 확인합니다.
activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
ConstraintSet
에는 @id/start
의 id
가 있으며 MotionLayout
의 모든 뷰에 적용할 모든 제약 조건을 지정합니다. 이 MotionLayout
에는 뷰가 하나만 있으므로 Constraint
가 하나만 필요합니다.
ConstraintSet
내부의 Constraint
는 제약하는 뷰의 ID(activity_step1.xml
에 정의된 @id/red_star
)를 지정합니다. Constraint
태그는 제약 조건과 레이아웃 정보만 지정한다는 점에 유의해야 합니다. Constraint
태그는 태그가 ImageView
에 적용되고 있다는 것을 모릅니다.
이 제약 조건은 높이, 너비, 그리고 red_star
뷰를 상위 요소의 상단 시작 부분으로 제한하는 데 필요한 두 가지 제약 조건을 지정합니다.
- 개요 패널에서
end
ConstraintSet를 선택합니다.
- 이전과 동일한 단계를 따라
end
ConstraintSet
에서red_star
의Constraint
를 추가합니다. - 모션 편집기를 사용하여 이 단계를 완료하려면 파란색 + 버튼을 클릭하여
bottom
및end
에 제약 조건을 추가합니다.
- XML의 코드는 다음과 같습니다.
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
@id/start
과 마찬가지로 이 ConstraintSet
의 @id/red_star
에 단일 Constraint
이(가) 있습니다. 이번에는 광고가 화면 하단으로 제한됩니다.
이름을 @id/start
및 @id/end
로 지정할 필요는 없지만 지정하는 것이 편리합니다.
4단계: 전환 정의
또한 모든 MotionScene
에는 전환이 1개 이상 포함되어야 합니다. 전환은 시작부터 끝까지, 한 애니메이션의 모든 부분을 정의합니다.
전환에서는 전환의 시작 및 종료 ConstraintSet
를 지정해야 합니다. 또한 전환은 애니메이션을 실행하는 시간 또는 보기를 드래그하여 애니메이션을 적용하는 방법과 같은 다른 방식으로 애니메이션을 수정하는 방법을 지정할 수도 있습니다.
- 모션 편집기에서는 MotionScene 파일을 만들 때 기본적으로 전환이 생성됩니다.
activity_step1_scene.xml
를 열어 생성된 전환을 확인합니다.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
이는 MotionLayout
가 애니메이션을 빌드하는 데 필요한 모든 것입니다. 각 속성을 살펴보면 다음과 같습니다.
- 애니메이션이 시작되면
constraintSetStart
가 뷰에 적용됩니다. constraintSetEnd
는 애니메이션이 끝날 때 뷰에 적용됩니다.duration
는 애니메이션의 소요 시간을 밀리초 단위로 지정합니다.
그러면 MotionLayout
가 시작 및 종료 제약조건 사이의 경로를 파악하고 지정된 기간 동안 애니메이션을 적용합니다.
5단계: 모션 편집기에서 애니메이션 미리보기
애니메이션: 모션 편집기에서 전환 미리보기를 재생하는 동영상
- 모션 편집기를 열고 개요 패널에서
start
와end
사이에 있는 화살표를 클릭하여 전환을 선택합니다.
- 전환이 선택되면 선택 패널에 재생 컨트롤과 재생바가 표시됩니다. 재생을 클릭하거나 현재 위치를 드래그하여 애니메이션을 미리 봅니다.
6단계: on click 핸들러 추가
애니메이션을 시작할 방법이 필요합니다. 이렇게 하는 한 가지 방법은 MotionLayout
가 @id/red_star
의 클릭 이벤트에 응답하도록 하는 것입니다.
- 모션 편집기를 열고 개요 패널에서 시작과 끝 사이의 화살표를 클릭하여 전환을 선택합니다.
- 개요 패널의 툴바에서 클릭 또는 스와이프 핸들러 만들기를 클릭합니다 . 이렇게 하면 전환을 시작하는 핸들러가 추가됩니다.
- 팝업에서 Click Handler를 선택합니다.
- View To Click을
red_star
로 변경합니다.
- Add(추가)를 클릭하면 모션 편집기에서 클릭 핸들러가 작은 점으로 표시됩니다.
- 개요 패널에서 전환을 선택한 상태에서 속성 패널에 방금 추가한 OnClick 핸들러에
toggle
의clickAction
속성을 추가합니다.
activity_step1_scene.xml
를 열어 모션 편집기에서 생성한 코드를 확인합니다.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
Transition
는 <OnClick>
태그를 사용하여 클릭 이벤트에 대한 응답으로 애니메이션을 실행하도록 MotionLayout
에 지시합니다. 각 속성을 살펴보면 다음과 같습니다.
targetId
는 클릭을 감시하는 뷰입니다.toggle
의clickAction
는 클릭 시 시작 상태와 종료 상태 간에 전환합니다.clickAction
의 다른 옵션은 문서에서 확인할 수 있습니다.
- 코드를 실행하고 Step 1을 클릭한 다음 빨간색 별을 클릭하여 애니메이션을 확인합니다.
5단계: 애니메이션 실행
앱을 실행합니다. 별표를 클릭하면 애니메이션이 실행되는 것을 확인할 수 있습니다.
완성된 모션 장면 파일은 시작 및 끝 ConstraintSet
를 가리키는 하나의 Transition
를 정의합니다.
애니메이션 시작 (@id/start
)에서 별표 아이콘은 화면 상단 시작 부분으로 제한됩니다. 애니메이션 (@id/end
)이 끝나면 별표 아이콘은 화면 하단 끝으로 제한됩니다.
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. 드래그 이벤트 기반 애니메이션
이 단계에서는 사용자 드래그 이벤트 (사용자가 화면을 스와이프할 때)에 응답하는 애니메이션을 빌드하여 애니메이션을 실행합니다. MotionLayout
는 뷰를 이동하는 터치 이벤트 추적뿐만 아니라 움직임을 유동적으로 만들기 위한 물리학 기반의 플링 동작을 지원합니다.
1단계: 초기 코드 검사하기
- 시작하려면 기존
MotionLayout
가 있는 레이아웃 파일activity_step2.xml
를 엽니다. 코드를 살펴보세요.
activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
이 레이아웃은 애니메이션의 모든 뷰를 정의합니다. 별표 3개 아이콘은 모션 장면에서 애니메이션으로 표시되기 때문에 레이아웃에서 제약을 받지 않습니다.
크레딧 TextView
에는 적용된 제약 조건이 적용됩니다. 전체 애니메이션에서 동일한 위치에 유지되고 어떠한 속성도 수정하지 않기 때문입니다.
2단계: 장면에 애니메이션 적용
마지막 애니메이션과 마찬가지로 애니메이션은 시작 및 종료 ConstraintSet,
와 Transition
로 정의됩니다.
시작 ConstraintSet 정의
- 모션 장면
xml/step2.xml
을 열어 애니메이션을 정의합니다. - 시작 제약조건
start
의 제약조건을 추가합니다. 시작할 때 별 세 개는 화면 하단 중앙에 위치합니다. 오른쪽 및 왼쪽 별표는alpha
값이0.0
입니다. 이는 완전히 투명하고 숨겨져 있음을 의미합니다.
step2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
이 ConstraintSet
에서는 별표마다 하나의 Constraint
를 지정합니다. 각 제약 조건은 애니메이션 시작 시 MotionLayout
에 의해 적용됩니다.
각 별표 보기는 시작, 끝 및 하단 제약 조건을 사용하여 화면 하단 중앙에 배치됩니다. 두 별 @id/left_star
및 @id/right_star
에는 모두 표시되지 않으며 애니메이션 시작 시 적용되는 추가 알파 값이 있습니다.
start
및 end
제약 조건 집합은 애니메이션의 시작과 끝을 정의합니다. 시작 시 제약 조건(예: motion:layout_constraintStart_toStartOf
)은 뷰의 시작을 다른 뷰의 시작으로 제한합니다. 처음에는 혼란스러울 수 있습니다. start
라는 이름이 모두 제약 조건 컨텍스트에서 사용되기 그리고 둘 다 사용되기 때문입니다. 구분을 돕기 위해 layout_constraintStart
의 start
는 '시작'을 나타냅니다. 즉, 왼쪽에서 오른쪽으로 쓰는 언어로, 오른쪽에서 왼쪽으로 쓰는 언어로 된 모습입니다. start
제약 조건 집합은 애니메이션의 시작을 나타냅니다.
최종 ConstraintSet 정의
- 체인을 사용하여
@id/credits
아래에 세 개의 별표를 모두 함께 배치하도록 끝 제약 조건을 정의합니다. 또한 왼쪽 및 오른쪽 별표alpha
의 끝 값을1.0
로 설정합니다.
step2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
결과적으로 보기는 애니메이션이 실행될 때 중앙에서 위로 퍼져나갑니다.
또한 alpha
속성이 두 ConstraintSets
에서 모두 @id/right_start
및 @id/left_star
에 설정되어 있으므로 애니메이션이 진행됨에 따라 두 뷰가 모두 페이드 인됩니다.
사용자 스와이프 기반 애니메이션
MotionLayout
는 사용자 드래그 이벤트 또는 스와이프를 추적하여 물리학 기반의 '플링'을 만들 수 있습니다. 애니메이션을 적용할 수 있습니다. 즉, 사용자가 뷰를 플링해도 뷰가 계속 진행되며 표면을 가로질러 굴릴 때 물리적 물체가 하는 것처럼 느려집니다. 이 유형의 애니메이션은 Transition
에서 OnSwipe
태그를 사용하여 추가할 수 있습니다.
OnSwipe
태그를 추가하는 TODO를<OnSwipe motion:touchAnchorId="@id/red_star" />
로 바꿉니다.
step2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
OnSwipe
에는 몇 가지 속성이 포함되어 있으며, 가장 중요한 속성은 touchAnchorId
입니다.
touchAnchorId
는 터치에 대한 응답으로 이동하는 추적된 뷰입니다.MotionLayout
는 이 뷰를 스와이프하는 손가락으로부터 동일한 거리를 유지합니다.touchAnchorSide
는 뷰의 어느 쪽을 추적해야 하는지 결정합니다. 이는 크기가 조절되거나 복잡한 경로를 따르거나 한쪽이 다른 쪽보다 빠르게 이동하는 뷰에 중요합니다.dragDirection
는 이 애니메이션에 중요한 방향 (위, 아래, 왼쪽, 오른쪽)을 결정합니다.
MotionLayout
가 드래그 이벤트를 수신 대기하면 리스너는 touchAnchorId
에 의해 지정된 뷰가 아닌 MotionLayout
뷰에 등록됩니다. 사용자가 화면의 아무 곳에서 동작을 시작하면 MotionLayout
는 손가락과 touchAnchorId
뷰의 touchAnchorSide
사이의 거리를 상수로 유지합니다. 예를 들어 사용자가 앵커 쪽에서 100dp 떨어진 지점을 터치하면 MotionLayout
는 전체 애니메이션에서 해당 쪽이 손가락에서 100dp 떨어지도록 유지합니다.
사용해 보기
- 앱을 다시 실행하고 2단계 화면을 엽니다. 애니메이션이 표시됩니다.
- '플링'을 사용해 보세요. 또는 애니메이션 중간에 손가락을 놓으면
MotionLayout
가 유체 물리학 기반 애니메이션을 표시하는 방식을 살펴볼 수 있습니다.
MotionLayout
는 ConstraintLayout
의 기능을 사용하여 풍부한 효과를 만들어 매우 다양한 디자인 간에 애니메이션을 적용할 수 있습니다.
이 애니메이션에서는 세 개의 뷰가 모두 화면 하단에 상위 요소를 기준으로 배치되어 시작됩니다. 마지막에 3개의 뷰는 체인의 @id/credits
를 기준으로 배치됩니다.
이러한 매우 다른 레이아웃에도 불구하고 MotionLayout
는 시작과 끝 사이에 유동적인 애니메이션을 만듭니다.
5. 경로 수정
이 단계에서는 애니메이션 도중 복잡한 경로를 따라가면서 모션 중에 크레딧에 애니메이션을 적용하는 애니메이션을 빌드합니다. MotionLayout
는 KeyPosition
를 사용하여 시작과 끝 사이에 뷰가 사용할 경로를 수정할 수 있습니다.
1단계: 기존 코드 살펴보기
layout/activity_step3.xml
및xml/step3.xml
를 열어 기존 레이아웃과 모션 장면을 확인합니다.ImageView
및TextView
에는 달과 크레딧 텍스트가 표시됩니다.- 모션 장면 파일 (
xml/step3.xml
)을 엽니다.@id/start
에서@id/end
까지의Transition
이 정의된 것을 볼 수 있습니다. 애니메이션은 두 개의ConstraintSets
를 사용하여 화면 왼쪽 하단에서 화면 오른쪽 하단으로 달 이미지를 이동합니다. 달이 움직이면 크레딧 텍스트가alpha="0.0"
에서alpha="1.0"
으로 페이드 인됩니다. - 이제 앱을 실행하고 3단계를 선택합니다. 달을 클릭하면 달이 시작부터 끝까지 선형 경로 (또는 직선)를 따라 움직이는 것을 볼 수 있습니다.
2단계: 경로 디버깅 사용 설정
달의 움직임에 호를 추가하기 전에 MotionLayout
에서 경로 디버깅을 사용 설정하면 유용합니다.
MotionLayout
로 복잡한 애니메이션을 개발하는 데 도움이 되도록 모든 뷰의 애니메이션 경로를 그릴 수 있습니다. 애니메이션을 시각화하거나 모션의 작은 부분을 세밀하게 조정할 때 유용합니다.
- 디버깅 경로를 사용 설정하려면
layout/activity_step3.xml
를 열고motion:motionDebug="SHOW_PATH"
를MotionLayout
태그에 추가합니다.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
경로 디버깅을 사용 설정한 후 앱을 다시 실행하면 모든 뷰의 경로가 점선으로 시각화됩니다.
- 원은 한 뷰의 시작 또는 종료 위치를 나타냅니다.
- 선은 하나의 뷰의 경로를 나타냅니다.
- 다이아몬드는 경로를 수정하는
KeyPosition
를 나타냅니다.
예를 들어 이 애니메이션에서 가운데 원은 크레딧 텍스트의 위치입니다.
3단계: 경로 수정하기
MotionLayout
의 모든 애니메이션은 애니메이션이 시작되기 전과 애니메이션이 완료된 후의 화면 모양을 정의하는 시작 및 종료 ConstraintSet
로 정의됩니다. 기본적으로 MotionLayout
는 위치가 변경되는 각 뷰의 시작 위치와 종료 위치 사이에 선형 경로 (직선)를 표시합니다.
이 예에서 달의 원호와 같은 복잡한 경로를 빌드하기 위해 MotionLayout
는 KeyPosition
를 사용하여 뷰가 시작과 끝 사이에 이동하는 경로를 수정합니다.
xml/step3.xml
를 열고 장면에KeyPosition
를 추가합니다.KeyPosition
태그는Transition
태그 내에 배치됩니다.
step3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
KeyFrameSet
는 Transition
의 하위 요소이며 전환 중에 적용되어야 하는 모든 KeyFrames
(예: KeyPosition
)의 집합입니다.
MotionLayout
는 시작과 끝 사이의 달 경로를 계산하므로 KeyFrameSet
에 지정된 KeyPosition
에 따라 경로를 수정합니다. 앱을 다시 실행하여 경로가 어떻게 수정되는지 확인할 수 있습니다.
KeyPosition
에는 경로를 수정하는 방법을 설명하는 여러 속성이 있습니다. 가장 중요한 기능은 다음과 같습니다.
framePosition
는 0~100 사이의 숫자입니다. 애니메이션에서 이KeyPosition
를 적용해야 하는 시점을 정의합니다. 1은 애니메이션을 통해 1%, 99%는 애니메이션을 통해 99%를 전달합니다. 값이 50이면 중간에 바로 적용합니다.motionTarget
는 이KeyPosition
가 경로를 수정하는 뷰입니다.keyPositionType
는 이KeyPosition
가 경로를 수정하는 방법입니다.parentRelative
,pathRelative
또는deltaRelative
일 수 있습니다 (다음 단계에서 설명).percentX | percentY
는framePosition
에서 경로를 수정할 정도입니다 (0.0~1.0 사이의 값, 음수 값 및 1보다 큰 값이 허용됨).
다음과 같이 해석할 수 있습니다. framePosition
에서 keyPositionType
에 의해 결정된 좌표에 따라 percentX
또는 percentY
만큼 이동하여 motionTarget
의 경로를 수정합니다.
기본적으로 MotionLayout
는 경로를 수정하여 도입되는 모든 모서리를 둥글게 만듭니다. 방금 만든 애니메이션을 보면 구부러진 곳에서 달이 곡선 경로를 따라가는 것을 확인할 수 있습니다. 대부분의 애니메이션에서 이 기능을 사용하면 되며, 그렇지 않은 경우 curveFit
속성을 지정하여 맞춤설정할 수 있습니다.
사용해 보기
앱을 다시 실행하면 이 단계의 애니메이션이 표시됩니다.
달이 호를 따라가는 이유는 Transition
에 지정된 KeyPosition
를 통과하기 때문입니다.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
이 KeyPosition
는 다음과 같이 읽을 수 있습니다. 'framePosition 50
(애니메이션이 중간 지점)에서 parentRelative
(전체 MotionLayout
)에 의해 결정된 좌표에 따라 50% Y
만큼 (화면 아래쪽의) 50% Y
만큼 이동하여 motionTarget
@id/moon
의 경로를 수정합니다.'
따라서 애니메이션이 끝날 때 달이 화면에서 50% 아래에 있는 KeyPosition
를 통과해야 합니다. 이 KeyPosition
은 X 모션을 전혀 수정하지 않으므로 달은 계속 수평으로 시작부터 끝까지 이동합니다. MotionLayout
는 시작과 끝 사이를 이동하는 동안 이 KeyPosition
를 통과하는 매끄러운 경로를 파악합니다.
자세히 보면 크레딧 텍스트는 달의 위치의 제약을 받습니다. 세로로도 이동하지 않는 이유는 무엇인가요?
<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
달이 이동하는 경로를 수정하더라도 달의 시작 및 끝 위치는 수직으로 전혀 이동하지 않습니다. KeyPosition
는 시작 또는 종료 위치를 수정하지 않으므로 크레딧 텍스트는 달의 최종 끝 위치로 제한됩니다.
달과 함께 크레딧을 이동하려면 크레딧에 KeyPosition
를 추가하거나 @id/credits
의 시작 제약 조건을 수정하면 됩니다.
다음 섹션에서는 MotionLayout
의 다양한 keyPositionType
유형을 자세히 알아봅니다.
6. keyPositionType 이해
이전 단계에서는 keyPosition
유형의 parentRelative
를 사용하여 경로를 화면의 50% 만큼 오프셋했습니다. keyPositionType
속성은 MotionLayout이 percentX
또는 percentY
에 따라 경로를 수정하는 방법을 결정합니다.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
사용 가능한 keyPosition
유형에는 parentRelative
, pathRelative
, deltaRelative
의 세 가지 유형이 있습니다. 유형을 지정하면 percentX
및 percentY
이 계산되는 좌표계가 변경됩니다.
좌표계란 무엇인가요?
좌표계를 사용하면 공간의 점을 지정할 수 있습니다. 화면상의 위치를 설명하는 데도 유용합니다.
MotionLayout
좌표계는 데카르트 좌표계입니다. 즉, 두 개의 수직선으로 정의된 X축과 Y축이 있습니다. 두 지점 간의 주요 차이점은 화면에서 X축이 이동하는 위치입니다 (Y축은 항상 X축과 수직을 이룹니다).
MotionLayout
의 모든 좌표계는 X축과 Y축에서 모두 0.0
과 1.0
사이의 값을 사용합니다. 음수 및 1.0
보다 큰 값을 허용합니다. 예를 들어 percentX
값이 -2.0
이면 X축의 반대 방향으로 두 번 이동합니다.
모두 대수학 수업처럼 들리면 아래 사진을 확인해 보세요!
parentrelative 좌표
parentRelative
의 keyPositionType
는 화면과 동일한 좌표계를 사용합니다. 전체 MotionLayout
의 왼쪽 상단으로 (0, 0)
를 정의하고 오른쪽 하단에 (1, 1)
를 정의합니다.
이 예에서 달의 호와 같이 전체 MotionLayout
를 이동하는 애니메이션을 만들고 싶을 때마다 parentRelative
를 사용할 수 있습니다.
그러나 모션을 기준으로 경로를 수정하려는 경우(예: 약간 곡선으로 만드는 경우) 다른 두 좌표계를 선택하는 것이 더 좋습니다.
deltarelative 좌표
델타는 변화를 나타내는 수학 용어이므로 deltaRelative
은 '상대적으로 변화'를 나타내는 방법입니다. deltaRelative
좌표에서 (0,0)
는 뷰의 시작 위치가고 (1,1)
는 종료 위치입니다. X축 및 Y축은 화면에 맞게 정렬됩니다.
X축은 화면에서 항상 가로이고 Y축은 화면에서 항상 세로입니다. parentRelative
와 비교할 때 주요 차이점은 좌표는 뷰가 이동할 화면의 일부분만 설명한다는 것입니다.
deltaRelative
는 수평 또는 수직 모션을 개별적으로 제어하는 데 유용한 좌표계입니다. 예를 들어, 수직 (Y) 이동을 50%에서 완료하고 수평 (X)으로 계속 움직이는 애니메이션을 만들 수 있습니다.
path상대 좌표
MotionLayout
의 마지막 좌표계는 pathRelative
입니다. X축이 시작부터 끝까지 모션 경로를 따라가기 때문에 다른 두 축과는 상당히 다릅니다. 따라서 (0,0)
는 시작 위치이고 (1,0)
는 종료 위치입니다.
왜 이렇게 해야 할까요? 언뜻 보기에는 상당히 놀랍습니다. 특히 이 좌표계는 화면 좌표계와 정렬되어 있지 않기 때문입니다.
몇 가지 측면에서 pathRelative
가 정말로 유용한 것으로 확인되었습니다.
- 애니메이션이 재생되는 동안 뷰 속도를 높이거나 낮추거나 중지합니다. X 측정기준은 항상 보기에서 이동하는 경로와 정확하게 일치하므로
pathRelative
KeyPosition
를 사용하여 해당 경로에서 특정 지점에 도달하는framePosition
를 변경할 수 있습니다. 따라서percentX="0.1"
가 있는framePosition="50"
의KeyPosition
가 있으면 애니메이션이 모션의 처음 10% 를 이동하는 데 시간의 50% 가 걸립니다. - 경로에 미묘한 호를 추가합니다. Y 차원은 항상 모션과 수직을 이루므로 Y를 변경하면 전체 모션을 기준으로 경로가 변경됩니다.
- 두 번째 측정기준을 추가하면
deltaRelative
이 작동하지 않습니다. 완전한 수평 및 수직 모션의 경우deltaRelative
은 하나의 유용한 측정기준만 생성합니다. 그러나pathRelative
는 항상 사용 가능한 X 및 Y 좌표를 생성합니다.
다음 단계에서는 둘 이상의 KeyPosition
를 사용하여 훨씬 더 복잡한 경로를 빌드하는 방법을 알아봅니다.
7. 복잡한 경로 빌드
이전 단계에서 빌드한 애니메이션을 보면 부드러운 곡선이 만들어지지만 모양은 '달처럼' 보일 수 있습니다.
여러 KeyPosition 요소로 경로 수정
MotionLayout
는 모션을 얻는 데 필요한 만큼의 KeyPosition
를 정의하여 경로를 추가로 수정할 수 있습니다. 이 애니메이션에서는 호를 만들지만 원하는 경우 화면 중앙에서 달이 오르락내리락하게 만들 수도 있습니다.
xml/step4.xml
를 엽니다. 이전 단계에서 추가한 것과 동일한 뷰와KeyFrame
가 있는 것을 확인할 수 있습니다.- 곡선 상단을 둥글게 만들려면
@id/moon
의 경로에KeyPositions
을 두 개 더 추가합니다. 하나는 상단에 도달하기 직전에, 다른 하나는 뒤에 추가합니다.
step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
이러한 KeyPositions
은 애니메이션의 25% 및 75% 동안 적용되고 @id/moon
가 화면 상단에서 60% 떨어진 경로를 통해 이동하도록 합니다. 기존 KeyPosition
를 50%로 결합하면 달이 따라올 부드러운 호를 만들 수 있습니다.
MotionLayout
에서는 원하는 모션 경로를 가져오는 데 필요한 만큼 KeyPositions
를 추가할 수 있습니다. MotionLayout
는 지정된 framePosition
에 각 KeyPosition
를 적용하고 모든 KeyPositions
을 통과하는 부드러운 모션을 만드는 방법을 파악합니다.
사용해 보기
- 앱을 다시 실행합니다. 4단계로 이동하여 애니메이션의 실제 동작을 확인합니다. 달을 클릭하면 달은 시작부터 끝까지 경로를 따라
KeyFrameSet
에 지정된 각KeyPosition
를 통과합니다.
직접 탐색하기
다른 유형의 KeyFrame
로 이동하기 전에 KeyFrameSet
에 KeyPositions
를 더 추가하여 KeyPosition
를 사용하여 어떤 종류의 효과를 만들 수 있는지 확인해 보세요.
다음 한 가지 예는 애니메이션 중에 앞뒤로 이동하는 복잡한 경로를 만드는 방법을 보여줍니다.
step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
KeyPosition
탐색을 완료했다면 다음 단계에서 다른 유형의 KeyFrames
로 이동합니다.
8. 모션 중 속성 변경
동적 애니메이션을 빌드하려면 애니메이션이 진행됨에 따라 뷰의 size
, rotation
또는 alpha
를 변경해야 하는 경우가 많습니다. MotionLayout
를 사용하면 KeyAttribute
를 사용하여 모든 뷰에서 여러 속성을 애니메이션으로 만들 수 있습니다.
이 단계에서는 KeyAttribute
를 사용하여 달의 크기를 조정하고 회전합니다. 또한 KeyAttribute
를 사용하여 달의 여정을 거의 완료할 때까지 텍스트가 표시되는 시간을 지연시킵니다.
1단계: KeyAttribute를 사용하여 크기 조절 및 회전
- 이전 단계에서 빌드한 것과 동일한 애니메이션이 포함된
xml/step5.xml
를 엽니다. 이 화면은 다양한 공간 사진을 배경으로 사용합니다. - 달의 크기가 커지고 회전하게 하려면
KeyFrameSet
의keyFrame="50"
및keyFrame="100"
에KeyAttribute
태그 두 개를 추가합니다.
step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
이러한 KeyAttributes
는 애니메이션의 50% 및 100% 에 적용됩니다. 50% 의 첫 번째 KeyAttribute
는 원호 상단에서 발생하며 뷰 크기가 두 배가 되고 -360도 (또는 하나의 완전한 원)로 회전합니다. 두 번째 KeyAttribute
는 두 번째 회전을 -720도 (완전히 2개의 원)로 마무리하고 scaleX
및 scaleY
값의 기본값이 1.0이므로 크기를 다시 일반으로 축소합니다.
KeyPosition
와 마찬가지로 KeyAttribute
는 framePosition
및 motionTarget
를 사용하여 KeyFrame
를 적용할 시점과 수정할 뷰를 지정합니다. MotionLayout
는 KeyPositions
사이에 보간하여 유동적인 애니메이션을 만듭니다.
KeyAttributes
는 모든 보기에 적용할 수 있는 속성을 지원합니다. visibility
, alpha
또는 elevation
와 같은 기본 속성 변경을 지원합니다. 여기서와 같이 회전을 변경하거나, rotateX
및 rotateY
를 사용하여 3차원으로 회전하거나, scaleX
및 scaleY
로 크기를 조정하거나, X, Y, Z로 뷰의 위치를 변환할 수도 있습니다.
2단계: 크레딧 표시 지연
이 단계의 목표 중 하나는 애니메이션이 거의 완료될 때까지 크레딧 텍스트가 표시되지 않도록 애니메이션을 업데이트하는 것입니다.
- 크레딧 표시를 지연하려면
keyPosition="85"
까지alpha
가 0으로 유지되도록KeyAttribute
를 하나 더 정의합니다.MotionLayout
는 여전히 0에서 100 알파로 원활하게 전환되지만 애니메이션의 마지막 15% 에 걸쳐 전환됩니다.
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
이 KeyAttribute
는 애니메이션의 처음 85% 에 대해 @id/credits
의 alpha
를 0.0으로 유지합니다. 알파 0에서 시작하므로 애니메이션의 처음 85% 에는 표시되지 않습니다.
이 KeyAttribute
의 최종 효과는 애니메이션이 끝날 때 크레딧이 표시되는 것입니다. 이렇게 하면 화면의 오른쪽 모서리에 달이 지면에 맞춰 지면과 조화를 이루는 모습이 됩니다.
다른 뷰가 이렇게 이동하는 동안 한 뷰에서 애니메이션을 지연시키면 사용자에게 역동적으로 느껴지는 인상적인 애니메이션을 빌드할 수 있습니다.
사용해 보기
- 앱을 다시 실행하고 5단계로 이동하여 애니메이션이 실제로 작동하는 것을 확인합니다. 달을 클릭하면 달은 시작부터 끝까지 경로를 따라
KeyFrameSet
에 지정된 각KeyAttribute
를 통과합니다.
달을 두 개의 완만한 원을 회전시켰기 때문에 이제 달이 두 번 뒤로 젖히고 애니메이션이 거의 끝날 때까지 크레딧이 나타나기 지연됩니다.
직접 탐색하기
최종 유형의 KeyFrame
로 이동하기 전에 KeyAttributes
에서 다른 표준 속성을 수정해 보세요. 예를 들어 rotation
를 rotationX
로 변경하여 생성되는 애니메이션을 확인합니다.
다음은 시도할 수 있는 표준 속성 목록입니다.
android:visibility
android:alpha
android:elevation
android:rotation
android:rotationX
android:rotationY
android:scaleX
android:scaleY
android:translationX
android:translationY
android:translationZ
9. 맞춤 속성 변경
리치 애니메이션에는 뷰의 색상 또는 기타 속성을 변경하는 작업이 포함됩니다. MotionLayout
는 KeyAttribute
를 사용하여 이전 작업에 나열된 표준 속성을 변경할 수 있지만 CustomAttribute
를 사용하여 다른 속성을 지정합니다.
CustomAttribute
는 setter가 있는 모든 값을 설정하는 데 사용할 수 있습니다. 예를 들어 CustomAttribute
를 사용하여 뷰에서 backgroundColor를 설정할 수 있습니다. MotionLayout
는 리플렉션을 사용하여 setter를 찾은 다음 반복적으로 호출하여 뷰에 애니메이션을 적용합니다.
이 단계에서는 CustomAttribute
를 사용하여 달에 colorFilter
속성을 설정하여 아래와 같은 애니메이션을 빌드합니다.
맞춤 속성 정의
- 시작하려면 이전 단계에서 빌드한 애니메이션이 포함된
xml/step6.xml
를 엽니다. - 달의 색상을 변경하려면
KeyFrameSet
의keyFrame="0"
,keyFrame="50"
,keyFrame="100".
에CustomAttribute
이 있는 두 개의KeyAttribute
를 추가합니다.
step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
KeyAttribute
내에 CustomAttribute
를 추가합니다. CustomAttribute
는 KeyAttribute
로 지정된 framePosition
에 적용됩니다.
CustomAttribute
내에서 attributeName
와 설정할 값 하나를 지정해야 합니다.
motion:attributeName
는 이 맞춤 속성에서 호출할 setter의 이름입니다. 이 예에서는Drawable
의setColorFilter
가 호출됩니다.motion:custom*Value
는 이름에 명시된 유형의 맞춤 값입니다. 이 예에서 맞춤 값은 지정된 색상입니다.
커스텀 값의 유형은 다음과 같습니다.
- 색상
- 정수
- 부동 소수점 수
- 문자열
- 측정기준
- 불리언
이 API를 사용하면 MotionLayout
는 모든 뷰에서 setter를 제공하는 모든 항목에 애니메이션을 적용할 수 있습니다.
사용해 보기
- 앱을 다시 실행하고 6단계로 이동하여 애니메이션이 실제로 작동하는 것을 확인합니다. 달을 클릭하면 달은 시작부터 끝까지 경로를 따라
KeyFrameSet
에 지정된 각KeyAttribute
를 통과합니다.
KeyFrames
를 더 추가하면 MotionLayout
는 달의 경로를 직선에서 복잡한 곡선으로 변경하여 이중 뒤로 뒤집기, 크기 조절, 애니메이션 중간에 색상이 변경됩니다.
실제 애니메이션에서는 종종 다른 경로와 속도에 따라 모션을 제어하는 여러 뷰를 동시에 애니메이션으로 만들 수 있습니다. 각 뷰에 서로 다른 KeyFrame
를 지정하면 MotionLayout
로 여러 뷰에 애니메이션을 적용하는 풍부한 애니메이션을 구성할 수 있습니다.
10. 드래그 이벤트 및 복잡한 경로
이 단계에서는 OnSwipe
를 복잡한 경로와 함께 사용하는 방법을 살펴봅니다. 지금까지 달의 애니메이션은 OnClick
리스너에 의해 트리거되었으며 고정된 시간 동안 실행됩니다.
지난 몇 단계에서 빌드한 달 애니메이션과 같이 OnSwipe
를 사용하여 경로가 복잡한 애니메이션을 제어하려면 OnSwipe
의 작동 방식을 이해해야 합니다.
1단계: OnSwipe 동작 살펴보기
xml/step7.xml
를 열고 기존OnSwipe
선언을 찾습니다.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- 기기에서 앱을 실행하고 7단계로 이동합니다. 호의 경로를 따라 달을 드래그하여 부드러운 애니메이션을 만들 수 있는지 확인해 보세요.
이 애니메이션을 실행하면 잘 보이지 않습니다. 달이 원호의 정상에 도달하면 달이 뛰어다니기 시작합니다.
버그를 이해하려면 사용자가 원호의 상단을 터치할 때 어떤 일이 일어나는지 생각해 보세요. OnSwipe
태그에 motion:touchAnchorSide="bottom"
가 있기 때문에 MotionLayout
는 애니메이션이 진행되는 동안 손가락과 뷰 하단 사이의 거리를 일정하게 하려고 합니다.
그러나 달의 하단이 항상 같은 방향으로 움직이지는 않으므로 MotionLayout
는 사용자가 원호의 꼭대기를 방금 지나갔을 때 어떻게 해야 할지 알 수 없습니다. 이를 생각해보면 달의 하단을 추적하고 있으므로 사용자가 달의 하단을 터치할 때 어느 위치에 배치해야 할까요?
2단계: 오른쪽 사용하기
이러한 버그를 방지하려면 항상 전체 애니메이션 재생 시간 동안 항상 한 방향으로 진행되는 touchAnchorId
및 touchAnchorSide
를 선택하는 것이 중요합니다.
이 애니메이션에서는 달의 right
면과 left
쪽이 화면에서 모두 한 방향으로 진행됩니다.
그러나 bottom
와 top
는 모두 방향을 반대로 합니다. OnSwipe
에서 이들을 추적하려고 하면 방향이 바뀌면 혼란스러워집니다.
- 이 애니메이션이 터치 이벤트를 따르도록 하려면
touchAnchorSide
를right
로 변경합니다.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
3단계: DragDirection 사용
dragDirection
과 touchAnchorSide
를 결합하여 평소와 다른 사이드 트랙을 만들 수도 있습니다. touchAnchorSide
가 한 방향으로만 진행되는 것이 중요하지만, MotionLayout
에 추적할 방향을 지정할 수 있습니다. 예를 들어 touchAnchorSide="bottom"
를 유지하되 dragDirection="dragRight"
를 추가할 수 있습니다. 이렇게 하면 MotionLayout
에서 뷰 하단의 위치를 추적하지만 오른쪽으로 이동할 때 위치만 고려합니다 (세로 모션은 무시됨). 따라서 하단이 위아래로 이동하더라도 OnSwipe
를 사용하여 올바르게 애니메이션 처리됩니다.
- 달의 움직임을 올바르게 추적하려면
OnSwipe
를 업데이트하세요.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
사용해 보기
- 앱을 다시 실행하고 달을 전체 경로를 통해 드래그해 보세요. 복잡한 호를 따르지만
MotionLayout
는 스와이프 이벤트에 대한 응답으로 애니메이션을 진행할 수 있습니다.
11. 코드를 사용한 모션 실행
MotionLayout
는 CoordinatorLayout
와 함께 사용할 때 풍부한 애니메이션을 빌드하는 데 사용할 수 있습니다. 이 단계에서는 MotionLayout
를 사용하여 접을 수 있는 헤더를 빌드합니다.
1단계: 기존 코드 살펴보기
- 시작하려면
layout/activity_step8.xml
을 엽니다. layout/activity_step8.xml
에서 작동하는CoordinatorLayout
및AppBarLayout
가 이미 빌드되었음을 확인할 수 있습니다.
activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
이 레이아웃은 CoordinatorLayout
를 사용하여 NestedScrollView
와 AppBarLayout
간에 스크롤 정보를 공유합니다. 따라서 NestedScrollView
가 위로 스크롤되면 AppBarLayout
에 변경사항을 알립니다. 이런 방식으로 Android에서 이와 같이 접히는 툴바를 구현할 수 있습니다. 텍스트의 스크롤이 '조정'됩니다. 있습니다.
@id/motion_layout
가 가리키는 모션 장면이 이전 단계의 모션 장면과 유사합니다. 그러나 CoordinatorLayout
와 호환되도록 OnSwipe
선언은 삭제되었습니다.
- 앱을 실행하고 8단계로 이동합니다. 텍스트를 스크롤할 때 달이 움직이지 않습니다.
2단계: MotionLayout 스크롤 만들기
NestedScrollView
가 스크롤되는 즉시MotionLayout
뷰가 스크롤되도록 하려면motion:minHeight
및motion:layout_scrollFlags
를MotionLayout
에 추가합니다.
activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- 앱을 다시 실행하고 8단계로 이동합니다. 위로 스크롤하면
MotionLayout
가 접힙니다. 하지만 애니메이션은 아직 스크롤 동작에 따라 진행되지 않습니다.
3단계: 코드로 모션 이동
Step8Activity.kt
을 엽니다 .coordinateMotion()
함수를 수정하여MotionLayout
에 스크롤 위치 변경사항을 알립니다.
Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
이 코드는 사용자가 현재 스크롤 오프셋으로 스크롤할 때마다 호출되는 OnOffsetChangedListener
를 등록합니다.
MotionLayout
는 진행률 속성을 설정하여 전환을 찾을 수 있도록 지원합니다. verticalOffset
및 백분율 진행률 간에 변환하려면 총 스크롤 범위로 나눕니다.
사용해 보기
- 앱을 다시 배포하고 8단계 애니메이션을 실행합니다.
MotionLayout
는 스크롤 위치에 따라 애니메이션을 진행합니다.
MotionLayout
를 사용하여 맞춤 동적 축소 툴바 애니메이션을 빌드할 수 있습니다. KeyFrames
시퀀스를 사용하여 매우 과감한 효과를 얻을 수 있습니다.
12. 축하합니다
이 Codelab에서는 MotionLayout
의 기본 API를 다루었습니다.
MotionLayout
의 실제 예를 더 보려면 공식 샘플을 확인하세요. 문서도 확인해 보세요.
자세히 알아보기
MotionLayout
는 반복되는 주기로 경로나 속성을 제어할 수 있는 KeyCycle,
와 시계 시간에 따라 애니메이션을 적용할 수 있는 KeyTimeCycle,
와 같이 이 Codelab에서 다루지 않은 더 많은 기능을 지원합니다. 각 예시의 샘플을 확인하세요.
이 과정의 다른 Codelab 링크는 Kotlin 기반 Android 고급 Codelab 방문 페이지를 참고하세요.