Jetpack Compose 의 새로운 레이아웃, LookaheadLayout 알아보기

최상의 애니메이션을 만들기 위한 레이아웃 소개

Ji Sungbin
성빈랜드
8 min readJul 4, 2022

--

[View in English]

Photo by Robert Katzki on Unsplash

지난 6월 29일에 컴포즈 1.3.0-alpha01 이 릴리즈 되면서 새로운 레이아웃인 LookaheadLayout 이 등장하였습니다. LookaheadLayout 의 컨셉을 이해하기 위해 아래와 같은 ExpandableFab 이 있다고 상상해 봅시다.

우리가 상상했던것과 달리 애니메이션이 없어서 예쁘지 않습니다. 애니메이션을 넣어보겠습니다.

이제 우리가 상상했던 ExpandableFab 이 완성됐습니다.

위 ExpandableFab 을 비롯한 애니메이션들은 초기 상태인 start 프레임과, 애니메이션이 진행되고 있는 애니메이션 프레임, 마지막으로 최종 상태인 finish 프레임이 그려지면서 진행됩니다.

그렇다면 컴포즈에서 이런 애니메이션을 구현하기 위해선 위 애니메이션이 그려지는 원리처럼 애니메이션 프레임을 추적하고, 각각 프레임마다 해당 단계에 맞는 새로운 컴포저블을 그려주면 될 거 같습니다.

하지만 1.2.0 버전까지는 이런 애니메이션 프레임을 추적할 수 있는 레이아웃이 존재하지 않습니다. 이를 극복하기 위해 등장한 레이아웃이 LookaheadLayout 입니다.

토스팀에서 제공한 토스페이스가 적용돼 있습니다 (이모지 폰트)

LookaheadLayout 은 start-finish 프레임 사이에 그려질 프레임들을 미리 그려보고, 해당 프레임에 그려질 것으로 예상되는 정보들(measurement, placement)을 기반으로 start-finish 사이에 새로운 레이아웃을 그릴 수 있게 도와줍니다.

start-finish 프레임 사이에 그려지는 프레임들을 추적하는 단계를 Lookahead 단계라고 부르며, Lookahead 단계에서 이렇게 그려질 것이라고 계산된 정보대로 start-finish 사이에 배치될 레이아웃을 intermediate layout 이라고 부릅니다.

Lookahead 단계에서 계산된 정보는 Modifier.onPlaced 로 받을 수 있고, Modifier.intermediateLayout 을 통해 실제로 intermediate layout 을 배치할 수 있습니다.

여기까지 LookaheadLayout 의 컨셉에 대해 알아보았습니다. 이제 실제로 사용해 보겠습니다.

LookaheadLayout 은 실험 단계인 컴포저블이며, content 의 스코프로 LookaheadLayoutScope 를 받고 있습니다.

LookaheadLayoutScope 는 Modifier.onPlaced 와 Modifier.intermediateLayout 을 구현하고 있습니다.

Modifier.onPlaced 는 intermediate layout 이 배치되기 위한 정보가 lookahead 단계에서 계산되면 계산된 값들을 인자로 받고 호출됩니다. 인자로는 LookaheadLayout 과 이 modifier 의 컴포저블이 사용하는 LookaheadLayoutCoordinates 를 받고 있습니다.

onPlaced 가 호출되는 과정을 그림으로 나타내 보면 아래와 같습니다.

핑크색으로 꽉 채워진 부분이 배치된 content 를 뜻합니다

인자로 받는 LookaheadLayoutCoordinates 는 lookahead 단계 진행 전과 후의 레이아웃 모두의 LayoutCoordinates 를 보유하고 있는 인터페이스 입니다. 이를 통해 계산된 intermediate layout 의 오프셋과 현재 배치돼 있는 content 의 오프셋을 가져올 수 있습니다.

LookaheadLayoutCoordinates.localLookaheadPositionOf 와 LookaheadLayoutCoordinates.localPositionOf 모두 특정 coordinate 을 기준으로 변환된 오프셋을 얻기 위해 사용됩니다. 유일한 차이점은 localPositionOf 와 달리 localLookaheadPositionOf 는 coordinate 계산을 위해 lookahead 위치를 사용합니다.

이제 남은 Modifier.intermediateLayout 를 알아보겠습니다.

Modifier.intermediateLayout 을 이용해 lookahead 단계에서 계산된 정보를 기반으로 intermediate layout 를 배치할 수 있습니다.

Modifier.onPlaced 와 Modifier.intermediateLayout 를 그림으로 표현하면 아래와 같습니다.

핑크색으로 꽉 채워진 부분이 배치된 content 를 뜻합니다

이제 실제로 사용해보기 위해 맨 처음에 보았던 ExpandableFab 의 프레임 과정을 다시 보겠습니다.

애니메이션 프레임 과정을 LookaheadLayout 을 통해 추적할 수 있게 됐고, 해당 애니메이션 프레임 안에선 크기와 오프셋이 변경되고 있습니다. Modifier.onPlaced 와 Modifier.intermediateLayout 를 사용하여 이를 구현할 수 있습니다.

크기 조정을 위해 Modifier.imtermediateLayout 으로 intermediate layout 의 크기가 제공되는 람다인 measure 인자를 통해 intermediate layout 를 morph 해주면 되고, 오프셋 조정을 위해 Modifier.onPlaced 으로 계산된 intermediate layout 의 오프셋을 기반으로 content 의 배치를 조정해 주면 됩니다.

오프셋 조정을 위한 Modifier.movement 를 먼저 만들어 보겠습니다.

다음으로 사이즈 조정을 위해 Modifier.transformation 을 만들어 주었습니다.

이제 이렇게 만든 Modifier 들을 적용하기 위해 기존 Fab 을 LookaheadLayout 으로 감싸주었고, Modifier 을 연결해 주었습니다.

결과는 아래와 같습니다.

하지만 우리가 원하던바와 다르게 애니메이션이 적용되지 않았습니다. intermediate layout 을 배치해주기만 했지 애니메이션을 적용히지 않아 순식간에 끝나기에 기존과 다른점이 보이지 않기 때문입니다.

Animtable 을 이용하여 애니메이션 처리를 해줄 수 있습니다. 사이즈와 오프셋 변동에 애니메이션을 주겠습니다.

이렇게 애니메이션을 넣어주면 우리가 원하던대로 나오게 됩니다.

끝!

이렇게 LookaheadLayout 에 대해 알아보았습니다. LookaheadLayout 을 활용하면 shared element transition 부터 시작해서 정말 많은 애니메이션들을 쉽게 구현할 수 있습니다.

이 글의 예제에 쓰인 ExpandableFab 의 전체 코드는

에서 확인하실 수 있습니다.

추가로 이번 1.3.0-alpha01 이 릴리즈 되면서 컴포즈가 독립 버전 관리 체계로 바뀌기 시작했습니다. 아직은 컴파일러만 분리돼 있으며 컴포즈 컴파일러 버전을 1.3.0-alpha01 으로 하면 코틀린 1.7.0 버전을 사용하실 수 있습니다.

감사합니다.

안드로이드 개발자 분들을 위한 카카오톡 오픈 채팅방을 운영하고 있습니다.

추가로 성빈랜드의 첫 메인 프로젝트를 같이 만들어갈 안드로이드 개발자분을 모집하고 있습니다! 자세한 정보는 이곳을 확인해 주세요. 감사합니다.

--

--

Ji Sungbin
성빈랜드

Experience Engineers for us. I love development that creates references.