[Android Jetpack] AAC Navigation Component -3. NavigationUI & Animation/Transition
아래의 AAC Navigation에 대한글을 먼저 보고 오세요!
이번 글에는 AAC Navigation에서 지원하는 NavigationUI 와 Animiation ,Transition에 대해 소개할 예정이다.
Navigation libraries Structure
Navigation은 4개의 모듈로 나눠진다.
navigation-common은 내부적으로 동작하는 부분이고,
navigation-runtime은 NavController에 해당한다.
navigation-fragment는 NavHostFragment, Destinations(Fragments)에 해당하고,
navigation-UI는 bottom-navigation, appbar 등의 UI 동작을 지원해주는 부분이다.
NavigationUI
NavigationUI에 대해 간단하게 알아보자
NavigationUI를 사용하여 아래의 Menu, Drawers, toolbar등의 UI들의 Action을 직접 구현하지 않고,
간단하게 연결하여 사용이 가능하다.
- Toolbar
- CollapsingToolbarLayout
- ActionBar
- DrawerLayout
- BottomNavigationView
먼저 xml에 navigation의 컴포넌트를 사용하고
각 컴포넌트의 NavController와 Menu를 연결해주면 된다.
주의해야할점은 Menu의 <Item>의 id와 NavGraph의 Destination(<fragment>)의 id가 같아야된다.
다음은 bottomNavigation을 구현해보는 예제이다.
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu"/>
먼저 xml에 BottomNavigation 컴포넌트를 추가한다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/dashboard"
android:title="@string/fragment_dashboard"/>
<item
android:id="@+id/home_list"
android:title="@string/fragment_home_list"/>
<item
android:id="@+id/mypageFragment"
android:title="@string/fragment_mypage"/>
</menu>
menu 리소스를 추가한다.
( 이때 NavGraph(xml)의 Destination Fragment 의 Id들과 매핑되기때문에 일치시켜주어야한다.)
binding.navView.setupWithNavController(navController)
이제 Activity내에서 위의 navController을 setUp해주면 된다.
구현은 간단하지만, 빌드해보면 첫번째 탭외에 다른 탭으로 이동시마다 backstack이 쌓이는 현상을 볼수있다.
BackButton이나 , appbar사용을 위한 backstack사용에 대한 추가적인 작업이 필요하다.
AppBarConfiguration
이러한 작업을 위해 AppBarConfiguration를 지원해준다.
Tab의 경우나, BottomNavigation등의 경우는 각 탭들 모두 최상단의 Destination이기 때문에,
이를 지원하기 위해 Top-Level-Destination을 정의할 수 있다.
AppBarConfiguration 에서 Top-Level-Destination을 지정해주어야한다.
var appBarConfiguration: AppBarConfiguration
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_list, R.id.dashboard, R.id.mypageFragment)
)
HomeList,Dashboard, MyPage가 Top-Level-Destination임을 선언 후,
아래 코드처럼 ActionBar에도 Top-Level-Destination 설정 하면,
Top-Level-Destincation이 아니라면 Actionbar에 뒤로 가기[<-] 가 표현된다 ( Up Button)
setupActionBarWithNavController(host.navController, appBarConfiguration)
onSupportNavigateUp에 appBarConfiguration Top-Level-Destination설정이 포함한 정보를 추가해주면,
Up-Button 의 이벤트에도 Top-Level-Destination설정이 가능하다.
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_container).navigateUp(appBarConfiguration)
}
※ Navigation 에서 Up-Button 과 Back Button은 동일한 작업이 되어야하지만,
( BottomNavigation 탭에서는 Multi - Start Point - Destination을 따로 지원하지 않기 때문에 ,,,)
Top-Level-Destination에서도, Back Button 이벤트 발생시,
StartPoint 외의 Destination(Fragment)에서 BackStack처리가 된다.
기본적으로 Navigation 기본원칙(Fixed Start Destination)으로 구현되어 있어,
appBar나 Up-Button의 경우는 Top-Level-Destination은 지원하지만 Back-Button의경우는
아직까진 Navigation에서 multiple start destinations을 지원하지 않는 것 같다.
아래는 조금이 편법을 사용하여, TopLevel 체크를 하였다.
override fun onBackPressed() {
if(appBarConfiguration.topLevelDestinations.contains( host.navController.currentDestination?.id)){
if (System.currentTimeMillis() - Back_Key_Before_Time < 2000) {
finish()
} else {
Toast.makeText(applicationContext, "한번더 누르면 꺼집니다.", Toast.LENGTH_SHORT).show()
Back_Key_Before_Time = System.currentTimeMillis()
}
return;
}
super.onBackPressed()
}
- 추 후엔 back 버튼에도 Top-Level-Destination을 기준으로 multiple start destinations을 지원해 주지 않을까 싶다.
추가
또, Single Activity 기반이기 때문에 navHostFragment가 아닌 범위 ( Appbar, BottomNavigation) 등의 영역은
Navigation에서 따로 지원해주지 않기 때문에, 수동으로 처리해주어야한다.
( 사실 Appbar의 경우는 Up-Button[<-]을 지원해주긴한다.)
(예를 들어 BottomNavigation은 각 Tab 화면 들의 Detail화면에서는 보여지지 않아야할때,
Top-Level-level의 Fragment 에서만 하단 BottomNavigation 탭이 보여야할때 수동으로 처리해주어야한다 .
아래코드는 해당 작업의 예시이다.
host.navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.dashboard, R.id.home_list, R.id.mypageFragment -> showBotNav()
else -> hideBotNav()
}
}
navController에 ChangedListener을 달아서, Bottom Navigation이 보여야할지, 말아야할지 처리해주어야한다.
Animation
Navigation에서는 Action에 Animation기능을 제공한다.
- enterAnim : action 실행시, 이동할 Destination에 대한 애니메이션
- exitAnim : action을 실행할 때 현재 Destination에 대한 애니메이션
- popExitAnim : 이전 화면으로 돌아갈 때(Pop or Back) 종료되는 현재 Destincation 에 대한 애니메이션
- popEnterAnim : 이전 화면으로 돌아갈 때(Pop or Back) 이동할 BackStack의 Destination에 대한 애니메이션
Animation추가는 두가지 방법이 있다.
1. navigate 실행시, NavOptions 을 정의해서 사용하는방법
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
<!-- 사용 -->
navController.navigate(R.id.flow_step_one_dest, null, options)
2. NavGraph에 명시하여 사용하는 방법
<action
android:id="@+id/action_to_add"
app:destination="@id/addFragment"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/slide_out_down"/>
마찬가지로 navigate를 사용하여 Action 실행시, Animation이 실행된다.
Transition
Navigation 에서도 간단하게 Transition 구현이 가능하다
일단, 각 Component View( XML )에 같은 TransitionName을 정의해준다.
<ImageView
android:transitionName="test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/test"
android:layout_gravity="top"/>
어떤 TransitionName이 naming이든 상관은 없지만, 전,후 화면의 name은 같아야 실행된다.
val extras = FragmentNavigatorExtras(
binding.imgview to "test"
)
findNavController().navigate( HomeListFragmentDirections.actionToDetail(itemId), extras)
먼저 action을 실행시키는 화면에서 FragmentNavigationExtras에 Transition에 대한 정보를 담아서
navigate에 파라미터로 추가하여 실행한다.
sharedElementEnterTransition =
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
받는 화면에서 위의 코드만 작성하면 Transition이 실행된다.
샘플 코드
해당 예제 코드는 아래 깃허브에 구현하였습니다.
(네비게이션 외에도 클린아키텍쳐 스터디를 위해 오픈소스를 진행하고 있습니다. 많은 Star 와 많은 PR,Issue 참여해주세요! )
https://github.com/namjackson/Clean-Architecture-App
다음글
AAC Navigation Component -1. Navigation 소개, 구성 및 개여, BackStack 관리 ( 이전 글 )
AAC Navigation Component -2. SafeArgs & Deeplink & Action ( 이전 글 )
AAC Navigation Component -3. NavigationUI & Animation/Transition ( 현재 글 )
참고
https://medium.com/@fornewid/io19-jetpack-navigation-33da8811c9de