Study Record

[Android] Compose 화면 간 이동(Navigation) 본문

안드로이드/compose

[Android] Compose 화면 간 이동(Navigation)

초코초코초코 2023. 9. 19. 18:44
728x90

😶 앱에서 대상 경로 정의

Compose 에서 한 화면에서 다른 화면으로 넘어가는 탐색의 기본 개념 중 하나는 경로이다. 경로는 URL 개념과 유사하게 다른 URL 이 웹사이트의 다른 페이지에 매핑되는 것처럼 경로는 대상에 매핑되어 고유한 식별자 역할을 하는 문자열이다. 대상은 일반적으로 사용자에게 표시되는 항목에 상응하는 단일 컴포저블리거나 그 컴포저블 그룹을 뜻한다.

 

 

😶 Navigation 구성요소

Naviagtion 구성요소는 세가지로 나뉜다.

 

NavController

대상(앱의 화면) 간 이동을 담당한다.

 

NavGraph

이동할 컴포저블 대상을 매핑한다.

 

NavHost

NavGraph 의 현재 대상을 표시하는 컨테이너 역할을 하는 컴포저블이다.

 

 

😶 화면 이동 방법 (NavHost, NavController)

NavHost 는 지정된 경로를 기반으로 다른 컴포저블 대상을 표시하는 컴포저블이다.  NavHost 는 다음과 같이 사용할 수 있다. navController 는 NavHostController 클래스의 인스턴스로 navigate() 메서드를 호출하여 다른 대상으로 이동하는 등의 방식으로 화면 간에 이동하는 데 이 객체를 사용할 수 있다. startDestination 은 앱에서 NavHost 를 처음 표시할 때 기본적으로 표시되는 대상을 정의하는 문자열 경로이다.

NavHost(
    navController,
    startDestination,
    modifier
){
    content
}

 

 

예시를 들 화면은 다음과 같다고 하자. 각 NEXT 버튼을 누르면 다음 화면으로 넘어가고 BACK 버튼을 누르면 처음 화면으로 돌아간다.

 

전체 코드는 다음과 같다.

enum class NavigationTestScreen {
    ONE, TWO, THREE, FOUR
}

@Composable
fun NavigationTestApp() {
    val navController = rememberNavController()

    Scaffold { innerPadding ->
        NavHost(
            navController = navController,
            startDestination = NavigationTestScreen.ONE.name,
            modifier = Modifier.padding(innerPadding)
        ) {
            composable(route = NavigationTestScreen.ONE.name) {
                TestScreen(
                    screenTest = "ONE SCREEN",
                    isBack = false,
                    onNextButtonClicked = { navController.navigate(NavigationTestScreen.TWO.name) },
                )
            }
            composable(route = NavigationTestScreen.TWO.name) {
                TestScreen(
                    screenTest = "TWO SCREEN",
                    onNextButtonClicked = { navController.navigate(NavigationTestScreen.THREE.name) },
                    onBackButtonClicked = {
                        navController.popBackStack(
                            NavigationTestScreen.ONE.name,
                            inclusive = false
                        )
                    }
                )
            }
            composable(route = NavigationTestScreen.THREE.name) {
                TestScreen(
                    screenTest = "THREE SCREEN",
                    onNextButtonClicked = { navController.navigate(NavigationTestScreen.FOUR.name) },
                    onBackButtonClicked = {
                        navController.popBackStack(
                            NavigationTestScreen.ONE.name,
                            inclusive = false
                        )
                    }
                )
            }
            composable(route = NavigationTestScreen.FOUR.name) {
                TestScreen(
                    screenTest = "FOUR SCREEN",
                    isNext = false,
                    onBackButtonClicked = {
                        navController.popBackStack(
                            NavigationTestScreen.ONE.name,
                            inclusive = true
                        )
                    }
                )
            }
        }
    }
}

@Composable
fun TestScreen(
    screenTest: String,
    isBack: Boolean = true,
    isNext: Boolean = true,
    onBackButtonClicked: () -> Unit = {},
    onNextButtonClicked: () -> Unit = {},
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = modifier
            .padding(20.dp)
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = screenTest, color = Color.White)
        Spacer(modifier = Modifier.height(12.dp))
        if (isBack) {
            OutlinedButton(onClick = onBackButtonClicked) {
                Text(text = "BACK")
            }
        }
        Spacer(modifier = Modifier.height(12.dp))
        if (isNext) {
            OutlinedButton(onClick = onNextButtonClicked) {
                Text(text = "NEXT")
            }
        }
    }
}

 

NavHost 의 content 함수 내에서 composable() 함수를 호출할 수 있다. 이 함수의 route 변수는 경로 이름에 해당하는 문자열로 모든 고유 문자열을 사용할 수 있다.

enum class NavigationTestScreen {
    ONE, TWO, THREE, FOUR
}

NavHost(
    navController = navController,
    startDestination = NavigationTestScreen.ONE.name,
    modifier = Modifier.padding(innerPadding)
) {
    composable(route = NavigationTestScreen.ONE.name) {
        TestScreen(
            screenTest = "ONE SCREEN",
            isBack = false,
            onNextButtonClicked = { navController.navigate(NavigationTestScreen.TWO.name) },
        )
    }
    ...
)

 

NavController 는 rememberNavController() 로 정의할 수 있다. 이 navController 로 다음 화면(composable())으로 넘어갈 수 있는데 예시 코드에서는 navigate(), popBackStack() 메서드가 사용되었다.

val navController = rememberNavController()

Scaffold { innerPadding ->
    NavHost(
        navController = navController,
        startDestination = NavigationTestScreen.ONE.name,
        modifier = Modifier.padding(innerPadding)
    ) {
        ...
        composable(route = NavigationTestScreen.TWO.name) {
            TestScreen(
                screenTest = "TWO SCREEN",
                onNextButtonClicked = { navController.navigate(NavigationTestScreen.THREE.name) },
                onBackButtonClicked = {
                    navController.popBackStack(
                        NavigationTestScreen.ONE.name,
                        inclusive = false
                    )
                }
            )
        }
        ...
    }
}

navgate() 에 원하는 고유 경로 이름을 변수로 넣어주면 그 화면으로 이동한다. popBackStack() 은 첫번째 매개변수로 원하는 경로로 이동한다. 두번째 매개변수 inclusive 가 true 이면 첫번째 매개변수의 화면까지 종료하고 false 이면 첫번째 매개변수는 종료하지 않는다.

 

 

😶 앱바 뒤로가기

enum class NavigationTestScreen {
    ONE, TWO, THREE, FOUR
}

@Composable
fun NavigationTestApp() {
    val navController = rememberNavController()
    val backStackEntry by navController.currentBackStackEntryAsState()

    Scaffold(
        topBar = {
            TestAppBar(
                canNavigateBack = navController.previousBackStackEntry != null,
                navigateUp = { navController.navigateUp() },
                currentScreen = backStackEntry?.destination?.route ?: NavigationTestScreen.ONE.name
            )
        }
    ) { innerPadding ->
        /* ... */
    }
}


@Composable
fun TestAppBar(
    canNavigateBack: Boolean,
    navigateUp: () -> Unit,
    currentScreen: String,
    modifier: Modifier = Modifier
) {
    TopAppBar(
        title = { Text(currentScreen) },
        colors = TopAppBarDefaults.mediumTopAppBarColors(
            containerColor = MaterialTheme.colorScheme.primaryContainer
        ),
        modifier = modifier,
        navigationIcon = {
            if (canNavigateBack) {
                IconButton(onClick = navigateUp) {
                    Icon(
                        imageVector = Icons.Filled.ArrowBack,
                        contentDescription = stringResource(R.string.back_button)
                    )
                }
            }
        }
    )
}

 

 

 

 

Compose를 통해 이동  |  Jetpack Compose  |  Android Developers

Compose를 통해 이동 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Navigation 구성요소는 Jetpack Compose 애플리케이션을 지원합니다. Navigation 구성요소의 인프라

developer.android.com

 

 

728x90