|

MotionLayout transition 이해하기: progress, OnSwipe, KeyFrame이 각각 하는 일

MotionLayout transition에서 progress, OnSwipe, KeyFrame 역할을 설명하는 안드로이드 대표 이미지
MotionLayout transition의 핵심 역할을 한 번에 정리한다

MotionLayout transition을 처음 볼 때 가장 많이 헷갈리는 부분은 progress, OnSwipe, KeyFrame이 서로 어떤 관계인지입니다. 이 글은 그 부분만 실무 기준으로 짧고 분명하게 정리합니다.

결론부터 말하면, MotionLayout transition은 start와 end 사이를 움직이는 전체 흐름이고, progress는 현재 위치, OnSwipe는 그 progress를 손으로 밀어주는 장치, KeyFrame은 중간 움직임을 다듬는 장치입니다.


먼저 전체 그림부터 잡기

MotionLayout은 단순히 뷰를 한 번 움직이는 도구가 아니라 한 상태에서 다른 상태로 넘어가는 과정을 설계하는 도구입니다. 여기서 기본 단위는 start state, end state, Transition, MotionScene 네 가지입니다. start state는 출발 배치, end state는 도착 배치, Transition은 출발에서 도착까지 가는 규칙, MotionScene은 그 규칙 전체를 담는 XML이라고 보면 됩니다.

MotionLayout의 start state, end state, Transition, MotionScene 흐름을 설명하는 이미지
MotionLayout 핵심 개념 흐름: start state -> Transition -> end state를 MotionScene이 묶는다

Transition는 정확히 무엇인가

Transition은 start state와 end state를 연결하는 규칙입니다. 단순한 시간 설정이 아니라, 어디서 시작하고 어디서 끝나는지, 얼마나 오래 움직이는지, 사용자가 직접 드래그할 수 있는지, 중간 움직임을 따로 조정할 것인지까지 담는 구조입니다. 즉 두 상태를 잇는 선이 아니라 그 사이 이동 규칙 전체라고 보는 편이 정확합니다.

<Transition
    motion:constraintSetStart="@id/collapsed"
    motion:constraintSetEnd="@id/expanded"
    motion:duration="500">

    <OnSwipe
        motion:touchAnchorId="@id/headerImage"
        motion:touchAnchorSide="bottom"
        motion:dragDirection="dragUp" />
</Transition>

이 코드는 접힌 상태에서 시작해 펼친 상태로 끝나고, 500ms 기준 전환이며, 사용자가 위로 끌면 이 전환이 진행된다고 읽으면 됩니다.


MotionLayout transition에서 progress는 무엇을 뜻하나

progress는 현재 transition이 어디까지 왔는지를 나타내는 값입니다. 실무에서는 0f는 start state, 1f는 end state, 0.5f는 중간쯤이라고 이해하면 거의 충분합니다. Android 공식 문서도 app:progress를 0부터 1 사이의 전환 진행값으로 설명합니다.

  • 0f = 아직 시작 화면
  • 0.3f = 30%쯤 이동한 화면
  • 0.5f = 중간 상태
  • 1f = 끝 화면

progress를 이해하면 MotionLayout은 단순 XML 애니메이션이 아니라 상태 변화 엔진처럼 보이기 시작합니다. 스크롤 값, ViewPager 진행도, 플레이어 상태와 연결하는 식으로도 자주 활용할 수 있습니다.

binding.motionLayout.progress = 0f
binding.motionLayout.progress = 0.5f
binding.motionLayout.progress = 1f

OnSwipe는 무엇을 하는가

OnSwipe는 사용자의 손동작을 transition progress에 연결하는 규칙입니다. 즉 OnSwipe가 직접 레이아웃을 다시 배치하는 것이 아니라, 사용자의 드래그를 읽고 그 결과로 progress를 늘리거나 줄입니다. Android 공식 문서도 OnSwipe를 터치 제어를 만드는 요소로 설명하며, dragDirection은 드래그 방향에 따라 progress가 어떻게 증가하는지와 연결됩니다.

  1. 사용자가 드래그한다
  2. OnSwipe가 그 입력을 읽는다
  3. MotionLayout이 progress를 바꾼다
  4. 현재 progress에 맞는 중간 배치를 계산한다

즉 손가락이 직접 뷰를 옮기는 구조가 아니라, 손가락이 progress를 움직이고 progress가 레이아웃 상태를 움직이는 구조입니다.


KeyFrame은 왜 필요한가

start와 end만 있으면 MotionLayout은 기본적으로 그 사이를 보간해서 움직입니다. 대부분의 간단한 전환은 이것만으로도 충분합니다. 하지만 중간쯤에서 잠깐 더 크게 보이게 하거나, 직선이 아니라 약간 휘어서 이동하게 하거나, 70% 지점부터 alpha가 더 빨리 줄게 만들고 싶다면 KeyFrameSet과 그 안의 KeyPosition, KeyAttribute 같은 요소가 필요해집니다.

Android API reference는 KeyFrameSet을 Key 객체들의 컨테이너로 설명하고, KeyPosition은 애니메이션 중 레이아웃 위치를 제어하고, KeyAttribute는 애니메이션 중 post-layout 속성을 제어한다고 설명합니다. 즉 start와 end만으로는 표현하기 어려운 중간 지점의 움직임을 명시하는 것이 KeyFrame의 핵심 역할입니다.

  • KeyPosition: 어디를 지나가게 할지 조정
  • KeyAttribute: 지나가는 동안 어떤 속성이 어떻게 변할지 조정
<KeyFrameSet>
    <KeyPosition
        motion:motionTarget="@id/card"
        motion:framePosition="50"
        motion:keyPositionType="parentRelative"
        motion:percentX="0.5"
        motion:percentY="0.2" />

    <KeyAttribute
        motion:motionTarget="@id/card"
        motion:framePosition="50"
        android:scaleX="1.05"
        android:scaleY="1.05" />
</KeyFrameSet>

이 예제는 progress가 50%쯤일 때 카드가 기본 직선 경로와 조금 다른 위치를 지나가고 동시에 크기가 살짝 커지도록 만든다는 뜻입니다. 즉 새 state를 늘리는 것이 아니라 transition 중간의 품질을 조정하는 것입니다.


많이 헷갈리는 부분을 한 번에 정리

  1. duration은 시간 기준이고 progress는 상태 위치 기준이다
  2. OnSwipe는 입력을 progress 변화로 연결한다
  3. KeyFrame은 state를 늘리는 도구라기보다 한 transition의 중간 품질을 다듬는 도구에 가깝다
  4. 모든 MotionLayout 전환에 KeyFrame이 필요한 것은 아니다

실무에서는 어떤 순서로 설계하면 좋을까

  1. start state와 end state만 먼저 만든다
  2. Transition으로 기본 흐름을 연결한다
  3. progress 기준으로 중간 움직임이 자연스러운지 본다
  4. 사용자 입력이 필요하면 OnSwipe를 붙인다
  5. 중간 궤적이나 속성이 어색할 때만 KeyFrame을 추가한다

이 순서를 지키면 문제의 원인이 state 설계인지, OnSwipe 설정인지, 중간 보정 문제인지 훨씬 구분하기 쉬워집니다. 즉 state 두 개로 기본 전환을 먼저 성공시키고 그 다음에 OnSwipe와 KeyFrame으로 다듬는 방식이 가장 안정적입니다.


ConstraintSet과 비교하면 어디가 다른가

ConstraintSet과 MotionLayout 차이를 비교한 표
ConstraintSet vs MotionLayout: 상태 변경과 전환 설계의 차이

ConstraintSet으로도 상태를 바꿀 수 있지만, MotionLayout은 상태 사이의 전환을 설계하는 데 더 강합니다. 전환이 한 번 발생하면 끝나는 정도라면 ConstraintSet으로도 충분할 수 있습니다. 하지만 중간값이 중요하거나, 드래그와 함께 부드럽게 연결돼야 하거나, 중간 궤적과 속성 변화를 세밀하게 다듬고 싶다면 MotionLayout이 더 적합합니다.


언제 MotionLayout 대신 더 단순한 도구를 쓰면 좋을까

MotionLayout은 강력하지만 항상 정답은 아닙니다. 단순 fade in/out, 버튼 하나의 짧은 scale 효과, 한 번만 재생되는 작은 attention animation처럼 상태 전환보다 단일 속성 변화가 중심인 경우에는 더 단순한 도구가 구현과 유지보수 모두 편할 수 있습니다. 반대로 여러 뷰가 함께 움직이고 위치, 크기, alpha, 여백이 동시에 바뀌며 스와이프와 진행도가 연결돼야 한다면 MotionLayout이 잘 맞습니다.


자주 하는 실수

  1. start와 end가 불안정한데 KeyFrame부터 넣는 실수
  2. OnSwipe를 제스처 처리 코드로만 이해하는 실수
  3. progress를 시간으로만 이해하는 실수
  4. 중간 움직임이 어색한데 state를 계속 늘리는 실수

한 번에 정리

  • Transition은 start와 end를 잇는 전체 규칙이다
  • progress는 그 전환이 어디까지 진행됐는지 나타내는 값이다
  • OnSwipe는 사용자 입력을 progress 변화로 연결한다
  • KeyFrame은 중간 지점의 위치나 속성을 더 정교하게 다듬는다

즉 MotionLayout transition을 이해하는 가장 좋은 방법은 각 요소를 따로 외우는 것이 아니라 하나의 전환 흐름 안에서 각자 맡는 역할로 구분하는 것입니다.


마무리

MotionLayout이 어렵게 느껴지는 이유는 상태, 진행도, 입력, 중간 보정이 한 시스템 안에 같이 들어 있기 때문입니다. 하지만 역할만 분리해서 보면 구조는 의외로 단순합니다. 상태를 정하고, Transition으로 연결하고, progress로 현재 위치를 보고, OnSwipe로 손동작을 연결하고, KeyFrame으로 중간 품질을 다듬는 흐름입니다.

관련해서 기본 배치 개념을 먼저 정리하고 싶다면 안드로이드 ConstraintLayout 완전 정리, 코드로 상태를 바꾸는 쪽을 비교해보고 싶다면 ConstraintSet 사용법 정리 글을 함께 보면 흐름이 더 잘 잡힙니다. 공식 기준은 Android Developers MotionLayout 가이드, MotionLayout API reference를 참고하면 됩니다.

함께보면 좋은 글