Posted by
RoadtoS7
on January 07, 2022 ·
17 mins read
Compose Tutorial
Android Compose Tutorial 을 공부하며, Compose에 대해 정리해보았다.
추가적으로 현재 Compose를 사용하는 프로젝트에서 애니메이션을 적용해야 해서, 이 부분에 필요한 내용을 추가했다.
Basic
onCreate()에서 setContent{} 블럭이 Activity의 레이아웃을 만드는 코드에 해당한다.
setContent{}에서 Composable Function을 호출한다.
Composable Function이란?
코드 상에서 @Composable 어노테이션이 붙은 함
UI를 코드로 만들 수 있게 해주는 함수
Composable Function은 Composable Function에서만 호출할 수 있다.
Layout
UI는 계층적으로 구성된다. 계층적인 UI는 Composable Function에서 다른 Composable Function을 호출함으로써 만들 수 있다.
두개의 Text()를 연달아서 호출하면 각 텍스트 뷰에 대한 위치 지정을 하지 않았으므로 겹쳐서 나타나게 된다.
Column, Row
Row, Column을 사용하면 view를 같은 row에 두거나, column에 두는 등 쉽게 뷰를 배치할 수 있다.
Modifier
뷰 사이즈, padding, appearance, layout, high-level interaction 설정할 때 사용한다.
Spacing
간격을 설정하는 Composable Function
Material Design
Compose는 매터리얼 원칙을 지원한다.
매터리얼 디자인을 바로 사용할 수 있다.
Surface
Surface를 활용하여 Text의 모양을 변경할 수 있다.
예제 코드: Column, Row, Modifier, Spacing, Material Design, Surface
List && Animation
LazyColumn & LazyRow
화면에 보이는 Column, Row만 렌더링한다.
따라서 길이가 긴 list를 구현할 때 사용하기 적합하다.
LazyColumn
LazyColumn을 호출하는 Composable Function List을 파라미터로 받아야 한다.
items라는 이름의 자식 Composable Function을 호출한다.
items은 List의 각 요소에 대해서 반복문을 돌면서 람다식을 호출한다.
뷰 사이즈 변화에 적용될 애니메이션 만들기
예제 코드에서는 사용자의 메시지를 보여주는 텍스트 뷰를 눌렀을 때 텍스트 뷰가 크기가 커져서 내용이 전부 드러나도록 했다.
이를 위해서는 확장 여부를 저장할 변수를 만들어야 한다.
그리고 이 변수의 상태 변경을 추적할 수 있어야 한다.
Compose에서는 remember 함수와 mutableStateOf함수로 변수의 상태 변화를 추적할 수 있도록 해준다.
동작 과정
Composable Function은 remember를 사용하여 변수의 상태를 메모리에 저장하며 mutableStateOf함수에 전달된 값을 추적할 수 있다.
mutableStateOf로 지정된 값은 변화될 때, Composable Function이 뷰를 업데이트 한다.
만일 remember만 사용하고 mutableStateOf는 사용하지 않을 때 어떻게 되는가❓
결과: mutableStateOf값이 초기(initial) 값으로 초기화된다.
이유: mutableStateOf는 자신의 값이 바뀔 때 자신을 참조하고 있는 모든 Composable Function에 대한 re-compose를 하게 된다.
(여기서 recompose란 Composable Function을 다시 호출하여, 뷰를 다시 그린다는 것을 의미한다.)
이때 remember가 recompose될 때에도 해당 변수의 값은 유지되도록 만들어준다.
아래 예시 코드를 통해서 살펴보자.
OutlinedTextField 가 EditTextView에 해당한다. 이것을 통해 사용자의 입력이 들어오면, onValueChanged 함수가 호출되어 name 변수의 값이 바뀐다.
name변수는 mutableStateOf 로 생성되었기 때문에, 자신의 값이 바뀌면 자신을 참조하고 있는 모든
remember로 만들어진 변수는 다른 Composable Function의 파라미터로 사용할 수 있다.
주의사항‼️ configuration change(ex) 화면 회전) 가 발생했을 때는 remeber 변수는 유지되지 않는다.
이를 위해서는 rememberSavable을 사용해야 한다. rememberSavable은 Bundle에 담길 수 있는 값이면 무엇이든 저장해놓는다.
만약 Bundle에 담길 수 없는 값이라면 저장할 수 있는 객체로 직접 만들어야 한다.
배경색 변화 애니메이션 : animateColorAsState()
메시지를 보여주는 텍스트 뷰 사이즈 변화 애니메이션: animateContentSize()
Compose 상태(state)
여기서 상태(state)란 뷰가 담고 있는 혹은 표현하는 데이터를 의미한다.
즉, TextView가 담고 있는 문장 혹은 단어가 TextView의 상태에 해당한다.
만약 ToggleButton이 현재 toggle on 되어있다면, toggle on이 Toggle Button의 상태이다.
Composable이 자기 자신의 상태를 갖고 있는 것은 좋지 않다.
자신의 상태를 직접 관리하는 Composable은 재활용하기 어렵고, 테스트하기 어려운 Composable이다.
Composable은 stateless하게 만들어주는 것이 좋다.
이때 hoisting을 활용한다.
hoisting이란?
프로그래밍 패턴 중의 하나
Composable을 호출하는 함수에게 Composable의 상태를 맡기는 것이다.
가장 간단한 방법은, Composable은 자신의 상태를 parameter로 전달받고,
이벤트가 발생했음을 뷰로 표현하기 위해서 함수를 사용하는 것이다.
hoisting 을 적용하지 않았을 때의 코드가 다음과 같다고 하자.
hoisting을 적용한 코드는 다음과 같다.
HelloContent의 파라미터 중 name은 현재 OutlinedTextField가 가리키는 값이 된다. onNameChange 는 Composable의 상태(여기서는 name)을 업데이트할 때 호출되는 람다식이다.
이렇게 Composable의 상태와 Composable의 상태를 바꾸는 함수를 상위 함수로 hoisting 함으로써,
Composable을 재활용하기 쉽고, 테스트하기에 용이해진다.
만약 TextField로 부터 받은 입력값을 다른 곳에서 사용하고자 한다면?
ex) Database에 저장하고자 한다.
view model을 사용해라.
view model에서 observable한 holder에 Composable의 상태를 담아서 사용한다.
그리고 view model에 존재하는 값이 바뀌면, 자동으로 Composable이 recompose되도록 만든다.
코드로 보면 다음과 같다.
이렇게 Composable의 상태를 ViewModel에 보관하면, ViewModel은 View보다 생명주기가 길기 때문에,
configuration change가 발생하더라도 view의 상태를 유지할 수 있다.
Composable의 상태를 바꾸는 함수도 viewModel로 hoisting하자.
이렇게 하면 ViewModel만이 view의 상태에 해당되는 name값을 바꾸는 개체가 된다.
네트워크 통신 결과에 따라서 사용자에게 보여주는 화면이 바뀌어야 하듯, UI의 상태는 app의 다른 계층에 의해서도 바뀔 수 있다.
따라서 UI의 상태를 ViewModel로 캡슐화하여 ViewModel만 name 값을 바꿀 수 있도록 하는 것이 좋다.