Compose UI 설계 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose UI 설계 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose의 UI는 변경할 수 없습니다. UI를 설
developer.android.com
ViewModel
ViewModel은 UI 데이터를 관리하고, 데이터의 변경 사항을 관찰할 수 있도록 합니다.
Compose 및 기타 라이브러리 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose 및 기타 라이브러리 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose에서는 자주 이용하는
developer.android.com
data class ToDoData(
val key: Int,
val text: String,
val done: Boolean = false
)
class ToDoViewModel : ViewModel() {
val toDoList = mutableStateListOf<ToDoData>()
val onSubmit: (String) -> Unit = {
val key = (toDoList.lastOrNull()?.key ?: 0) + 1
toDoList.add(ToDoData(key, it))
}
val onEdit: (Int, String) -> Unit = { key, newText ->
val i = toDoList.indexOfFirst { it.key == key }
toDoList[i] = toDoList[i].copy(text = newText)
}
val onToggle: (Int, Boolean) -> Unit = { key, checked ->
val i = toDoList.indexOfFirst { it.key == key }
toDoList[i] = toDoList[i].copy(done = checked)
}
val onDelete: (Int) -> Unit = { key ->
val idx = toDoList.indexOfFirst { it.key == key }
toDoList.removeAt(idx)
}
}
Compose에서 ViewModel을 사용하려면 viewModel() 함수를 사용하여 ViewModel 인스턴스를 가져와야 합니다.
이 함수는 Compose의 Lifecycle과 함께 ViewModel의 생명주기를 자동으로 관리합니다.
@Composable
fun ViewModelExample(viewModel: ToDoViewModel = viewModel()) {
Column {
...
LazyColumn {
items(
items = viewModel.toDoList,
key = { it.key }
) { toDoData ->
ToDo(
toDoData = toDoData,
onEdit = viewModel.onEdit,
onToggle = viewModel.onToggle,
onDelete = viewModel.onDelete
)
}
}
}
}
LiveData
위의 ViewModel 코드와 대부분 비슷하지만 LiveData를 사용하여 UI 데이터를 관리합니다.
LiveData<...>에서 LiveData 내부의 값이 변경되면 LiveData가 갱신되면서 Compose의 State가 갱신됩니다.
하지만, LiveData<List<...>>의 경우에는 List의 참조가 변경될 때, 즉 List가 통째로 다른 List로 바뀌었을 경우에만 LiveData가 갱신되면서 Compose의 State가 갱신됩니다.
새로운 리스트 객체를 할당하는 방법으로 다음이 있습니다.
- newList = mutableListOf<...>().also { list -> list.addAll(oldList) }
- newList = oldList.toMutableList()
- newList = ArrayList(oldList)
Compose 및 기타 라이브러리 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose 및 기타 라이브러리 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose에서는 자주 이용하는
developer.android.com
class ToDoViewModelWithLiveData : ViewModel() {
private val _rawToDoList = mutableListOf<ToDoData>()
private val _toDoList = MutableLiveData<List<ToDoData>>(_rawToDoList)
val toDoList: LiveData<List<ToDoData>> = _toDoList
val onSubmit: (String) -> Unit = {
val key = (_rawToDoList.lastOrNull()?.key ?: 0) + 1
_rawToDoList.add(ToDoData(key, it))
_toDoList.value = mutableListOf<ToDoData>().also { list ->
list.addAll(_rawToDoList)
}
}
val onEdit: (Int, String) -> Unit = { key, newText ->
val i = _rawToDoList.indexOfFirst { it.key == key }
_rawToDoList[i] = _rawToDoList[i].copy(text = newText)
_toDoList.value = _rawToDoList.toMutableList()
}
val onToggle: (Int, Boolean) -> Unit = { key, checked ->
val i = _rawToDoList.indexOfFirst { it.key == key }
_rawToDoList[i] = _rawToDoList[i].copy(done = checked)
_toDoList.value = _rawToDoList.toMutableList()
}
val onDelete: (Int) -> Unit = { key ->
val i = _rawToDoList.indexOfFirst { it.key == key }
_rawToDoList.removeAt(i)
_toDoList.value = ArrayList(_rawToDoList)
}
...
}
Compose에서는 observeAsState() 함수를 사용하여 LiveData를 State로 변환합니다.
이 함수는 LiveData를 subscribe하고, 데이터가 변경될 때 UI를 자동으로 업데이트합니다.
@Composable
fun LiveDataExample(viewModel: ToDoViewModelWithLiveData = viewModel()) {
Column {
...
val items = viewModel.toDoList.observeAsState(emptyList()).value
LazyColumn {
items(
items = items,
key = { it.key }
) { toDoData ->
ToDo(
toDoData = toDoData,
onEdit = viewModel.onEdit,
onToggle = viewModel.onToggle,
onDelete = viewModel.onDelete
)
}
}
}
}
CompositionLocal
Compose에서 CompositionLocal은 컴포지션 트리 내에서 데이터를 공유하고 전달하는 데 사용됩니다.
CompositionLocal을 사용하면 하위 컴포저블 함수에서 상위 컴포저블 함수의 데이터를 직접 접근할 수 있어, 매번 인자로 데이터를 전달할 필요 없이 보다 간결하게 상태나 설정 값을 공유할 수 있습니다.
- CompositionLocalProvider를 통해 상위 컴포저블 함수에서 값을 제공할 수 있습니다.
- 하위 컴포저블 함수에서는 CompositionLocal.current를 사용하여 값을 접근합니다.
CompositionLocal을 사용한 로컬 범위 지정 데이터 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. CompositionLocal을 사용한 로컬 범위 지정 데이터 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Composition
developer.android.com
@Composable
fun CompositionLocalExample() {
CompositionLocalProvider(LocalElevation provides 12.dp) {
Card(
modifier = Modifier.padding(8.dp),
elevation = CardDefaults.cardElevation(
defaultElevation = LocalElevation.current
)
) {
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.4f
)
) {
Column(
modifier = Modifier.padding(16.dp),
) {
Text("LocalElevation.current : ${LocalElevation.current}")
Text("LocalContentColor.current.alpha : ${LocalContentColor.current.alpha}")
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Text("LocalContentColor.current.alpha : ${LocalContentColor.current.alpha}")
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Text("LocalContentColor.current : ${LocalContentColor.current}")
}
CompositionLocalProvider(LocalContentColor provides Color.Magenta) {
Text("LocalContentColor.current : ${LocalContentColor.current}")
}
}
}
}
}
}
Text("LocalElevation.current : ${LocalElevation.current}")
}
Navigation
Compose에서 Navigation을 사용하여 화면 간의 이동을 관리할 수 있습니다.
Compose를 사용한 탐색 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose를 사용한 탐색 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Navigation 구성요소는 Jetpack Compose
developer.android.com
1. 의존성 추가
모듈 수준의 build.gradle 파일에 Navigation 라이브러리를 추가합니다.
dependencies {
implementation("androidx.navigation:navigation-compose:2.7.7")
}
2. NavHost와 NavController 설정
NavController는 네비게이션 상태를 관리하고, NavHost는 네비게이션 그래프를 정의합니다.
composable() 함수를 통해 특정 경로에 대해 실행될 컴포저블 함수를 정의할 수 있습니다.
- composable("Home") : "Home"을 이름으로 하는 경로 설정
- composable("User/{userId}") : 경로에는 파라미터가 포함될 수 있습니다. 이렇게 정의된 파라미터는 NavBackStackEntry 객체를 통해 접근할 수 있습니다.
@Composable
fun NavigationExample(
modifier: Modifier = Modifier,
// NavController : 컴포저블의 백스택을 추적함
// rememberNavController()를 사용하여 NavController 생성
navController: NavHostController = rememberNavController()
) {
// NavHost 생성
// NavController와 Navigation Graph를 연결
NavHost(
navController = navController,
startDestination = "Home"
) {
composable("Home") {
...
}
composable("Office") {
...
}
composable("Playground") {
...
}
composable("User/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.get("userId")
Text(text = "userId : $userId")
}
}
}
3. 경로의 세부사항 정의
navController.navigate()를 통해 현재 화면에서 다른 화면으로 이동할 수 있습니다.
다음의 함수와 옵션을 사용하여 네비게이션 동작을 제어할 수 있습니다.
- popUpTo() : 네비게이션 그래프 내의 특정 경로까지의 백 스택을 제거합니다.
- inclusive : inclusive 옵션을 true로 설정하면, popUpTo로 지정된 경로 자체도 백 스택에서 제거됩니다. 기본값은 false입니다.
- launchSingleTop : launchSingleTop 옵션을 true로 설정하면, 중복된 화면 인스턴스가 생성되는 것을 방지할 수 있습니다. 이는 특정 화면이 이미 네비게이션 백 스택의 맨 위에 있을 때, 새로운 인스턴스를 생성하지 않고 기존 인스턴스를 재사용하도록 합니다. 기본값은 false입니다.
composable("Home") {
Column {
Text("Home")
Button(onClick = {
navController.navigate("Office") {
// 이동 순서를 Home -> Office -> Playground 라 하자
// (현재 스택) Home -> Office
// 여기서 Playground로 이동하면 Home과 Playground 사이에 있는 Office가 스택에서 사라짐
// (현재 스택) Home -> Playground
// 여기서 백 버튼을 누르면 Home으로 이동됨
popUpTo("Home") {
// 이동 순서를 Home -> Office -> Playground 라 하자
// Home에서 Office로 이동하면 Home이 스택에서 사라짐
// (현재 스택) Office
// 여기서 Playground로 이동하면 현재 스택에는 Home이 없기 때문에 아무 일도 안 일어남
// (현재 스택) Office -> Playground
// 여기서 백 버튼을 누르면 Office로 이동됨
inclusive = true
}
}
}) {
Text("Office로 이동")
}
Button(onClick = {
navController.navigate("Playground") {
popUpTo("Home") {
inclusive = true
}
}
}) {
Text("Playground로 이동")
}
Button(onClick = {
navController.navigate("Home") {
// 현재 최상단에 해당 요소가 있다면 새로 스택에 추가하지 않음
// Home -> Home
// (현재 스택) Home
// 여기서 백 버튼을 누르면 앱 종료됨
launchSingleTop = true
}
}) {
Text("Home으로 이동")
}
Button(onClick = {
navController.navigate("User/MyId")
}) {
Text("아이디로 연결")
}
}
}
DI와 Hilt
Compose에서 Hilt를 사용하여 의존성 주입(DI)을 설정하는 방법은 기존 Android View 시스템에서 사용하는 방식과 유사합니다.
Compose 및 기타 라이브러리 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose 및 기타 라이브러리 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose에서는 자주 이용하는
developer.android.com
1. 의존성 추가
프로젝트 수준의 build.gradle 파일에 Hilt의 Gradle 플러그인을 추가합니다.
buildscript {
dependencies {
classpath("com.google.dagger:hilt-android-gradle-plugin:2.51.1")
}
}
모듈 수준의 build.gradle 파일에 Hilt 라이브러리를 추가합니다.
plugins {
...
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
...
kapt {
correctErrorTypes = true
}
}
dependencies {
// Hilt
implementation("com.google.dagger:hilt-android:2.51.1")
kapt("com.google.dagger:hilt-android-compiler:2.51.1")
// Hilt와 Compose Navigation 간의 통합을 지원함
// Compose 내에서 HiltViewModel을 보다 쉽게 사용할 수 있게 해줌
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}
2. Application 클래스 설정
Hilt를 사용하려면 @HiltAndroidApp 어노테이션을 추가하여 Application 클래스를 설정해야 합니다.
@HiltAndroidApp
class MyApplication : Application()
3. Activity 설정
Hilt를 사용하는 Activity에 @AndroidEntryPoint 어노테이션을 추가합니다.
이는 Hilt가 Activity에 의존성을 주입할 수 있도록 합니다.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
...
}
}
}
4. ViewModel 주입
ViewModel 클래스에 @HiltViewModel 어노테이션을 추가하고, 생성자에 @Inject를 사용합니다.
Compose에서 HiltViewModel을 사용하려면 hiltViewModel() 함수를 사용합니다.
@HiltViewModel
class MyViewModel @Inject constructor(
private val service: MyService
) : ViewModel() {
...
}
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
...
}
5. 의존성을 제공할 Hilt 모듈 설정
Hilt 모듈을 사용하여 의존성을 제공할 수 있습니다.
Hilt 모듈은 @Module과 @InstallIn 어노테이션을 사용하여 정의됩니다.
@Provides 및 @Singleton 어노테이션을 통해 애플리케이션 전체에서 단일 인스턴스로 의존성을 제공하는 메서드를 만들 수 있습니다.
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideMyService(): MyService {
...
}
}
'안드로이드 > Compose' 카테고리의 다른 글
[Android] Compose 성능 최적화 (0) | 2024.06.11 |
---|---|
[Android] Compose - 부수 효과 (0) | 2024.05.27 |
[Android] Compose - Animation (0) | 2024.05.25 |
[Android] Compose - ConstraintLayout (0) | 2024.05.17 |
[Android] Compose - Lazy layout (0) | 2024.05.15 |