본문 바로가기

프로그래밍/Android

[Compose] Jetpack Compose Tutorial

Tutorial (https://developer.android.com/jetpack/compose/tutorial)

 


Lesson 1 : Composable functions
Compose 는 Composable function 으로 구성되어있다. 
이 함수들은 Ui 구성에 집중하기 보다 앱이 어떻게 보이는지 설명하고, 데이터 의존성을 제공함으로써 UI 를 정의하도록 한다.

일반 함수를 Compose 함수를 만들려면 @Composable annotation 을 추가하면 된다.

Composable function 은 다른 Composable function 에서만 호출될 수 있다.

- setConent block : setConent 블록은 Composable function 이 호출될 수 있는 액티비티 레이아웃이다. setContentView 의 Compose 버전이라고 생각하면 된다.

Jetpack Compose 는 Kotlin Complier plugin 을 사용하여 Composable function 를 앱의 Ui 요소로 변환한다. 

 

@Composable annotation
- Compose 함수 만들려면 이 annotation 을 추가한다.
@Preview annotation
- 이 annotation 은 Android Studio 에서 앱을 빌드 하지 않고도 Composable 함수를 미리볼 수 있도록 한다.
Preview 함수는 매개변수가 없는 함수여야 한다. 
프로젝트를 다시 빌드해도 Preview 함수를 호출하는 곳이 없으므로 앱 자체는 변경되지 않는다. Preview 는 Split 영역에서 미리보기를 제공한다. 이 곳에서 @Preview annotation 이 달린 함수의 UI 를 미리보기로 표시한다. 


Lesson 2 : Layouts
UI 요소는 계층적이며, 다른 요소를 포함할 수 있다. Compose 에서는 Composable 함수에서 다른 Composable 함수를 호출하여 UI 계층구조를 만든다.
Column 을 사용하면 UI 요소를 수직으로 정렬할 수 있다. 
Row 를 사용하면 UI 요소를 수평으로 정렬할 수 있고, Box 를 사용하면 UI 요소를 쌓을 수 있다. 

Composable 을 꾸미거나 구성할 때는 Modifier 를 사용한다. 
Modifier 는 Composable 크기, 레이아웃, 모양을 변경하거나 UI 요소를 클릭 가능하여 상위 수준의 상호작용을 가능하게 한다. 

@Composable
fun MessageCard(message: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painterResource(id = R.drawable.profile_picture),
            contentDescription = "Contact profile",
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column {
            Text(text = message.author)
            Spacer(modifier = Modifier.width(4.dp))
            Text(text = message.body)
        }
    }
}



Lesson 3 : Material Design
Compose 의 많은 UI 요소가 Material Design 을 즉시 사용가능하도록 되어있다. 
Theme 과 Surface 로 Composable function 을 래핑하고, Preview 함수에서도 설정하면 Composable 이 앱 테마에 정의된 스타일을 상속하여 앱 전체에서 일관성이 보장된다. 
MaterialDesign 은 ui.theme 패키지 하위에 Color, Typography, Shame 의 세가지 핵심 요소로 구성된다. 

- Color
MaterialTheme.colors 를 사용해 색상 스타일을 지정할 수 있다. 

Image(
    painterResource(id = R.drawable.profile_picture),
    contentDescription = "Contact profile",
    modifier = Modifier
        .size(40.dp)
        .clip(CircleShape)
        .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
)
Text(
    text = message.author,
    color = MaterialTheme.colors.secondaryVariant
)


- Typography
폰트는 style 에 추가하면 된다. 

Text(
    text = message.author,
    color = MaterialTheme.colors.secondaryVariant,
    style = MaterialTheme.typography.subtitle2
)


- Shape 
도형으로 최종 디자인을 마무리 할 수 있다. 

Surface(shape = MaterialTheme.shapes.medium, elevation = 1.dp) {
    Text(
        text = message.body,
        modifier = Modifier.padding(all = 4.dp),
        style = MaterialTheme.typography.body2
    )
}


- Dark Mode Theme 
Preview 에 name 을 통해 Dark Mode 를 사용할 수 있다. 
파일에서 별도의 함수로 여러 Preview 를 만들거나, 또는 하나의 함수에 여러 Preview 를 만들 수 있다. 

@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)



Lesson 4 : List 와 Animation 
- LazyColumn, LazyRow 

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        messages.map {
            item {
                MessageCard(message = it)
            }
        }
    }
}

 

위 코드에서 LazyColumn 에 하위요소로 item 이 있는 것을 알 수 있다. 
item 은 List 를 파라미터로 가져오고, item 의 람다는 Message 의 instance 인 message 라는 매개변수를 받는다. 
이 람다는 List 항목마다 호출된다. (여기 부분은 이해가 안되네. 코드는 이해가 되는데 내용이 이해가 안 감) 

- Animation 

var isExpanded by remember { mutableStateOf(false) }
Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { 
   // 생략 
}

 

Local Ui 상태를 저장하여 관리할 수 있다. 

상태 변경을 추적하려면 remember 와 mutableStateOf 함수를 사용한다.
Composable function 은 remember API 를 사용해 메모리에 로컬 상태를 저장하고 mutableStateOf 에 전달된 값의 변경사항을 추적할 수 있다. 이 변수를 사용하는 하위 요소는 값이 업데이트 되면 자동으로 다시 그려지는데 이것을 Recomposition 이라고 한다. 
변수에 remember 나 mutableStateOf 같은 Compose State API 를 사용하여 상태를 변경하면 UI 가 자동으로 업데이트 된다. 


* by 를 올바르게 사용하려면 getValue, setValue 구문을 import 해야한다. 

val surfaceColor by animateColorAsState(
    targetValue = if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface
)


animateColorAsState 함수를 사용하여 값을 점진적으로 수정되는 애니메이션을 줄 수 있다. 
animateContentSize modifier 으로 부드럽게 애니메이션을 줄 수 있다. 



궁금한 것 
- Preview 의 showBackground 는 무엇인가?
- Compose 함수는 왜 대문자로 시작하는가? 
- LazyColumn 의 람다 이야기가 이해가 안된다.