Skip to content

Latest commit

ย 

History

History
341 lines (275 loc) ยท 12.4 KB

File metadata and controls

341 lines (275 loc) ยท 12.4 KB

Navigation Component

Index

Navigation Component๋ž€?

Navigating์€ ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•œ๋‹ค.
Android ๊ฐœ๋ฐœ์— ์žˆ์–ด์„œ ํ•„์ˆ˜์ ์ธ ๊ธฐ๋ณธ ์š”์†Œ์ผ ๊ฒƒ์ด๋‹ค.
Jetpack๊ณผ ํ•จ๊ป˜ ์†Œ๊ฐœ๋œ Navigation Component๋Š” ์ด๋Ÿฌํ•œ App ๋‚ด์˜ ํ™”๋ฉด์ด๋™ ํ๋ฆ„์„ ๊ทธ๋ž˜ํ”„๋กœ ์ง€์ •ํ•˜์—ฌ
๋งˆ์น˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค.

Navigation Component ์žฅ์ 

  • ์ผ๋ฐ˜์ ์ธ ์•ˆ๋“œ๋กœ์ด๋“œ ํ™”๋ฉด์ด๋™์— ๋Œ€ํ•œ ๋‹จ์ˆœํ•œ ์„ค์ •
    • ํ•˜๋‹จ ํƒ์ƒ‰ ๋“ฑ
  • ๋ฐฑ ์Šคํƒ ์ฒ˜๋ฆฌ ์œ„์ž„
  • ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ์œ„์ž„
  • ํ™”๋ฉด ๊ฐ„ Type Safe ํ•œ Arguments ์ „๋‹ฌ
  • Transition animation ์ฒ˜๋ฆฌ
  • ๊ฐ„๋‹จํ•œ ๋”ฅ ๋งํ‚น
  • ๋ชจ๋“  Navigation ์ •๋ณด๋ฅผ ๋ชจ์œผ๊ณ , ์‹œ๊ฐํ™”๋œ ๊ทธ๋ž˜ํ”„๋กœ ๋ณผ ์ˆ˜ ์žˆ์Œ

Navigation Component ํŠน์ง•

Navigaion Component์˜ ํŠน์ง•์ค‘ ํ•˜๋‚˜๋Š” ์„ธ ์ž๊ธฐ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด ์กฐํ™”๋กญ๊ฒŒ ์ž‘๋™๋œ๋‹ค๋Š” ๊ฒƒ

  • Navigation graph
  • NavHostFragment
  • NavController

Navigation graph

Navigation graph๋Š” ์ƒˆ๋กœ์šด Android resource ์œ ํ˜•์œผ๋กœ,
xml ํŒŒ์ผ ํ˜•ํƒœ๋กœ ํ™”๋ฉด ํƒ์ƒ‰์— ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ์ค‘์‹ฌํ™” ํ•œ๋‹ค.

image
๊ทธ๋ž˜ํ”„๋‚ด์—์„œ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ํ™”๋ฉด๋“ค์€ ๋ชฉ์ ์ง€(Destination)์ด๋ผ ๋ถˆ๋ฆฐ๋‹ค. Destination์„ ํด๋ฆญํ•˜๋ฉด ๋”ฅ๋งํฌ URL ๋“ฑ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋ž˜ํ”„๋‚ด์˜ ํ™”์‚ดํ‘œ๋Š” ์•ก์…˜์ด๋ผ ๋ถ€๋ฅธ๋‹ค. ์•ฑ์„ ํ†ตํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๊ฒฝ๋กœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
๊ทธ๋ž˜ํ”„ ๋‚ด์˜ ์•ก์…˜์„ ํด๋ฆญํ•˜๋ฉด arguments, transaction, animation, backstack ์กฐ์ • ๋“ฑ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

<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/home_dest">

    <!-- ...tags for fragments and activities here -->

</navigation>
  • <navigation>์€ ๋ชจ๋“  ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„์˜ ๋ฃจํŠธ ๋…ธ๋“œ์ด๋‹ค
  • <navigation>์—๋Š” <activity> ๋˜๋Š” <fragment> ์š”์†Œ๋กœ ํ‘œ์‹œ๋œ ๋Œ€์ƒ์ด ํ•˜๋‚˜ ์ด์ƒ ํฌํ•จ๋œ๋‹ค
  • app:startDestination์€ ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ์ฒ˜์Œ ์—ด ๋•Œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๋Š” ์†์„ฑ์ด๋‹ค

<navigation> ์•ˆ์˜ <fragment> ์š”์†Œ์— ๋Œ€ํ•˜์—ฌ ์‚ดํŽด๋ณด์ž

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.sample.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two_dest">
    </action>
</fragment>
  • android:id๋Š” ์ด XML ๋ฐ ์ฝ”๋“œ์˜ ๋‹ค๋ฅธ ์œ„์น˜์—์„œ ๋Œ€์ƒ์„ ์ฐธ์กฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ID๋ฅผ ์ •์˜ํ•œ๋‹ค
  • android:name์€ ๋Œ€์ƒ์œผ๋กœ ์ด๋™ํ•  ๋•Œ ์ธ์Šคํ„ด์Šคํ™”ํ•  ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ์ •๊ทœํ™”๋œ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์„ ์–ธํ•œ๋‹ค
  • tools:layout์€ ๊ทธ๋ž˜ํ”ฝ ํŽธ์ง‘๊ธฐ์—์„œ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ์„ ์ง€์ •ํ•œ๋‹ค

NavHostFragment

NavHostFragment๋Š” Fragment๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, NavHost ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.
NavHostFragment๋Š” ๋ณดํ†ต FragmentContainerView์™€ ์‚ฌ์šฉํ•˜๋ฉฐ, ํ™”๋ฉด์ด๋™์ด ๋ฐœ์ƒํ•˜๋„๋ก ๋ ˆ์ด์•„์›ƒ ๋‚ด์— ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜์—ญ์„ ์ œ๊ณตํ•œ๋‹ค. NavHostFragment๋Š” ๊ฐœ๋ณ„์ ์œผ๋กœ NavController๋ฅผ ๊ฐ€์ง„๋‹ค.

<androidx.fragment.app.FragmentContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_sample"
app:defaultNavHost="true" />
  • android:name ์†์„ฑ์€ NavHost ๊ตฌํ˜„์˜ ํด๋ž˜์Šค์ด๋ฆ„์„ ๋„ฃ๋Š”๋‹ค
  • app:navGraph ์†์„ฑ์€ NavHostFragment๋ฅผ ๊ทธ๋ž˜ํ”„์™€ ์—ฐ๊ฒฐํ•œ๋‹ค
  • app:defaultNavHost="true" ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด NavHostFragment๊ฐ€ ์‹œ์Šคํ…œ ๋’ค๋กœ ๋ฒ„ํŠผ์„ ๊ฐ€๋กœ์ฑˆ๋‹ค
    • ํ•˜๋‚˜์˜ NavHost๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค
    • ๋™์ผํ•œ ๋ ˆ์ด์•„์›ƒ์— ์—ฌ๋Ÿฌ ํ˜ธ์ŠคํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•œ ํ˜ธ์ŠคํŠธ๋งŒ ๊ธฐ๋ณธ NavHost๋กœ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค

NavController

NavController๋Š” ํ™”๋ฉด์ด ์ด๋™๋˜๋Š” Navigation์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. NavController๋Š” Graph๋‚ด์— ์ •์˜๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Kotlin์ฝ”๋“œ ๋‚ด์—์„œ ํ™”๋ฉด ์ด๋™์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

findNavController().navigate(R.id.win_action)

Navigation UI

  • Options Menus
  • Bottom Navigation
  • Navigation View
  • Navigation Drawer
  • ActionBar
  • Toolbar
  • Collapsing ToolBar

SafeArgs

Navigation Component์—๋Š” ๋Œ€์ƒ ๋ฐ ์ž‘์—…์— ๋Œ€ํ•ด ์ง€์ •๋œ ์ธ์ˆ˜์— ๋Œ€ํ•œ ์œ ํ˜• ์•ˆ์ „ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ๊ฐœ์ฒด ๋ฐ ๋นŒ๋” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” safe args ๋ผ๋Š” Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ๋‹ค.

build.gradle(:project)

dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
    //...
    }

build.gradle(:app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'androidx.navigation.safeargs.kotlin'

android { 
   //...
}

NavGraph์—์„œ argument ์„ค์ •ํ•˜๊ธฐ

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.sample.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        android:name="flowStepNumber"
        app:argType="integer"
        android:defaultValue="1"/>

    <action...>
    </action>
</fragment>

/app/build/generated/debug/com/sample/navigation/ ๊ฒฝ๋กœ์— Args ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
image

FlowStepFragment ํ™”๋ฉด์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋„˜๊ฒจ๋ฐ›์€ args๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

val safeArgs: FlowStepFragmentArgs by navArgs()

FlowStepFragmentํ™”๋ฉด์œผ๋กœ args๋ฅผ ๋„˜๊ธธ ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
    val flowStepNumberArg = 1
    val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
    findNavController().navigate(action)
}

Destination์œผ๋กœ Deeplinking

Navigation Component๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋”ฅ ๋งํฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์˜ ํ•œ ๊ฐ€์ง€ ์ด์ ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ ์œ„์ ฏ, ์•Œ๋ฆผ ๋˜๋Š” ์›น ๋งํฌ์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ์ง„์ž…์ ์—์„œ ์ ์ ˆํ•œ ๋ฐฑ ์Šคํƒ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ๋Œ€์ƒ์—์„œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์œ„์ ฏ์—์„œ ๋”ฅ๋งํฌ๋ฅผ ํ†ตํ•ด Destination์œผ๋กœ ์ด๋™

DeepLinkAppWidgetProvider.kt

class DeepLinkAppWidgetProvider : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        val remoteViews = RemoteViews(
            context.packageName,
            R.layout.deep_link_appwidget
        )

        val args = Bundle()
        args.putString("myarg", "From Widget")
        val pendingIntent = NavDeepLinkBuilder(context)
                .setGraph(R.navigation.mobile_navigation)
                .setDestination(R.id.deeplink_dest)
                .setArguments(args)
                .createPendingIntent()

        remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)
        appWidgetManager.updateAppWidget(appWidgetIds, remoteViews)
    }
}
  • setGraph ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„๋ฅผ ํฌํ•จํ•œ๋‹ค
  • setDestination ๋งํฌ๊ฐ€ ์–ด๋””๋กœ ์ด๋™ํ•˜๋Š”์ง€ ์ง€์ •ํ•œ๋‹ค
  • setArguments ๋”ฅ ๋งํฌ์— ์ „๋‹ฌํ•˜๋ ค๋Š” ๋ชจ๋“  ์ธ์ˆ˜๋ฅผ ํฌํ•จํ•œ๋‹ค

AndroidManifest.xml

<receiver android:name=".DeepLinkAppWidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/deep_link_appwidget_info" />
        </receiver>

nav_graph.xml

<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/home_dest">
    
    ...

๋”ฅ ๋งํฌ๋ฅผ ํ†ตํ•ด ์ง„์ž…ํ•œ๊ฒฝ์šฐ Desination์œผ๋กœ app:startDestination ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฑ์Šคํƒ์„ ํ†ตํ•ด ๋Œ์•„๊ฐˆ ํ™”๋ฉด์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

WebLink

๋”ฅ ๋งํฌ์˜ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์šฉ๋„ ์ค‘ ํ•˜๋‚˜๋Š” ์›น ๋งํฌ๊ฐ€ ์•ฑ์—์„œ ํ™œ๋™์„ ์—ด ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ „ํ†ต์ ์œผ๋กœ ์•ˆ๋“œ๋กœ์ด๋“œ๋Š” manifestํŒŒ์ผ์— <intent-filter>์„ ์‚ฌ์šฉ ํ•˜๊ณ  URL์„ ์—ด๋ ค๋Š” ํ™œ๋™๊ณผ ์—ฐ๊ฒฐํ•˜์˜€๋‹ค.
Navigation Component๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ์ž‘์—…์ด ๋งค์šฐ ๊ฐ„๋‹จํ•ด์ง€๊ณ  ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„์˜ ๋Œ€์ƒ์— URL์„ ์ง์ ‘ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง์ ‘์ ์ธ URL ์ผ์น˜ ์™ธ์—๋„ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์ด ์ œ๊ณต๋œ๋‹ค.

  • scheme์ด ์—†๋Š” URI๋Š” http ๋ฐ https๋กœ ๊ฐ„์ฃผ๋œ๋‹ค
    • ์˜ˆ๋ฅผ ๋“ค์–ด, www.example.com์€ http://www.example.com , https://www.example.com ์™€ ์ผ์น˜ํ•œ๋‹ค
  • ํ˜•์‹์˜ ์ž๋ฆฌ ํ‘œ์‹œ์ž๋ฅผ ์‚ฌ์šฉ {placeholder_name}ํ•˜์—ฌ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฌธ์ž๋ฅผ ์ผ์น˜ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค
    • ์˜ˆ๋ฅผ ๋“ค์–ด, http://www.example.com/users/4๋Š” http://www.example.com/users/{id}
  • .* ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 0๊ฐœ ์ด์ƒ์˜ ๋ฌธ์ž๋ฅผ ์ผ์น˜ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค
  • NavController๋Š” ์ž๋™์œผ๋กœ ACTION_VIEW intent๋ฅผ ์ฒ˜๋ฆฌ ํ•˜๊ณ  ์ผ์น˜ํ•˜๋Š” ๋”ฅ ๋งํฌ๋ฅผ ์ฐพ๋Š”๋‹ค

๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ graph์— URI๊ธฐ๋ฐ˜ ๋”ฅ ๋งํฌ ์ถ”๊ฐ€ํ•˜๊ธฐ

nav_graph.xml์˜ ์ด๋™ํ•  ๋Œ€์ƒํ™”๋ฉด์— ์•„๋ž˜์™€๊ฐ™์ด <deeplink>๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

<fragment
    android:id="@+id/deeplink_dest"
    android:name="com.sample.navigation.DeepLinkFragment"
    android:label="@string/deeplink"
    tools:layout="@layout/deeplink_fragment">

    <argument
        android:name="myarg"
        android:defaultValue="Android!"/>

    <deepLink app:uri="www.example.com/{myarg}" />
</fragment>

AndroidManifest.xml <nav-graph>๋ฅผ manifest์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. manifest์— ์„ ์–ธ๋œ <nav-graph>๋Š” Graph์— ์ž‘์„ฑ๋œ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ intent-filter, action, category, data ๋“ฑ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <nav-graph android:value="@navigation/nav_graph" />
</activity>

์ƒ์„ฑ๋˜๋Š” ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค

 <activity
            android:name="com.example.android.codelabs.navigation.MainActivity">

            <intent-filter>

                <action
                    android:name="android.intent.action.MAIN" />

                <category
                    android:name="android.intent.category.DEFAULT" />

                <category
                    android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <intent-filter>

                <action
                    android:name="android.intent.action.VIEW" />

                <category
                    android:name="android.intent.category.DEFAULT" />

                <category
                    android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="http" />

                <data
                    android:scheme="https" />

                <data
                    android:host="www.example.com" />

                <data
                    android:pathPrefix="/" />
            </intent-filter>
        </activity>

์ฐธ๊ณ