컴포즈는 코틀린으로 만들어져서 강력하다!
모던하고 강력함, 코드를 읽고 쓰기 편함. 간단함.
재사용 가능한 Component를 만들기 쉬움.
UI 요구 사항이 복잡해짐에 따라 각 UI는 상태에 따라 변경되어야 하는데 이게 꼬여서 발생되는 버그가 많음. 이걸 쉽게 해결하기 위해서 나온게 선언형 UI
NewProject > Empty Compose Activity
Jetpack Activity의 onCreate() 함수에서 setContent에서 해당 액티비티의 UI 레이아웃을 정의합니다.
=> 이 코드 블럭에 UI 요소를 추가하면된다.
기존 앱의 일부 UI만 컴포즈로 구성할 수 있음.
android {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.2"
}
}
Composble하게 만들려면 @Composable
을 붙여주면 됨.
@Preview
를 붙이면 실 단말이나 에뮬레이터를 사용하지 않고도 UI 미리보기가 가능함.
사용할 땐 @Preview
를 먼저 붙이고 @Composable
를 붙임.
// 실제 UI
@Composable
fun MessageCard(name: String) {
Text(text = "Hello $name!")
}
// 미리보기
@Preview
@Composable
fun PreviewMessageCard() {
MessageCard("Android")
}
1. 컴포지션: 표시할 UI입니다. Compose는 구성 가능한 함수를 실행하고 UI 설명을 만듭니다.
2. 레이아웃: UI를 배치할 위치입니다. 이 단계는 측정과 배치라는 두 단계로 구성됩니다. 레이아웃 요소는 레이아웃 트리에 있는 각 노드의 레이아웃 요소 및 모든 하위 요소를 2D 좌표로 측정하고 배치합니다.
3. 그리기: UI를 렌더링하는 방법입니다. UI 요소는 일반적으로 기기 화면인 캔버스에 그려집니다.
안드로이드 툴(=안드로이드 스튜디오)로 UI를 새로운 함수로 추출해 재사용할 수 있게 해줌(툴이 좋다는 뜻인듯)
UI 미리보기 제공
Composable 함수 끼리 호출해서 UI 계층적 구조를 만들 수 있음.
여러가지 UI함수를 나열해서 사용하면, 그 UI들 간에 정렬이 필요함.
이때 사용할 수 있는것들이
또한 UI간 간격이나 크기 모양을 변경할 때는 Modifier
를 사용하면 된다.
Image(
painter = painterResource(R.drawable.profile_picture),
contentDescription = "Contact profile picture",
modifier = Modifier
// Set image size to 40 dp
.size(40.dp)
// Clip image to be shaped as a circle
.clip(CircleShape)
)
더 복잡한 UI를 구성하기 위해 ConstraintLayout을 제공하지만 위 세개를 사용해서 훨씬 간단하게 구성할 수 있음.
컴포즈에서는 마테리얼 디자인을 적용할 수 있음.
Material Design은 Color
, Typography
, Shape
세 가지 핵심 요소를 중심으로 구성됨.
Color
색상MaterialTheme.colors
를 사용하여 정의된 색상 사용 가능 Typography
서체 스타일Text
에 추가하면됨.Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Shape
도형// 아래 처럼 어노테이션을 붙여주면 두가지 미리보기를 같이 볼 수 있음.
@Preview(name = "Light Mode")
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
ComposeTutorialTheme {
Surface {
MessageCard(
msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
)
}
}
}
Material: 테마, 컬러, 모양 변경이나 Button, Cards, FABs, 등등.. 자주 사용되는것들 제공
위에 Layout에서 봤던 정렬을 위한걸 제공하고, 이걸 중첩해서 사용해야함.
애니메이션 시스템이 아주 간단하고 효과적으로 적용할 수 있게 변경되었음.
테스트 객체가 1급 접근성. UI테스트를 위한 것들이 많다. 애니메이션 까지도.,.,
코루틴을 사용하면 비동기 처리를 쉽게 할 수 있음.
State<T>
는 immutable, mutable하게 우리가 값을 바꾸고 싶은 값에 사용.val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
아래 처럼 그리드 커스텀도 가능하고
스크롤 될때 아이템 애니메이션 주기도 쉽다. => item.animatePlacement { ... }
reordering 기능도 제공하고 lazy Grid도 제공한다.
key: 기본적으로 항목 위치를 기준으로 키가 정해지는데 원하는대로 데이터의 id값을 키로 줄수도 있음. 키가 있으면 데이터랑 일관성이 유지되어 관리가 더 쉬움.
LazyColumn/Row
표현되는 요소만 렌더링하여 긴 목록에서 효율적으로 사용됨.(RecyclerView 같은)
패딩줄땐 패딩 모디파이어를 쓰면 되는데 얘는 보여지는 컨텐츠 영역 자체를 좁혀버려서
contentPadding을 사용하면 패딩은 주되 보여지는 컨텐츠 영역이 좁아지지 않아서 얘를 활용하는 방법도 있다~
컨텐츠 아이템 사이의 패딩은 arrangemment space 사용해서 간격 띄워주면 된다.
LazyColumn(
// 패딩
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
// 간격
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
// ...
}
val state = rememberLazyListState()
val list = state.layoutInfo.visibleItemsInfo.map{
it.index
}
state.layoutInfo.totalItemsCount
LazyVerticalGrid, LazyHorizontalGrid 두가지가 있고
columns와 rows 두개를 매개변수로 받음.
GridCells.Adaptive(minSize = 128.dp)
같은걸로 128dp 이상의 크기를 가진 그리드를 그릴 수 있음.
사용할 열의 수를 정확히 알고있으면GridCells.Fixed
를 사용하면 됨.
items도 사용가능한데 span
을 사용해서 열을 지정해 줌.
// items 사용 예시
LazyVerticalGrid(
// ...
) {
item(span = {
// LazyGridItemSpanScope:
// maxLineSpan
GridItemSpan(maxLineSpan)
}) {
CategoryCard(“Fruits”)
}
// ...
}
여러 요소가 한 항목의 일부로 내보내지면 한항목으로 처리되어 개별적으로 구성하기가 힘듬.
모든 요소를 한 항목에 배치하는 극단적인 경우에는 지연 레이아웃 사용의 목적이 완전히 무효화됩니다. 잠재적인 성능 문제 외에도 한 항목에 더 많은 요소를 넣으면 scrollToItem() 및 animateScrollToItem()도 방해합니다
// 나쁜예
LazyVerticalGrid(
// ...
) {
item { Item(0) }
item {
Item(1)
Item(2)
}
item { Item(3) }
// ...
}
// 좋은 예
LazyVerticalGrid(
// ...
) {
item { Item(0) }
item {
Item(1)
Divider()
}
item { Item(2) }
// ...
}
배치에 대한 구체적인 요구사항이 있을 경우
object TopWithFooter : Arrangement.Vertical {
override fun Density.arrange(
totalSize: Int,
sizes: IntArray,
outPositions: IntArray
) {
var y = 0
sizes.forEachIndexed { index, size ->
outPositions[index] = y
y += size
}
if (y < totalSize) {
val lastIndex =
outPositions.lastIndex
outPositions[lastIndex] =
totalSize - sizes.last()
}
}
}