Jetpack之Navigation

2021-08-12  本文已影响0人  948d307d6ecf

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

有三个核心点:

三者之间的关系:当需要切换页面时,使用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,其中有三点需要注意:

<?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(包含发送方和接收方)提供类型安全的类和方法:

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()
    }
}
上一篇下一篇

猜你喜欢

热点阅读