오늘도 더 나은 코드를 작성하였습니까?

NavigationUI와 AppBar 본문

Android Jetpack Architecture/NavigationUI

NavigationUI와 AppBar

hik14 2021. 11. 24. 14:44

navigation component 에는 NavigationUI 클래스가 포함되어 있습니다.

이 클래스에는 Appbar, Drawer 및 BottomNavigation으로 navigation을 관리하는 정적 메서드가 포함되어 있습니다

 

 

Appbar

NavigationUI에는 사용자가 앱을 navigation할 때 상단 앱 바의 콘텐츠 를 자동으로 업데이트하는 메서드가 포함되어 있습니다.

예를 들어 NavigationUI는 탐색 그래프의 destination label 속성 을 사용하여 상단 앱 바의 제목을 최신 상태로 유지합니다.

<navigation>
    <fragment ...
              android:label="Page title">
      ...
    </fragment>
</navigation>

 

상단 앱 바 구현과 함께 NavigationUI를 사용할 때 라벨의 {argName} 형식을 사용하여 대상에 제공된 argument에서 destination에 첨부된 라벨을 자동으로 채울 수 있습니다.

binding.button.setOnClickListener {
   val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment("두번째 프래그먼트")
   findNavController().navigate(action)
}
<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/navi_graph"
    app:startDestination="@id/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.navigation.FirstFragment"
        android:label="첫번째 프래그먼트"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>

    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.navigation.SecondFragment"
        android:label="{argName}"
        tools:layout="@layout/fragment_second" >
        <action
            android:id="@+id/action_secondFragment_to_thirdFragment"
            app:destination="@id/thirdFragment" />
        <argument
            android:name="argName"
            app:argType="string"/>
    </fragment>

</navigation>

 

NavigationUI는 다음과 같은 상단 Appbar 유형을 지원합니다.

 

  • Toolbar
  • CollapsingToolbarLayout
  • ActionBar

*Toolbar를 setSupportActionBar()에 인수로 전달하면 ActionBar는 그 Toolbar의 전체 소유권을 가정하며 이 함수를 호출한 후에는 Toolbar API를 사용하면 안 됩니다. ActionBar 지원을 사용하여 ActionBar를 NavController에 연결 해야 한다. 

 

AppBarConfiguration

NavigationUI는 AppBarConfiguration 객체를 사용하여 앱 표시 영역의 왼쪽 상단 모서리에 있는 탐색 버튼의 동작을 관리합니다.

 

navi button 의 동작은 사용자가 최상위 수준 대상에 있는지 여부에 따라 달라집니다

 

최상위 수준 대상은 계층 구조의 관련 대상 집합에 있는 루트 또는 최고 수준 대상입니다.

최상위 수준 대상은 이보다 상위 수준의 대상이 없기 때문에 상단 앱 바에 '위로' 버튼을 표시하지 않습니다.

기본적으로 앱의 시작 destination(home)은 유일한 최상위 수준 대상입니다.

 

 사용자가 최상위 수준 대상에 있을 때 destination이 DrawerLayout을 사용하면 탐색 버튼은 창 아이콘 이 됩니다.

destination이 DrawerLayout을 사용하지 않으면 탐색 버튼이 숨겨집니다.

사용자가 다른 destination에 있을 때 탐색 버튼은 upButton 으로 표시됩니다. 시작 destination만 최상위 수준 대상으로 사용하여 탐색 버튼을 구성하려면 다음과 같이 AppBarConfiguration 객체를 만들어 해당하는 탐색 그래프에 전달합니다.

val appBarConfiguration = AppBarConfiguration(navController.graph)

 

기본 시작 destination을 사용하는 대신 여러 최상위 수준 대상을 정의해야 할 수도 있습니다.

 

이와 관련한 일반적인 사용 사례는 BottomNavigationView를 사용하는 것,  서로 계층적으로 관련이 없으며 각각 고유한 관련 대상 집합을 포함할 수 있는 동위 화면이 있을 수 있습니다.

 

이 같은 경우에는 destination ID set 을 생성자에 대신 전달할 수 있습니다.

val appBarConfiguration = AppBarConfiguration(setOf(R.id.main, R.id.profile))

 

1.  Toolbar를 NavigationUI 로 포함하기

- xml에 툴바 정의.

<LinearLayout>
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar" />
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        ... />
    ...
</LinearLayout>

- onCreate() 메서드에서 setupWithNavController() 를 호출

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val navHostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

    val appBarConfiguration = AppBarConfiguration(navController.graph)
    findViewById<Toolbar>(R.id.toolbar)
        .setupWithNavController(navController, appBarConfiguration)
}

 

Toolbar를 사용할 때는 navigation에서 navigation 버튼의 클릭 이벤트를 자동으로 처리하므로 onSupportNavigateUp()을 재정의할 필요가 없습니다.

 

모든 destination에서 navigation 버튼이 upButton으로 표시되도록 구성하려면, AppBarConfiguration을 빌드할 때 최상위 수준 대상에 대해 빈 상태의 대상 ID 집합을 전달합니다.

 

이는 모든 destination에서 second Activity 에 ToolbarupButton이 표시되어야 하는 경우 유용합니다.

그러면 사용자는 백 스택에 다른 destination이 없을 때 상위 Activity으로 돌아갈 수 있습니다.

 

즉, 1개의 activity와 다수의 fragment를 사용하는 경우가 아닌  여러개의 activity가  각각 fragment를 사용하는 경우 사용한다

 

setFallbackOnNavigateUpListener()를 사용하여 navigateUp()이 그 외에 아무런 동작도 하지 않을 때의 대체 동작을 제어할 수 있습니다.

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    val appBarConfiguration = AppBarConfiguration(
        topLevelDestinationIds = setOf(),
        fallbackOnNavigateUpListener = ::onSupportNavigateUp
    )
    findViewById<Toolbar>(R.id.toolbar)
        .setupWithNavController(navController, appBarConfiguration)
}

2.  CollapsingToolbarLayout를 NavigationUI 로 포함하기

- CollapsingToolbarLayout 정의하기. 

<LinearLayout>
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/tall_toolbar_height">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="top"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"/>
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        ... />
    ...
</LinearLayout>

- onCreate() 메서드에서 setupWithNavController() 를 호출

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
    val toolbar = findViewById<Toolbar>(R.id.toolbar)
    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    val appBarConfiguration = AppBarConfiguration(navController.graph)
    layout.setupWithNavController(toolbar, navController, appBarConfiguration)
}

 layout.setupWithNavController(toolbar, navController, appBarConfiguration)

*toolbar의 상위인 collapsingToolbarLayout 에 설정한다. 

 

3.  Actionbar NavigationUI 로 포함하기

- setupActionBarWithNavController() 설정

 

AppBarConfiguration onSupportNavigateUp()을 재정의할 때도 사용하므로 onCreate() 외부에 선언해야 합니다.

private lateinit var appBarConfiguration: AppBarConfiguration

...

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

- onSupportNavigateUp()을 재정의하여 상단 탐색을 처리

override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.nav_host_fragment)
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

 

 

app bar variations 지원.

Activity에 상단 Appbar를 추가하는 것은 앱 바의 레이아웃이 앱의 각 destination에서 유사할 때는 상관이 없다, 하지만, 상단 Appbardestination 전반에서 상당히 많이 변경되면 상단 Appbar를  Activity에서 삭제하고 대신 각 destination fragment에서 정의하는 것이 좋다.

 

destination 중 하나는 표준 Toolbar를 사용하지만,

다른 destination AppBarLayout을 사용하여 탭이 있는 더 복잡한 앱 바를 만들 수 있습니다.

 

1번 fragment 

<LinearLayout>
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        ... />
    ...
</LinearLayout>

2번 fragment 

<LinearLayout>
    <com.google.android.material.appbar.AppBarLayout
        ... />

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            ... />

        <com.google.android.material.tabs.TabLayout
            ... />

    </com.google.android.material.appbar.AppBarLayout>
    ...
</LinearLayout>

 

navigation component 로직은 activity 에서 초기화하는 대신 각각의 프래그먼트의 onViewCreated() 메서드 내에서 setupWithNavController()를 호출해야 한다는 점을 제외하면 이 두 프래그먼트 모두에서 동일합니다.

 

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val navController = findNavController()
    val appBarConfiguration = AppBarConfiguration(navController.graph)

    view.findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)
}

프래그먼트 전환이 설정되었을 때 상단 앱 바를 대상 프래그먼트 레이아웃에 배치하면 프래그먼트가 전환하는 동안에 앱 바가 나머지 레이아웃과 함께 애니메이션 처리됩니다.

'Android Jetpack Architecture > NavigationUI' 카테고리의 다른 글

BottomNavigation과 NavigationUI  (0) 2021.11.26
ActionBar vs ToolBar  (0) 2021.11.23