모든 Navigation 관련 정보가 하나의 중심 위치에 모여 있는 XML 리소스. 단순히 말해 사용자가 앱에서 갈 수 있는 모든 플로우를 보여주고 앱내의 Fragment를 한눈에 확인 할 수 있다.
Navigation Graph에서 대상을 표시하는 빈 컨테이너. 대상 구성요소에는 프래그먼트 대상을 표시하는 기본 NavHost 구현인 NavHostFragment가 포함된다.
NavHost에서 App Navigation을 관리하는 객체. NavController는 사용자가 앱 내에서 이동할 때 NavHost에서 대상 콘텐츠의 전환을 조종하는 역할을 한다.
Navigation Graph에서 특정 경로를 따라 이동할지, 특정 대상으로 직접 이동할지 NavController에게 전달하고 NavController가 NavHost에 적절한 대상을 표시해주는 방식!
Module단위 build.gradle 파일을 열어 plugins, android, dependencies에 아래 라인을 추가해주자!
plugins {
id 'kotlin-kapt'
}
android {
buildFeatures {
dataBinding true
}
}
dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:2.4.0")
implementation("androidx.navigation:navigation-ui-ktx:2.4.0")
}
navigation의 버전은 공식문서에서 확인한 후 안정된 버전으로 작성하는 것이 좋다. 현재 내가 글을 쓰는 시점에서는 2.4.0 버전이 권장된다.
이대로 생성하면 Fragment와 그와 연결된 레이아웃이 함께 만들어진다. Fragment 이름을 HomeFragment라고 명명하면 fragment_home.xml이 같이 생성되는 것!
res 폴더 아래에 navigation 디렉터리를 만들고, 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/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.heyhong.fragment.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/buildingFragment"
android:name="com.example.heyhong.fragment.BuildingFragment"
android:label="BuildingFragment"
tools:layout="@layout/fragment_building" />
<fragment
android:id="@+id/facilityFragment"
android:name="com.example.heyhong.fragment.FacilityFragment"
android:label="FacilityFragment"
tools:layout="@layout/fragment_facility" />
<fragment
android:id="@+id/noticeFragment"
android:name="com.example.heyhong.fragment.NoticeFragment"
android:label="NoticeFragment"
tools:layout="@layout/fragment_notice" />
</navigation>
(1) navigation
(2) fragment
res 폴더 아래에 menu 디렉터리를 만들고, nav_menu.xml 파일을 생성한다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/homeFragment"
android:icon="@drawable/ic_menu_home"
android:title="홈"/>
<item
android:id="@+id/buildingFragment"
android:icon="@drawable/ic_menu_building"
android:title="건물"/>
<item
android:id="@+id/facilityFragment"
android:icon="@drawable/ic_menu_facility"
android:title="시설"/>
<item
android:id="@+id/noticeFragment"
android:icon="@drawable/ic_menu_notice"
android:title="공지"/>
</menu>
menu_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/blue_1" android:state_checked="true"/>
<item android:color="@color/black" android:state_checked="false"/>
</selector>
아이템을 클릭한 상태와 클릭하지 않은 상태에 색을 다르게 주기 위한 파일이다.
android:state_checked="true"가 클릭한 상태, false가 클릭하지 않은 상태다.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_bar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/white"
app:menu="@menu/nav_menu"
app:itemBackground="@color/white"
app:itemIconTint="@drawable/menu_selector"
app:itemTextColor="@drawable/menu_selector"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/nav_host" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(1) fragment
android:name="androidx.navigation.fragment.NavHostFragment"
탐색 그래프에서 대상을 표시하는 빈 컨테이너인 NavHost 역할을 하도록 설정한다.
app:navGraph="@navigation/nav_graph"
NavHostFragment와 내비게이션 그래프를 연결시킨다.
app:defaultNavHost="true"
뒤로가기 버튼을 누르면 NavHostFragment(@navigation/navi_graph에서 app:startDestination으로 설정)로 돌아오게 설정한 것이다.
기본값은 false이고, 뒤로가기 버튼을 누르면 앱이 종료된다.
(2) BottomNavigationView
app:menu="@menu/nav_menu"
BottomNavigationView와 메뉴를 연결시킨다.
app:labelVisibilityMode="labeled"
아이콘 밑의 레이블을 보이게 설정한다. 숨기고 싶으면 unlabeled로 설정한다.
app:itemIconTint="@drawable/menu_selector" (아이콘)
app:itemTextColor="@drawable/menu_selector" (레이블)
아이콘과 레이블의 색상을 지정해주는 속성인데, 아이템을 클릭한 상태와 클릭하지 않은 상태에 색을 다르게 주기 위해 위에서 만들었던 selector를 연결해준다.
app:itemBackground="@color/white"
아이템을 클릭할 때 검은 원이 퍼져나가는 ripple효과를 제거하기 위한 속성이다.
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initBinding()
initNavigation()
}
private fun initBinding() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
private fun initNavigation() {
NavigationUI.setupWithNavController(binding.navBar, findNavController(R.id.nav_host))
}
}
NavController
NavigationUI.setupWithNavController
참고 문서
https://hayeon1549.tistory.com/2
https://jae-young.tistory.com/46
https://medium.com/harrythegreat/android-navigation-component-%EA%B0%9C%EB%85%90%EA%B3%BC-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-1-5ac6ac081643