ConstraintLayout 2.0이 출시되면서 MotionLayout이라는 새로운 레이아웃이 등장했다. MotionLayout은 ConstrainLayout을 상속한 레이아웃으로 새로운 방식으로 동적인 화면을 구성한다.
기존에는 애니메이션을 구현하기 위해서는 다음과 같은 기술들을 적용했다.
MotionLayout은 기존의 방식과는 다르게 레이아웃 2개를 만든 뒤, 두 레이아웃 사이의 전환효과를 정의한다. 기본적으로 애니메이션되는 프레임들을 탐색 가능하며, 화면 터치 조작과 키프레임 및 각종 화면 전환 효과 등을 지원한다.
MotionLayout은 별도의 Java/Kotlin 코드 작성 없이도 애니메이션을 만들어 낼수 있다. 원하는 애니메이션 효과를 xml만 선언해주면 된다. 현재는 공개되지 않았지만 추후에는 아래와 같은 툴도 쓸 수 있게 될 것입니다.
API Level 18 이상 지원 (전체 안드로이드 사용자의 95.9%)
TransitionManager와는 달리, MotionLayout은 중첩된 레이아웃 계층뿐만 아니라 Activity Transition에서도 작동할 수 있는 직접적인 자식뷰에 대한 기능들도 제공한다.
MotionLayout은 UI를 움직이거나 사이즈를 조절하고 싶을때 쓸 수 있다. 버튼, 이미지, 타이틀 영역 등 사용자의 조작과 직접적으로 상호작용 할 때 유용하다. 아래의 샘플프로젝트를 다운받아 테스트 해보기 바란다.
샘플 : https://github.com/googlesamples/android-ConstraintLayoutExamples
app 모듈 레벨의 gradle에 다음과 같이 추가한다.
dependencies {
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha2'
}
MotionLayout의 가장 큰 특징이자 장점은 애니메이션에 관련 내용을 별도의 xml로 분리(모듈화,재사용)하여 사용한다는 점이다.
MotionLayout은 ConstraintLayout을 사용할때처럼 화면에 표현하고 싶은 View들과 MotionHelper만을 포함한다. (MotionHelper는 샘플 프로젝트의 21번 FadeIn 참조)
독립된 MotionScene으로 부터 화면 터치에 대한 정의, 키프레임에 대한 정의, 뷰 속성에 대한 정의등을 선언할 수 있으며 MotionLayout은 app:layoutDescription이라는 속성에 MotionScene에 대한 아이디만을 참조하여 사용하게 된다.
MotionLayout의 각 파츠가 어떤 역할을 하는지 하나씩 알아보도록 하자.
MotionScene에서 정의된 ConstraintSet에 의해 레이아웃 간 화면 전환을 지원한다.
그러므로 MotionScene 파일 연결이 필수적으로 필요하며, 연결시에는 “layoutDescription”에 리소스 아이디로 연결한다.
MotionScene은 ConstraintSet, 키프레임, 터치 조작, 화면 전환 효과 등의 애니메이션 정보를 포함한 xml 파일이며, res / xml 폴더에 저장되어야 한다.
MotionScene 내에서 정의되며, 화면을 어떤 식으로 전환시킬지에 대한 정의를 한다.
Transition 내에서 정의된다. 손끝의 움직임과 화면 전환을 일치시켜주는 핸들러이다. 몇가지 설정해야하는 속성들이 있다.
targetView를 클릭했을때 어떤 화면전환 애니메이션 효과를 보여줄지 정의한다.
KeyFrame을 적용하면 자연스럽게 애니메이션에 커브를 적용하거나 모핑을 시도할 수 있다. KeyFrameSet에 적용할 수 있는 태그는 다음과 같다.
아래의 예제를 확인해보자.
<Transition ...>
<KeyFrameSet>
<KeyPosition
motion:keyPositionType="parentRelative"
motion:percentY="0.25"
motion:framePosition="50"
motion:target="@+id/button"/>
</KeyFrameSet>
</Transition>
키프레임에서의 뷰의 위치를 지정한다.
parentRelative와 마찬가지로 상대적으로 직관적인 좌표계이며 일반적으로는 좋은 결과를 나타낸다. 뷰가 수평 또는 수직 동작으로 시작되거나 끝날때 유용하다.
잠재적인 문제가 하나 있는데, 위젯의 시작 위치와 끝 위치의 차이에 따라 정의되므로 차이가 매우 작거나 0인 경우 영향을 받는 축에서 키 프레임의 위치가 변경되지 않는다. 예를 들어 뷰가 화면의 왼쪽에서 오른쪽으로 이동하면서 동일한 높이에 머무르는 경우 위치 키프레임에 deltaRelative percentY를 사용해봐야 아무런 일이 일어나지 않는다.
pathRelative : 시작 상태와 끝 상태 사이의 직선 경로를 기준으로 정의된다. deltaRelative를 좌표계로 사용했을때 생기던 문제점을 해결할 수 있으며, 세로 축에서 이동하지 않는 위젯에서도 pathRelative를 사용하면 위치 키 프레임을 이탈 경로로 설정할 수 있다. -좌표도 지원한다. 끝점이 변경되더라도 일정하게 유지되는 곡선 모양(예 : “S”모양)을 얻을 수 있다.
parentRelative : 좌표가 부모 컨테이너 기준으로 표현된다. 키프레임의 위치를 표현하는 매우 직관적인 방법이다. 일반적으로 컨테이너에 상대적이여야 하는 계산을 할 때 이 방법을 이용한다.
이 좌표계는 부모 위치에만 기반을 두고있기 때문에 , 결과적으로 키프레임의 위치가 차선적 위치(시작/끝 위치에 상대적)로 끝나는 상황이 발생할 수 있다.
KeyFrame 내에서 KeyPosition과 비슷한 역할을 하지만 Position이 아닌 Attribute(속성)에 관여한다.
<KeyFrameSet>
<KeyAttribute
android:scaleX="2"
android:scaleY="2"
android:rotation="-45"
motion:framePosition="50"
motion:target="@id/button" />
</KeyFrameSet>
KeyFrame과 비슷한 속성을 가지고 있음( target, framePosition, curveFit, transitionEasing, transitionPathRotate, drawPath, progress)
View에서 사용하는 attribute를 사용할 수 있음. (visibility, alpha, elevation, rotation, rotationX, rotationY, scaleX, scaleY, translationX, translationY, translationZ)
elevation과 translationZ는 API Level 21 이상에서 사용 가능하다.
ConstraintSet의 기본적인 개념은 모든 레이아웃 위치에 대해 캡슐화를 하는 것이다. View를 재생성할 필요 없이 View에 대한 위치 및 수치만 바꿀 수 있다.
TransitionManager와 함께 동작하며 MotionLayout의 가장 기초적인 부분이 된다.
여러 개의 를 포함할 수 있다.
특정 뷰에 위치나 크기에 대한 대한 제약 조건을 정의할 수 있습다.
<Constraint>에 속하며 뷰에 대한 속성을 변경시킬 수 있다. start되는 constraint에서 정의했다면 end쪽도 정의해야 한다.
아직 Alpha 단계의 라이브러리라 관련 자료가 턱없이 부족하며, 구글 안드로이드 개발자 Nicolas Roard의 포스팅을 참조한 것이 9할이다.MotionLayout으로부터 나오는 산출물들이 Awesome인지라 자꾸자꾸 파보게 된다.
직접 개인 프로젝트에 적용해보고, 이런 저런 테스트해본 결과 아직 애니메이션 관련해서 매끄럽지 않은 부분이 많이 보이지만, 정식버전으로 릴리즈 될때는 많은것들이 수정되고 안정화 될것이라고 생각한다.