일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Python
- TEST
- 프로그래머스
- textfield
- Codeforces
- Compose
- pandas
- MiTweet
- 암호학
- activity
- 쿠링
- Coroutines
- MyVoca
- GitHub
- Coroutine
- Kotlin
- livedata
- boj
- androidStudio
- 코루틴
- android
- relay
- AWS
- ProGuard
- architecture
- Hilt
- 백준
- 코드포스
- Gradle
- Rxjava
- Today
- Total
이동식 저장소
[Android Kotlin Fundamentals] Navigation path 본문
이 글은 Google의 Android Kotlin Fundamentals를 참고하여 작성되었습니다.
Fragment
간의 이동은 Navigation
을 이용하여 정의하는 것이 좋다. 물론 onClick
등의 콜백을 사용할 수도 있지만, Navigation
을 사용하면 조건부 이동, 이전 화면으로 돌아가는 등의 액션을 더 쉽게 관리할 수 있다.
Navigation 라이브러리 사용
Android navigation library를 사용하려면 module-level build.gradle
파일에 다음을 추가해야 한다.
implementation "androidx.navigation:navigation-fragment-ktx:2.3.3"
implementation "androidx.navigation:navigation-ui-ktx:2.3.3"
2021.02.02. 기준 최신 버전은 2.3.3이다.
기본 개념
Navigation destination은 fragment 또는 activity 등이 될 수 있다. Navigation graph를 통해 destination간의 이동 경로를 정의할 수 있다. 보통은 하나의 activity에서 여러 개의 fragment 사이를 이동한다.
- Navigation 타입의 리소스 파일을 만든다. 만들어진 파일은
res/navigation
폴더에 생성될 것이다. 이름은 대략navigation.xml
정도로 짓는다. navigation.xml
파일을 열어서 우측 위의 Design 탭을 클릭한다. 이곳에 destination 간의 이동 경로가 그려진다.- 이동 경로는
action
으로 표현된다.action
마다 부여된ID
를 사용하여 코드에서 어떤action
을 따라 이동할 것인지 결정할 수 있다.
Fragment가 보여질 컨테이너를 Navigation host fragment라고 한다. 보통 NavHostFragment
라고 부르며 navigation graph의 호스트 역할을 한다.
NavHostFragment
는 graph 사이를 이동할 때 fragment를 자동으로 교체하며, fragment back stack을 관리한다.- 예를 들어 다음의 뷰를
activity_main.xml
에서NavHostFragment
로 사용할 수 있다.
<androidx.fragment.app.FragmentContainerView
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
...
/>
이동
버튼을 클릭했을 때 다른 뷰로 이동하고 싶다면 다음과 같이 하면 된다.
- 버튼의
onClick
리스너 안에서findNavController().navigate()
를 호출한다. - 이때
action
의ID
를 매개변수로 넘겨줘야 한다.
하나의 뷰에서 출발하는 여러 개의 action
을 정의하고, 조건에 따라 액션을 수행할 수도 있다. 예를 들어 "다음" 버튼을 클릭하면 다음 화면으로 넘어가고, "힌트" 버튼을 클릭하면 힌트 화면으로 넘어갈 수도 있다. 어쨌든 findNavController().navigate()
만 잘 기억해 놓으면 된다.
뒤로 가기?
휴대폰의 뒤로 가기 버튼을 누르면 (일반적으로) 이전에 보여진 화면으로 돌아갈 수 있다. 하지만 특정 상황에서는 메인 화면으로 바로 건너뛰어야 하는 경우도 있다.
-
navigation.xml
파일을 열어서 아무 action이나 하나 클릭해 보자. 오른쪽에 보면 Pop Behavior라는 메뉴가 있을 것이다. -
popUpTo 매개변수는 back stack에서 어느 destination까지 pop할지 를 지정하는 매개변수이다.
-
popUpToInclusive는 pop하는 과정에서 popUpTo에서 지정한 destination을 포함할지 결정하는 매개변수이다. true로 설정하면 popUpTo까지 pop되며, false로 설정하거나 비워 두면 popUpTo 바로 전까지 pop한다.
-
popUpTo
를 시작 fragment로 지정하고,popUpToInclusive
를 true로 지정하면 뒤로 가기 버튼을 눌렀을 때 앱이 종료된다.
또 다른 뒤로 가기
상단 바(app bar)의 왼쪽 위에는 다음과 같이 뒤로 가기 버튼을 넣을 수 있다. 영어로는 Up button이라고 부른다.
Navigation controller의 Navigation UI
라이브러리를 사용하면 app bar에 up button을 추가할 수 있다.
MainActivity.onCreate()
에서 다음과 같이NavigationUI.setUpActionBarWithNavController()
를 실행한다. 이름이 길지만 잘 외워 보자.
val navController = this.findNavController(R.id.navHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
onSupportNavigateUp()
을 다음과 같이 override한다.
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.navHostFragment)
return navController.navigateUp()
}
}
옵션 메뉴
반대로 App bar의 오른쪽 위에는 옵션 메뉴(대략 이런 버튼
) 가 존재한다. 옵션 메뉴를 선택했을 때 특정 Fragment로 이동하도록 코딩해 보자.
navigation.xml
에 fragment의 ID가 정의되어 있는지 확인한다.- 옵션 메뉴를 정의한다.
- Menu 리소스 파일을 만든다. 파일 이름은 대략
options_menu.xml
정도로 하겠다. 메뉴 파일은res/menu
폴더에 만들어진다. options_menu.xml
파일을 열어서 Menu Item을 하나 추가한다.- 추가한 menu item의 ID를 설정한다. Fragment로 이동하는 메뉴의 경우, menu item의 ID를
navigation.xml
에 정의된 Fragment의 ID와 동일하게 하자. 이러면 내부적으로onClick
메소드가 자동으로 작성된다. 꼭 이렇게 해야 하는 것은 아니지만, 같은 기능을 더 편리하게 구현할 수 있다.
- Menu 리소스 파일을 만든다. 파일 이름은 대략
- App bar에 옵션 메뉴를 추가한다.
- 옵션 메뉴가 보여질 Fragment나 Activity의
onCreateView()
안에서setHasOptionsMenu(true)
를 호출한다. onCreateOptionsMenu()
를 다음과 같이 override한다.
- 옵션 메뉴가 보여질 Fragment나 Activity의
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.options_menu, menu)
}
- 옵션 메뉴의 클릭 이벤트를 정의한다.
onOptionsItemSelected()
를 다음과 같이 override하여 옵션 메뉴가 클릭되었을 때 적절한 navigation action을 따라가도록 한다.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return NavigationUI.
onNavDestinationSelected(item, requireView().findNavController())
|| super.onOptionsItemSelected(item)
}
Navigation drawer
요즘은 Up button이나 옵션 메뉴보다 drawer가 더 많이 보이는 것 같다. Navigation drawer는 마치 서랍처럼 가장자리에서 슥 하고 나오는 뷰를 의미한다.
Drawer는 상단의 header, 하단의 menu로 구성된다. Header는 별도의 레이아웃을 지정하면 되고, menu는 메뉴 리소스 파일을 만들어야 한다.
Navigation drawer는 다음의 두 가지 방법으로 열 수 있다.
- (보통) 왼쪽 가장자리에서 오른쪽으로 스와이프하여 연다.
- App bar의 drawer 버튼()을 눌러서 연다.
개발자로서 우리는 두 가지 방법을 모두 구현해야 한다. 다음과 같이 해 보자.
- Module-level
build.gradle
에 다음과 같이 의존성을 추가한다. 작성일 기준 최신 버전은 1.2.1이다.
implementation "com.google.android.material:material:1.2.1"
- Navigation graph의 각 Fragment에
ID
를 지정한다. Drawer에 넣을 Fragment는 모두 graph에 추가되어 있어야 한다. Drawer도 Navigation으로 작동하기 때문이다. - Drawer에 넣을 menu를 만들자.
- Menu 리소스 파일을 만든다. 나는
nav_drawer_menu.xml
파일을 만들었다. - 만들어진 메뉴 파일에 Menu Item을 넣는다. 옵션 메뉴에서처럼 각 Item의 ID를
navigation.xml
에 있는 Fragment의 ID와 동일하게 하면onClick
을 따로 만들지 않아도 된다. 메뉴를 만들 때 공통적으로 적용되는 기능이므로 잘 기억해 두자.
- Menu 리소스 파일을 만든다. 나는
- Navigation Drawer를 추가하자.
- Navigation host Fragment가 포함된 레이아웃을 수정해야 한다. 최상위 레이아웃을
DrawerLayout
으로 감싸주자. 정확한 이름은androidx.drawerlayout.widget.DrawerLayout
이다. DrawerLayout
바로 밑에NavigationView
를 추가한다. 정확한 이름은com.google.android.material.navigation.NavigationView
이다.
- Navigation host Fragment가 포함된 레이아웃을 수정해야 한다. 최상위 레이아웃을
- Drawer에 navigation controller를 연결한다.
- Navigation controller를 생성한 Activity로 가서, 다음과 같이
onCreate()
안에NavigationUI.setUpWithNavController()
를 호출한다.
- Navigation controller를 생성한 Activity로 가서, 다음과 같이
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
NavigationUI.setupWithNavController(binding.navView, navController)
- App bar에 drawer button을 추가한다.
onCreate()
에서 다음과 같이NavigationUI.setupActionBarWithNavController()
를 호출한다.
NavigationUI.setupActionBarWithNavController(
this, navController, binding.drawerLayout)
- 사실 drawer 버튼의 자리는 위에서 추가했던 up button의 자리와 동일하다.
onSupportNavigateUp()
을 다음과 같이 수정하여 두 버튼이 자연스럽게 동작하도록 한다.
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.navHostFragment)
return NavigationUI.navigateUp(navController, binding.drawerLayout)
}
'Primary > Android' 카테고리의 다른 글
[Android] Transformations.map이 동작하지 않을 때 (0) | 2021.02.17 |
---|---|
[Android Kotlin Fundamentals] Lifecycle (2) | 2021.02.03 |
Inflate()에서 매개변수 attachToParent의 의미 (1) | 2020.12.22 |
ViewBinding 사용 시 레이아웃 크기가 wrap_content로 고정되는 문제 (0) | 2020.12.21 |
ViewBinding을 사용할 때 layout_margin이 적용되지 않는 오류 (0) | 2020.12.13 |