안드로이드 애니메이션 구현을 위한 MotionLayout 이해하기
넌 이런 거(MotionLayout) 피지 마라…
1. 들어가며
올 해 여름, 지인의 소개로 한 스타트업의 앱을 개발할 기회가 있었습니다. 하드웨어를 제조하는 업체에서 제어를 위한 앱을 개발하고자 하였고, 당시 팀 내에는 별도의 개발자가 없는 상황이었습니다. 두근두근하는 마음으로 앱의 시안을 전달받았고, 브랜딩과 디자인에 공을 들이고있는 회사답게 전달받은 디자인의 복잡성은 무시무시(?)했습니다.
위의 화면에서는 2개의 하드웨어를 제어할 수 있고, 각각의 작동 상태가 나머지 하드웨어의 상태에도 영향을 주었습니다. 따라서 표현해야하는 상황과 앱 내 컴포넌트들의 움직임들이 다양했습니다.
위치가 정적인 간단한 애니메이션은 Lottie를 이용하여 구현해본 적이 있었지만, 이런 디자인과 애니메이션을 구현해본 경험은 없었기 때문에 시안에 대한 피드백이 어려웠습니다. 무조건 안된다고 할 수는 없으니까요.
구현 가능성에 대한 답변을 위해 애니메이션 관련 라이브러리나 Lottie를 사용하는 방법을 검토해보았습니다. 최대한 구현 시간을 절약하려던 잔머리를 부려봤지만 디자인 시안의 개성이 너무 강해(?) 사용할 수 없었습니다.
그러던 중 안드로이드에서 공식으로 제공하는 모션과 애니메이션을 위한 레이아웃인 MotionLayout을 알게되었습니다.
2. MotionLayout의 개념과 적용 방법
공식 다큐먼트에 적힌대로 MotionLayout은 모션과 위젯 애니메이션을 구현하는데 사용하는 레이아웃입니다. ConstraintLayout을 상속받기 때문에 xml에서 최상단 뷰를 MotionLayout으로만 설정해주면 됩니다.
핵심적으로 먼저 이해하면 좋을 구현 과정들을 먼저 이해하면 쉽습니다.
(1) 애니메이션을 넣고자 하는 레이아웃을 MotionLayout으로 감싸기
(2) 애니메이션 정의하기(MotionScene)
(3) 애니메이션을 정의한 파일을 MotionLayout에 적용하기
(1) 애니메이션을 넣고자 하는 레이아웃을 MotionLayout으로 감싸기
애니메이션을 적용하고자 하는 뷰들을 MotionLayout으로 감쌉니다. 위의 예시에서는 레이아웃 전체에 애니메이션을 적용하기 위해 기존의 ConstraintLayout을 MotionLayout으로 수정하였습니다.
MotionLayout으로 뷰들을 감싸면, layoutDescription 이라는 속성이 정의가 안되어있어 오류가 발생합니다.
따라서 layoutDescription에는 애니메이션을 정의한 MotionScene 파일(xml 형식)을 연결해줘야 합니다.
(2) 애니메이션 정의하기(MotionScene)
다음으로는 애니메이션을 적용하고자 하는 레이아웃의 layoutDescription 속성에 연결할 MotionScene 파일(xml 형식)을 정의해야합니다.
우선 resource 폴더 내에 xml 폴더가 없을 경우에는 새롭게 생성을 해준 뒤, 폴더 내에 MotionScene을 정의할 xml 파일을 하나 생성합니다.
이렇게 생성해준 xml 파일에 애니메이션을 정의하게 될텐데, 기억해야 할 키워드들은 다음과 같습니다.
📌 Constraint, ConstraintSet
말그대로 Constraint들의 Set입니다. MotionScene 파일에는 애니메이션의 시작과 끝에 해당하는 ConstraintSet 2개를 정의하게 되는데요, Constraint들에는 MotionScene을 연결한 레이아웃 내에 존재하는 각종 뷰들의 상태를 정의합니다. 예를들어 해당 뷰가 ImageView라면 src
속성이나 adjustViewBounds
속성을 설정할 수 있는 것이죠.
📌 Transition
앞서 애니메이션의 시작과 끝에 해당하는 ConstraintSet 2개를 정의하였는데요, 시작에서 끝으로 애니메이션이 진행되는 옵션들을 정의합니다.
constraintSetStart와 constraintSetEnd에는 위에서 정의한 2개의 ConstraintSet을 각각 연결해주고, 이외에 애니메이션이 실행되는 총 기간(duration)이나 페이드인/아웃처럼 애니메이션이 움직이는 속도 옵션(motionInterpolator) 등을 정의해줄 수 있습니다.
Transition을 정의했으면 내부에는 다음과 같은 설정도 할 수 있습니다.
(1) <onSwipe>,<onClick>
스와이프로 Transition을 실행시킬지, 클릭으로 Transition을 실행시킬지 정의합니다.
(2) <keyFrameSet>
애니메이션의 시작과 끝 뿐만 아니라, 중간에 좀 더 세세하게 애니메이션을 정의하고싶을 때 사용합니다. 애니메이션 시작과 끝을 각각 0과 100이라고 했을 때, 0~100 사이 지점 중 하나를 지정하여 애니메이션을 정의할 수 있습니다.
(3) 애니메이션을 정의한 파일을 MotionLayout에 적용하기
지금까지 애니메이션을 정의한 MotionScene xml 파일을 작성해보았습니다. 다시 한 번 요약해볼까요?
ConstraintSet 2개를 정의하여 시작과 끝 상태를 정의하였고, 내부에는 애니메이션을 적용할 뷰들이 연결된 Constraint와 뷰들의 속성들(크기, 위치 등)을 정의하였습니다. 그리고 애니메이션의 시작에서 끝으로 진행되는 애니메이션 방식을 설정한 Transition을 정의하여 MotionScene xml 파일을 작성하였습니다.
엄밀히 말하면 실제로 구현 시에는 MotionLayout xml 파일의 layoutDescription에 MotionScene xml 파일을 연결하여 애니메이션이 적용되는 것을 눈으로 보면서 개발하게 됩니다. 따라서 마지막 단계라고 칭하기에는 적합하지 않지만 개념적으로 이해하기 쉽도록 해당 과정을 마지막 단계로 정의하였습니다.
(1) 애니메이션을 넣고자 하는 레이아웃을 MotionLayout으로 감싸기
(2) 애니메이션 정의하기(MotionScene)
(3) 애니메이션을 정의한 파일을 MotionLayout에 적용하기
3. 마치며
지금까지 설명한 MotionLayout에 대한 학습을 마친 후, 일부 세부적인 효과를 제외하고는 전달받은 시안을 개발할 수 있다는 판단을 내렸습니다.
그 결과…
비록 시안과 100% 동일하지는 않았으나, 구현 상의 애로사항을 디자이너 님께 공유드리며 세부적인 것들을 변경해나갔습니다. 예를들어 위와 같은 창문 속 태양과 달 애니메이션을 정의하려면 피그마에 생성된 이미지 파일을 일부 수정해야 했는데, xml에서 이미지들의 순서를 하나하나 설명하며 수정의 필요성을 설명드리며 합의점에 도달할 수 있습니다.
참고
MotionLayout의 전체적인 개념과 세부적인 속성들을 이해하는 데에 공식 코드랩만한 것이 없었습니다. 저처럼 복잡한 애니메이션을 구현하기에 앞서 막막함이 있으시다면 추천드립니다.