2022. 4. 19. 20:49ㆍAndroid/AAC
- 구성요소
- Navigation Graph : navigation 정보를 담은 xml 파일
- NavHost : Navigation Graph의 목적지를 보일 빈 컨테이너
- NavController : NavHost 안에서 navigation을 관리
- Navigation Graph 설계
1. Destination 추가
1-1. Fragment
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_graph"
app:startDestination="@id/simpleFragment">
<Fragment
android:id="@+id/simpleFragment"
android:label="@string/sampleFragmentTitle"
android:name="com.example.android.navigation.fragment.SimpleFragment" />
</navigation>
1-2. DialogFragment
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph">...
<dialog
android:id="@+id/my_dialog_fragment"
android:name="androidx.navigation.myapp.MyDialogFragment">
<argument
android:name="myarg"
android:defaultValue="@null" />
<action
android:id="@+id/myaction"
app:destination="@+id/another_destination" />
</dialog>
...
</navigation>
1-3. Activity : 기본적으로 Navigation Graph는 ActivityScope에서 유효하므로, Activity Destination 추가는 현재 그래프의 endpoint로 사용됨
- startActivity()와 같은 맥락
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_graph"
app:startDestination="@id/simpleFragment">
<activity
android:id="@+id/localDestinationActivity"
android:label="@string/localActivityTitle"
app:action="android.intent.action.VIEW"
app:dataPattern="https://example.com?userId={userId}"
app:targetPackage="${applicationId}">
<argument
android:name="userId"
app:argType="string" />
</activity>
</navigation>
- 암시적 Intent와 같은 맥락 : 찾지 못한 경우, ActivityNotFoundException 발생
-> 암시적 Intent와 같은 맥락으로 코드에서 발생시킬 경우 아래와 같이 호출할 수 있음
navController.navigate(R.id.localDestinationActivity, bundleOf("userId" to "someUser") )
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_graph"
app:startDestination="@id/simpleFragment">
<activity
android:id="@+id/sampleActivityDestination"
android:name="com.example.android.navigation.activity.DestinationActivity"
android:label="@string/sampleActivityTitle" />
</navigation>
2. Nested Navigation Graph 구현 : Navigation Graph 안에서 다른 Navigation Graph를 취하는 경우로, <navigation>을 통해 직접 기술하거나 <include>를 통해 외부에서 정의한 Navigation Graph xml 파일을 포함시킬수도 있음
2-1. Global Action 정의 : 모든 Destination에서 사용가능한 action을 재사용할 수 있도록 정의


<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/in_game_nav_graph"
app:startDestination="@id/in_game"> <!-- Action back to destination which launched into this in_game_nav_graph-->
<action
android:id="@+id/action_pop_out_of_game"
app:popUpTo="@id/in_game_nav_graph"
app:popUpToInclusive="true" />
<fragment
android:id="@+id/in_game"
android:name="com.example.android.gamemodule.InGame"
android:label="Game">
<action
android:id="@+id/action_in_game_to_resultsWinner"
app:destination="@id/results_winner" />
<action
android:id="@+id/action_in_game_to_gameOver"
app:destination="@id/game_over" />
</fragment>
<fragment
android:id="@+id/results_winner"
android:name="com.example.android.gamemodule.ResultsWinner" />
<fragment
android:id="@+id/game_over"
android:name="com.example.android.gamemodule.GameOver"
tools:layout="@layout/fragment_game_over"
android:label="fragment_game_over" />
</navigation>
<!-- (root) nav_graph.xml -->
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/fragment">
<include app:graph="@navigation/included_graph" />
<fragment
android:id="@+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="@layout/fragment_blank">
<action
android:id="@+id/action_fragment_to_second_graph"
app:destination="@id/second_graph" />
</fragment>
...
</navigation>
- Navigation
1. using ID
view.findNavController().navigate(R.id.viewTransactionsAction)
findNavController().navigate(
R.id.action_fragmentOne_to_fragmentTwo,
null,
navOptions { // Use the Kotlin DSL for building NavOptions
anim {
enter = android.R.animator.fade_in,
exit = android.R.animator.fade_out
}
})
2. using Action : 높은 가독성과 전환 animation 적용 가능, compile-time safety를 제공 및 safe args 적용이 가능한 action이 권장됨
val action = SpecifyAmountFragmentDirections
.actionSpecifyAmountFragmentToConfirmationFragment(amount)
v.findNavController().navigate(action)
3. using Deeplink
findNavController().navigate(
deepLinkUri,
navOptions { // Use the Kotlin DSL for building NavOptions
anim {
enter = android.R.animator.fade_in,
exit = android.R.animator.fade_out
}
})
4. using DeeplinkRequest : deeplink로 이동하는 경우와 다르게 백스택이 유지됨, popUpTo+popUpToInclusive로 백스택 삭제도 가능
- popUpToInclusive : true - 해당 destination도 백스택에서 제거, false - 해당 destination까지 백스택 유지
- popUpToSaveState : true - 백스택 제거 시 state 저장, false - 백스택에서 제거만 진행
-> app:restoreSaveState="true"로 해당 destination으로 이동 시 복원(popUpToSaveState가 true여야 의미가 있음)
val request = NavDeepLinkRequest.Builder
.fromUri("android-app://androidx.navigation.app/profile".toUri())
.build()
findNavController().navigate(request)
5. using Backstack : NavController.navigateUp() / NavController.popBackStack()
-> FloatingWindnow 인터페이스 상속한 dialog의 경우, 상속하지 않은 fragment나 activity로 navigate 시 모두 popBackStack됨
- savedStateHandler로 navigation 조건 처리하기



class LoginFragment : Fragment() {
companion object {
const val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL"
}
private val userViewModel: UserViewModel by activityViewModels()
private lateinit var savedStateHandle: SavedStateHandle
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
savedStateHandle = findNavController().previousBackStackEntry!!.savedStateHandle
savedStateHandle.set(LOGIN_SUCCESSFUL, false)
val usernameEditText = view.findViewById(R.id.username_edit_text)
val passwordEditText = view.findViewById(R.id.password_edit_text)
val loginButton = view.findViewById(R.id.login_button)
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
login(username, password)
}
}
fun login(username: String, password: String) {
userViewModel.login(username, password).observe(viewLifecycleOwner, Observer { result ->
if (result.success) {
savedStateHandle.set(LOGIN_SUCCESSFUL, true)
findNavController().popBackStack()
} else {
showErrorMessage()
}
})
}
fun showErrorMessage() {
// Display a snackbar error message
}
}

'Android > AAC' 카테고리의 다른 글
| Kotlin Flow (0) | 2022.03.25 |
|---|---|
| Kotlin Coroutine (0) | 2022.03.24 |
| Compose의 State (0) | 2022.03.03 |
| Compose 감잡기 (0) | 2022.02.24 |
| Jetpack - Compose (0) | 2021.10.28 |