지난 글 Android Jetpack Navigation으로 화면 전환 구현에 이어 Navigation Drawer를 구현하는 방법을 알아보겠습니다. 지난 번에는 네이게이션 그래프로 순환 구조를 정의하였고, AppBar에 Label을 출력하도록 하였습니다.
네비게이션은 Drawer라는 기능을 지원하는데요. 이번에는 네비게이션을 이용하여 Drawer를 구현해보겠습니다.
간단히 샘플앱을 만들어보죠.
프로젝트 생성
Kotlin
과 Use AndroidX artifacts
를 선택해주세요.
(Android Studio 버전이 3.4 미만이라면 이 기능이 없습니다. 마이그레이션 기능을 이용하거나 직접 의존성을 변경해줘야 합니다)
AndroidX로 프로젝트를 생성하지 않았다면, 메뉴에서 [Refactor] -> [Migrate to AndroidX...]
를 누르시면 AndroidX를 사용하는 프로젝트로 마이그레이션이 됩니다.
Navigation을 사용하려면 앱 gradle에서 dependency를 추가해야 합니다.
dependencies {
....
implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha07'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha07'
implementation "com.google.android.material:material:1.0.0"
}
View 객체 생성
화면의 단위는 fragment입니다.
MainFragment
와 SubFragment
두개의 화면을 생성하고 Drawer에서 두개의 화면으로 진입할 수 있도록 구현하겠습니다.
MainFragment.kt
class MainFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_main, container, false)
return view
}
}
SubFragment.kt
class SubFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_sub, container, false)
return view
}
}
fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Main fragment"
android:textSize="24sp"/>
</FrameLayout>
fragment_sub.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Sub fragment"
android:textSize="24sp"/>
</FrameLayout>
Host 설정
Drawer에서 메뉴를 선택했을 때 MainActivity의 화면이 변경되어야 하기 때문에, MainActivity의 fragment를 Host로 설정하려고 합니다.
activity_main.xml
에서 CoordinatorLayout
를 사용하지 않고 DrawerLayout
로 변경해야 합니다.
그리고 fragment를 추가하고 호스트로 설정을 해줍니다.
모든 수정이 끝난 코드는 다음과 같습니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"/>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</androidx.drawerlayout.widget.DrawerLayout>
Navigation graph 정의
그래프는 navigation 태그 아래에 MainFragment
와 SubFragment
를 추가했습니다.
그리고 처음 보여지는 화면은 MainFragment로 하고 싶기 때문에 startDestination
는 MainFragment로 설정했습니다.
/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"
app:startDestination="@+id/main_fragment">
<fragment
android:id="@+id/main_fragment"
android:name="com.codechacha.navi.drawer.MainFragment"
android:label="Main fragment"
tools:layout="@layout/fragment_main" >
</fragment>
<fragment
android:id="@+id/sub_fragment"
android:name="com.codechacha.navi.drawer.SubFragment"
android:label="Sub fragment"
tools:layout="@layout/fragment_sub"/>
</navigation>
이제 빌드는 가능하겠네요. 실행해보면 MainFragment 화면이 보입니다. 하지만 Drawer가 구현되지 않아 SubFragment로 이동할 수 없네요.
Drawer 구현
Drawer를 구현하려면 activity_main.xml
에 NavigationView
를 정의해야 합니다.
NavigationView
를 추가한 activity_main.xml
의 코드는 다음과 같습니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"/>
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
style="@style/Widget.MaterialComponents.NavigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/menu_navigation"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</androidx.drawerlayout.widget.DrawerLayout>
Drawer(NavigationView)에서 보여지는 View로, nav_header
와 menu_navigation
도 만들어줘야 합니다.
nav_header.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="?attr/colorPrimaryDark"
android:gravity="bottom"
android:orientation="vertical"
android:padding="16dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:text="Codechacha"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
</FrameLayout>
menu_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/main_fragment"
android:title="Main fragment"/>
<item
android:id="@+id/sub_fragment"
android:title="Sub fragment"/>
</menu>
이제 xml은 모두 만들었고 MainActivity.kt
에 코드를 조금 추가해주면 됩니다.
지난 글에서 AppBarConfiguration
를 사용해보았는데요.
drawer와 함께 사용하려면 인자로 drawer_layout도 전달해야 합니다.
navController = Navigation.findNavController(this, R.id.nav_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
NavController와 AppBarConfiguration를 생성하고 ActionBar에 등록을 해줍니다.
추가로 navigation_view
에 NavController를 설정해줍니다.
setSupportActionBar(toolbar)
setupActionBarWithNavController(navController, appBarConfiguration)
// Set up navigation menu
navigation_view.setupWithNavController(navController)
뒤로가기 버튼과 Back key를 눌렀을 때 이전 화면으로가기 위해 다음과 같은 코드를 추가해야 합니다.
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
완성된 MainActivity.kt
는 다음과 같습니다.
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = Navigation.findNavController(this, R.id.nav_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
// Set up ActionBar
setSupportActionBar(toolbar)
setupActionBarWithNavController(navController, appBarConfiguration)
// Set up navigation menu
navigation_view.setupWithNavController(navController)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
}
실행해보면 Drawer가 동작하고 화면전환이 되는 것을 볼 수 있습니다.
참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력