본문 바로가기

[Android]/허접 Programming Tips

[Navigation] Android Jetpack Navigation + BottomNavigationView (2 / 2)

안녕하세요 허접샴푸입니다!

 

Navigation 관련 2편입니다.

 

이번 편에서는 NavigationExtensions에 대해서 알아보고 MainActivity.kt 코드를 작성하도록 하겠습니다.

긴 말 없이 바로 이어나가도록 하겠습니다.

 

 

[START]

일단 먼저 1편에서도 말씀드렸지만, Google Sample을 참고한 것이기 때문에, 자세한 구현은 저도 모르는 부분들이 있습니다.

그러나 설명할 수 있는 부분까지 설명하도록 하겠습니다.

참고 : https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample

 

(1) NavigationExtensions.kt 복사 붙이기

https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample

위에 있는 NavigationExtensions.kt를 그대로 가져와 저희 프로젝트에 집어넣습니다. 해당 코드는 뒤에 알아보도록 하겠습니다.

음 Navigation을 사용해보신 분이라면, 현재 Back Stack 관련 문제가 제대로 해결되지 않았습니다. 물론 NavigationExtensions.kt를 커스터마이징하여 잘 쓰고 있는 똑똑한 분들도 있겠지만 Google에서 공식적으로 이것이 답이다! 하고 내놓은 버전은 아직 없습니다. 그래서 저 NavigationExtensions.kt에서도 "multiple back stack을 지원하기 전까진 이 샘플이 해결방법이다"라고 주석으로 적어 놓았습니다. 

그래서 저 또한 주저하지 않고 바로 이놈을 씁니다!

 

(2) MainActivity.kt 작성 (이 코드 또한 위 Google Sample Github의 MainActivity와 동일합니다.)

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.navigation.NavController
import androidx.navigation.ui.setupActionBarWithNavController
import com.dj.testnavigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
class MainActivity : AppCompatActivity() {
private lateinit var bottomNavigationView: BottomNavigationView
private var currentNavController: LiveData<NavController>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(savedInstanceState == null){
setUpBottomNavigationBar()
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
setUpBottomNavigationBar()
}
private fun setUpBottomNavigationBar(){
bottomNavigationView = findViewById(R.id.bottom_nav)
val navGraphIds = listOf(R.navigation.navigation_home, R.navigation.navigation_hot, R.navigation.navigation_list)
val controller = bottomNavigationView.setupWithNavController(
navGraphIds, supportFragmentManager, R.id.nav_host_container, intent
)
controller.observe(this, Observer {navController->
setupActionBarWithNavController(navController)
})
currentNavController = controller
}
override fun onSupportNavigateUp(): Boolean {
return currentNavController?.value?.navigateUp() ?: false
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

- onCreate에서 savedInstsanceState이 null이면 setUpBottomNavigationBar()을 합니다. savedInstanceState이 null이라는 뜻은 저장되어있는 번들 값이 없다는 뜻이며 앱이 처음 실행되었음을 의미합니다. 즉 처음 실행되었으니 바텀 내비게이션 바를 세팅해라는 뜻이네요.

- onRestoreInstanceState은 onStart() 다음에 호출되는 override 함수입니다. 화면이 회전되게 되면 해당 Activity는 onCreate부터 다시 생명주기 함수들을 호출합니다. 이때 savedInstanceState은 null이 아니게 되며, onRestoreInstanceState이 호출되게 됩니다. 

- onRestoreInstanceState이 되면 기존에 있었던 BottomNavigationView의 instance state과 selectedItemId가 복구되어 setupBottomNavigationBar()을 다시 호출하여 selectedItemId에 맞는 fragment를 화면에 보여주게 됩니다.

(관련 내용은 아래 그림 참조)

출처: https://stackoverflow.com/questions/4853505/android-how-to-disable-feature-no-title/4853734#4853734

- setupWithNavController을 통해 bottomNavigationView를 앞 1편에서 만든 Navigation graph들과 연결시켜줍니다.

- setupActionBarWithNavController는 현재 보이고 있는 Fragment의 title을 actionbar에 표시해주거나, 뒤로 가기 버튼 등 Navigatino Controller와  Actionbar을 자동적으로 이어줍니다. Observer을 붙여서, navigationController에 변화가 있을 때마다 ActionBar을 설정하도록 해줍니다.

 

- onSupportNavigateUp은 Actionbar에서 뒤로 가기 버튼을 눌렀을 때 호출되며, 뒤로 갈 수 있으면 navigateUp() boolean 값을 반환하며 true일 것이며, 뒤로 갈 수 없으면 null일 것이며 false를 반환합니다.

 

(결과 화면)

 

여기서 잠깐!

위 결과 화면에서 Home 화면을 보면, "NAVIGATE TO HOME DETAIL FRAGMENT" 버튼이 보이시죠?

네 맞습니다. 해당 버튼을 누르면 detail fragment로 navigate 하는 방법을 알려드리겠습니다.

 

(1) 먼저 HomeDetailFragment와 fragment_home_detail.xml 파일을 생성합니다.

import androidx.fragment.app.Fragment
import com.dj.testnavigation.R
class HomeDetailFragment: Fragment(R.layout.fragment_home_detail){
}
<?xml version="1.0" encoding="utf-8"?>
<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"
>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Home Detail"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

(2) res > navigation > navigation_home.xml으로 가서 위에서 만든 fragment를 추가합니다.

 

(3) 아래와 같이 HomeFragment에서 HomeDetailFragment로 화살표를 이어줍니다.

화살표를 이으면, 오른쪽에 "id"에 "action_homeFragment_to_homeDetailFragment"라고 적혀 있는 것을 확인할 수 있습니다. id는 자신이 원하는 형태로 바꾸어도 되며 꼭 저 형식일 필요는 없습니다.

예로, action_navigate 이런 식으로 바꾸어도 무방합니다.

 

(4) HomeFragment.kt 버튼 클릭 리스너 추가 및 navigate!

package com.dj.testnavigation.ui.home
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.dj.testnavigation.R
import kotlinx.android.synthetic.main.fragment_home.*
class HomeFragment : Fragment(R.layout.fragment_home) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navigate_to_home_detail_button.setOnClickListener {
findNavController().navigate(R.id.action_homeFragment_to_homeDetailFragment)
}
}
}
view raw HomeFragment.kt hosted with ❤ by GitHub

- navigate_to_home_detail_button에 clickListener을 달아 줍니다.

- 위에서 말한 "id"를 여기서 이제 사용하면 끝입니다! 

findNavController().navigate(R.id.action_homeFragment_to_homeDetailFragment)

- findNavController()은 현재 Fragment의 NavController을 찾으며, navigate은 navigation graph에서 해당 id의 action을 찾아 그 action의 목적지로 navigate 해줍니다. 

 

(결과 화면)

 

 

[Github]

https://github.com/DJDrama/Playground/tree/master/TestNavigation

 

DJDrama/Playground

Contribute to DJDrama/Playground development by creating an account on GitHub.

github.com

 

이렇게 2편에 걸쳐서 Android Navigation, BottomNavigationView에 대해서 아주 약하게 맛만 보았습니다.

맛을 본 이유는 무엇일까요?

배달의 민족 App이 BottomNavigationView로 이루어져 있기 때문에 따라 만들기 위해서 잠시 짬을 내어 이렇게 작성해보았습니다.

그럼 배달의 민족 App 따라 만들 때 더 자세히 다루어보도록 하겠습니다. 뿅~!