美文网首页
Jetpack之Navigation

Jetpack之Navigation

作者: 948d307d6ecf | 来源:发表于2021-08-12 21:09 被阅读0次

    navigation是负责fragment之间切换的处理工具

    有三个核心点:

    • Navigation Graph (New XML Resource): 控制中心,包含所有页面及页面间的关系
    • NavHostFragment (Layout XML View):特殊的Fragment,是其他Fragment的“容器”,Navigation Graph中所有Fragmnet正是通过这个特殊的Fragment进行展示的
    • NavController (Kotlin / Java Object): 负责完成Navigation中具体的页面切换工作

    三者之间的关系:当需要切换页面时,使用NavController对象,告诉它想要去的Navigation Graph中的哪个Fragment,NavController对象会将目的地的Fragment展示在NavHostFragment中。

    一、基本使用

    1. 添加依赖

    dependencies {
        
        ......
        def nav_version = "2.3.5"
    
        // Kotlin
        implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
        implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    }
    

    2. 创建Navigation Graph

    res文件夹 -> New -> Android Resource File


    Create Navigation Graph

    3. 添加NavHostFragmnet

    在activity_main.xml文件中添加fragment作为NavHostFragment,其中有三点需要注意:

    • name必须是"androidx.navigation.fragment.NavHostFragment"
    • defaultNavHost设置为"true"
    • navGraph设置为"@navigation/nav_graph"
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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"
        tools:context=".MainActivity">
    
        <fragment
            android:id="@+id/nav_host_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" />
    
    </RelativeLayout>
    

    4. 创建Destination

    在nav_graph.xml页面里,依次点击加号、 Create new destination创建一个Destination,首次在此创建一个MainFragment作为StartDestination

    <?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"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.mynavigationdemo.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main" />
    </navigation>
    

    5. 页面切换

    在nav_graph.xml页面里,就像创建MainFragment一样创建一个SecondFragment

    <?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"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.mynavigationdemo.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main" />
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.example.mynavigationdemo.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" />
    </navigation>
    

    5.1 创建Action

    在nav_graph.xml页面的Design面板里,单击MainFragment,然后选中右侧的圆圈热点并拖拽指向右侧的SecondFragmnt


    选中热点并拖拽
    Action创建完成
    <?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"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.mynavigationdemo.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main">
            <action
                android:id="@+id/action_mainFragment_to_secondFragment"
                app:destination="@id/secondFragment" />
        </fragment>
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.example.mynavigationdemo.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" />
    </navigation>
    

    5.2 使用NavController导航到目的地

    在fragment_main.xml中添加一个按钮,然后在MainFragment文件中添加该按钮点击事件的监听器,当点击该按钮时通过NavController完成页面跳转

    class MainFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
                findNavController().navigate(R.id.action_mainFragment_to_secondFragment)
            }
        }
    }
    

    导航到目的地是使用 NavController 完成的,它是一个在 NavHost 中管理应用导航的对象。每个 NavHost 均有自己的相应NavController,NavController 提供了几种导航到目的地的不同方式,如需从 Fragment、Activity 或View中获取NavController,请使用以下某种方法:

    • Fragment.findNavController()
    • View.findNavController()
    • Activity.findNavController(viewId: Int)

    5.3 添加切换动画

    在nav_graph.xml页面的Design面板里,点击MainFragment指向SecondFragment的箭头(即页面跳转Action),然后在Attributes面板的Animations部分中,点击要添加的动画旁边的下拉箭头,选择需要设置的动画


    添加动画
    <?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"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.mynavigationdemo.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main">
    
            <action
                android:id="@+id/action_mainFragment_to_secondFragment"
                app:destination="@id/secondFragment"
                app:enterAnim="@android:anim/fade_in"
                app:exitAnim="@android:anim/fade_out"
                app:popEnterAnim="@android:anim/fade_in"
                app:popExitAnim="@android:anim/fade_out" />
        </fragment>
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.example.mynavigationdemo.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" />
    </navigation>
    

    6. 传递参数

    6.1 基本类型参数

    6.1.1 添加参数

    在nav_graph.xml页面的Design面板里,点击接收参数的目的地Fragment(此处为secondFragment), 然后在Attributes面板的Arguments列点击右侧添加按钮(+)可添加参数

    添加参数
    <?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"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.mynavigationdemo.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main">
    
            <action
                android:id="@+id/action_mainFragment_to_secondFragment"
                app:destination="@id/secondFragment"
                app:enterAnim="@android:anim/fade_in"
                app:exitAnim="@android:anim/fade_out"
                app:popEnterAnim="@android:anim/fade_in"
                app:popExitAnim="@android:anim/fade_out" />
        </fragment>
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.example.mynavigationdemo.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" >
            <argument
                android:name="name"
                app:argType="string"
                android:defaultValue='"HSG"' />
        </fragment>
    </navigation>
    
    6.1.2 接收参数
    class SecondFragment : Fragment() {
        val TAG = "SecondFragment"
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val name = arguments?.getString("name")
            Log.d(TAG, "onCreate() called with: name = $name")
        }
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_second, container, false)
        }
    }
    

    logcat 输出:D/SecondFragment: onCreate() called with: name = "HSG"

    6.1.3 传递参数
    class MainFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
                val bundle = Bundle()
                bundle.putString("name", "Hello World")
                findNavController().navigate(R.id.action_mainFragment_to_secondFragment, bundle)
            }
        }
    }
    

    logcat 输出:D/SecondFragment: onCreate() called with: name = Hello Word

    6.2 使用safe args传递参数

    该插件可以生成简单的 object和builder类,以便以类型安全的方式浏览和访问任何关联的参数。我们强烈建使用Safe Args进行数据传递,因为它可以确保类型安全。

    6.2.1 添加依赖

    在project的build.gradle文件中添加safe args插件:

    buildscript {
        repositories {
            google()
        }
        dependencies {
            def nav_version = "2.3.5"
            classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
        }
    }
    

    在app的build.gradle文件中添加:

    (1)如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请添加:

    apply plugin: "androidx.navigation.safeargs"
    

    (2)如需生成适用于仅 Kotlin 模块的 Kotlin 语言代码,请添加:

    apply plugin: "androidx.navigation.safeargs.kotlin"
    

    根据迁移到 AndroidX 文档,gradle.properties 文件中必须具有 android.useAndroidX=true

    启用 Safe Args 后,生成的代码会为每个Action(包含发送方和接收方)提供类型安全的类和方法:

    • 为Action的每一个发送方创建一个类。该类的名称是在发送方的名称后面加上“Directions”。例如,如果发送方是名为MainFragment的Fragment,则生成的类的名称为 MainFragmentDirections,该类会为发送方中定义的每个Action提供一个方法。
    • 对于用于传递参数的每个Action,都会创建一个 inner 类,该类的名称根据Action的名称确定。例如,如果Action名称为 action_mainFragment_to_secondFragment,则类名称为 ActionMainFragmentToSecondFragment,可以此关联的 action 类来设置参数值。
    • 为接收目方创建一个类。该类的名称是在接收方的名称后面加上“Args”。例如,如果接收方的 Fragment 的名称为 SecondFragment,则生成的类的名称为 SecondFragmentArgs。可以使用该类的 fromBundle() 方法获取传递的参数。
    6.2.2 传递参数
    class MainFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
    //            val bundle = Bundle()
    //            bundle.putString("name", "Hello World")
    //            findNavController().navigate(R.id.action_mainFragment_to_secondFragment, bundle)
                val direction = MainFragmentDirections.actionMainFragmentToSecondFragment("Hello Android")
                findNavController().navigate(direction)
            }
        }
    }
    
    6.2.3 接收参数
    class SecondFragment : Fragment() {
        val TAG = "SecondFragment"
        private val args by navArgs<SecondFragmentArgs>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    //        val name = arguments?.getString("name")
            Log.d(TAG, "onCreate() called with: name = ${args.name}")
        }
    }
    

    logcat 输出:D/SecondFragment: onCreate() called with: name = Hello Android

    7. 配置ActionBar

    通过Navigation完成页面跳转已基本完成,但和Activity跳转相比仍有不足之处——当前的ActionBar标题不会跟随Navigation页面变化,而且也没有用于返回的小图标。因此,本节就来解决该问题,Navigation已提供了便捷的解决方案,只需配置NavController对ActionBar进行控制,即可解决该问题。

    在MainActivity中,通过四步即可完成配置:
    第1步,在Activity上获取NavController

    navController = findNavController(R.id.nav_host_fragment)
    

    第2步,配置AppBarConfiguration,以便NavController接管ActionBar后能正确进行控制

    appbarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
    

    第3步,配置NavController对ActionBar进行控制

    NavigationUI.setupActionBarWithNavController(this, navController, appbarConfiguration)
    

    第4步,让NavController接管返回事件

    override fun onSupportNavigateUp(): Boolean {
            return NavigationUI.navigateUp(
                navController,
                appbarConfiguration
            ) or super.onSupportNavigateUp()
        }
    

    完整代码:

    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 = findNavController(R.id.nav_host_fragment)
            appbarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
            NavigationUI.setupActionBarWithNavController(this, navController, appbarConfiguration)
        }
    
        override fun onSupportNavigateUp(): Boolean {
            return NavigationUI.navigateUp(
                navController,
                appbarConfiguration
            ) or super.onSupportNavigateUp()
        }
    }
    

    相关文章

      网友评论

          本文标题:Jetpack之Navigation

          本文链接:https://www.haomeiwen.com/subject/zibhbltx.html