Android Jetpack学习笔记之Navigation (一)

1.xml试图布局元素分析

1.1 .1试图元素属性分析

1.在创建工程Activity的时候选择

这样就是自动创建了底部导航栏 分别是3个底部导航table。

1.2 手动创建 

在layout_activity_main中写布局,布局如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment_container"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />


</androidx.constraintlayout.widget.ConstraintLayout>
1.3 分析属性
1.3.1   FragmentContainerView:

一个自定义view继承了FrameLayout,与ViewGroup不同,它只接受FragmentViews

改进了对Fragment z -ordering的处理,优化了两个Fragment退出和进入过度不会互相重叠,使用ragmentContainerView将先开启退出动画 然后才是进入动画。可以使用 androidx.fragment.app.FragmentManager 更方便地进行 Fragment 的替换、添加和移除操作。它对过渡动画有更好的支持,可以更流畅地进行 Fragment 之间的切换。

FragmentContainerView 提供了更好地控制嵌入 Fragment 的行为,比如更好地处理 Fragment 的生命周期管理和布局行为

FragmentContainerView 默认不支持嵌套 Fragment 的透明度改变,这使得 UI 行为更可预测,更容易避免 Fragment 在透明度改变时出现的绘制问题。

对 Fragment 的透明度处理更加一致:它自动管理了 Fragment 的透明度,确保在多个 Fragment 堆叠时不会出现透明度问题

默认处理 Fragment 的生命周期:FragmentContainerView 能够处理 Fragment 的生命周期,并在 Activity 或者 Fragment 中相应的生命周期方法中自动进行 Fragment 的添加或移除。

1.3.2 navigation 导航

在这个布局中定义了2个元素分别是 

app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"

首先

app:defaultNavHost="true" 

这个属性用于指定当前的 FragmentContainerView 是导航图中的默认主机 (NavHost). 当设置为 true 时,NavHostFragment 会自动处理系统的后退按钮事件,并将其分发到当前活动的 Fragment。如果你在应用程序中有多个 NavHostFragment,这个属性可以确保某一个 NavHostFragment 作为默认处理者接收系统的导航事件,确保该 NavHostFragment 是应用中的主要导航主机,从而接管系统返回键的处理。

app:navGraph="@navigation/nav_graph"

这个属性指定了与 NavHostFragment 关联的导航图 (Navigation Graph)。nav_graph 是一个 XML 文件,通常位于 res/navigation 目录下,用来定义应用中的导航路径,包括 Fragment 的关系、动作(Actions)、传递的参数等。通过这个属性,NavHostFragment 可以根据导航图管理不同 Fragment 之间的导航。告诉 NavHostFragment 使用哪个导航图来管理应用中不同 Fragment 的导航路径。

然后可以在navigation目录下创建导航图nav_graph

<?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/home_Fragment">

    <fragment
        android:id="@+id/home_Fragment"
        android:name="com.neil.flicky.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
</navigation>

Fragment创建可以自己创建 然后配置到这个导航图中 还有个更方便一点的就是在nav_graph导航图中的可视化去配置

这是使用导航图内的可视化创建的方式 当你利用这种方式创建后,会自动帮你把这个Fragment配置到导航图中如图:

这里在解释一下 其中的

app:startDestination="@id/home_Fragment"
这个属性是指这个navigation的第一个Fragment用于指定导航图 (navGraph) 中的起始目的地 (start destination)。在导航图中,起始目的地是应用启动时或导航进入时首先加载的Fragment。也就是说应用启动时候第一个显示的页面 这个指定的id可以随便设置 前提是需要在这个navigation文件中已经配置好的。
1.3.3 navigation导航中的不同Fragment的其他配置

可以通过手动写去配置不同的属性  这里我就不去写了 这里示范一下可视化面板配置。同样的道理,可视化面板配置完成后会自动添加到navigation xml文件里面。

我这边动画都是选的系统默认的,这个可以自定义。

  • ID

    • 说明:这是该导航动作的唯一标识符,可以通过它来引用或触发此导航操作。
    • 推荐配置:保持系统自动生成的ID即可,除非你有特定的命名需求。
  • From

    • 说明:表示导航操作的起始Fragment,即从哪个Fragment开始导航。
  • Destination

    • 说明:表示导航操作的目标Fragment,即导航的终点。
  • Transition(过渡动画):

    • Enter:定义进入目标Fragment时的动画效果。
    • Exit:定义离开起始Fragment时的动画效果。
    • Pop Enter:定义从目标Fragment返回到起始Fragment时的动画效果。
    • Pop Exit:定义从起始Fragment返回到目标Fragment时的动画效果。
    • 推荐配置:根据用户体验要求,可以选择“None”(无动画)或添加适当的动画,如“fade”或“slide”。动画可以提升用户体验。
  • Pop Behavior(返回行为):

    • Pop To:指定在返回栈中弹出至哪个Fragment。
    • Inclusive:如果选中此选项,当触发“Pop To”时,目标Fragment也会被弹出(即不保留目标Fragment)。
    • 推荐配置:如果希望在返回时弹出多个Fragment,则设置Pop To;如果希望仅返回到上一层Fragment,保持为None即可。
  • Launch Options(启动选项):

    • Single Top:如果选择了此选项,则不会在导航栈中添加新的实例,如果目标Fragment已经在栈顶。
    • 推荐配置:根据需要设置。一般用于避免重复添加相同的Fragment实例。

3种不同的属性分别是action ,deeplink ,argment 

1.1.2 action

action 用于定义在两个 destination 之间的导航行为。destination 是指应用中的一个页面或 Fragment,而 action 则是在这些页面之间进行跳转时的“桥梁”。

具体作用:
  • 导航: 定义从一个 destination 跳转到另一个 destination 的路径。
  • 动画: 可以为 action 配置动画效果,如淡入淡出等。
  • 过渡参数: 允许将参数从一个 destination 传递到另一个。

1.1.2. deeplink

deeplink 用于定义应用的深层链接,可以直接导航到特定的 destination。通过设置 deeplink,你可以使应用从外部链接(如浏览器、通知等)直接打开特定的页面。

具体作用:
  • 深层链接: 使得应用可以通过特定的 URL 或其他形式的链接直接导航到某个 destination
  • 集成外部链接: 可以将外部的应用、通知、Web 等内容通过链接的方式集成到你的应用中。

1.1.3. argument

argument 用于定义 destinationaction 可以接受的参数。这些参数可以从一个页面传递到另一个页面,并在目标页面中使用。

具体作用:
  • 参数传递: 允许在页面间传递数据,如 ID、用户名等。
  • 默认值: 可以为参数定义默认值,方便在没有传递参数时使用。

添加完后的navigation是这样

<?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/home_fragment">
        <!-- 定义一个fragment -->
    <fragment
        android:id="@+id/home_fragment"
        android:name="com.neil.flicky.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/navigate_to_category_fragment"
            app:destination="@+id/category_fragment">

        </action>
    </fragment>
        <fragment
            android:id="@+id/category_fragment"
            android:name="com.neil.flicky.navigation.CategoryFragment"
            android:label="fragment_category"
            tools:layout="@layout/fragment_category" >
            <action
                android:id="@+id/navigate_to_tags_fragment"
                app:destination="@id/tags_fragment" />
        </fragment>
        <fragment
            android:id="@+id/tags_fragment"
            android:name="com.neil.flicky.navigation.TageFragment"
            android:label="fragment_tage"
            tools:layout="@layout/fragment_tage">
            <action
                android:id="@+id/navigate_to_home_fragment"
                app:destination="@+id/home_fragment" />
        </fragment>
    <fragment
        android:id="@+id/mine_fragment"
        android:name="com.neil.flicky.navigation.MineFragment"
        android:label="fragment_mine"
        tools:layout="@layout/fragment_mine" >
        <!-- 定义一个argument -->
        <argument
            android:name="UserId"
            app:argType="string"
            android:defaultValue="123" />
        <!-- 定义一个action -->
        <action
            android:id="@+id/action_mine_Fragment_to_category_Fragment"
            app:destination="@id/navigation_category"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"
            app:popEnterAnim="@anim/nav_default_pop_enter_anim"
            app:popExitAnim="@animator/nav_default_pop_exit_anim"
            app:launchSingleTop="true" />
        <!-- 定义一个deepLink -->
        <deepLink
            app:action="com.neil.flicky.ACTION_MINE"
            app:mimeType="type/video"
            app:uri="https://www.example.com/mine" />
    </fragment>
</navigation>





这段XML代码定义了一个导航图(Navigation Graph),用于在Android应用中管理不同Fragment之间的导航。

### 导航图根元素
```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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/home_fragment">
```
- `xmlns:android`、`xmlns:app`、`xmlns:tools`:定义了XML命名空间,用于引用Android资源。
- `android:id`:为导航图指定一个ID,用于在代码中引用。
- `app:startDestination`:指定导航图启动时显示的默认Fragment,这里是`home_fragment`。

### 定义Fragment
```xml
<fragment
    android:id="@+id/home_fragment"
    android:name="com.neil.flicky.navigation.HomeFragment"
    android:label="fragment_home"
    tools:layout="@layout/fragment_home">
    <action
        android:id="@+id/navigate_to_category_fragment"
        app:destination="@+id/category_fragment">
    </action>
</fragment>
```
- `android:id`:Fragment的ID。
- `android:name`:Fragment的类名。
- `android:label`:Fragment的标签。
- `tools:layout`:用于开发工具的布局文件引用。
- `<action>`:定义从一个Fragment导航到另一个Fragment的动作,这里是从`home_fragment`导航到`category_fragment`。

### 定义多个Fragment和导航动作
类似地,代码中定义了`category_fragment`和`tags_fragment`,并分别定义了从`category_fragment`到`tags_fragment`,以及从`tags_fragment`回到`home_fragment`的导航动作。

### 定义带有参数的Fragment
```xml
<fragment
    android:id="@+id/mine_fragment"
    android:name="com.neil.flicky.navigation.MineFragment"
    android:label="fragment_mine"
    tools:layout="@layout/fragment_mine">
    <argument
        android:name="UserId"
        app:argType="string"
        android:defaultValue="123" />
    <action
        android:id="@+id/action_mine_Fragment_to_category_Fragment"
        app:destination="@id/navigation_category"
        app:enterAnim="@anim/nav_default_enter_anim"
        app:exitAnim="@anim/nav_default_exit_anim"
        app:popEnterAnim="@anim/nav_default_pop_enter_anim"
        app:popExitAnim="@animator/nav_default_pop_exit_anim"
        app:launchSingleTop="true" />
    <deepLink
        app:action="com.neil.flicky.ACTION_MINE"
        app:mimeType="type/video"
        app:uri="https://www.example.com/mine" />
</fragment>
```
- `<argument>`:定义Fragment的参数,这里定义了一个名为`UserId`的字符串参数,默认值为`123`。
- `<action>`:定义从一个Fragment导航到另一个Fragment的动作,并指定了进入和退出动画。
- `<deepLink>`:定义了一个深度链接,允许应用通过特定的URI和动作来启动特定的Fragment。

### 注意事项
- 确保所有引用的Fragment类和资源文件(如布局文件、动画文件)都存在且正确。
- 使用导航图时,需要在Activity或Fragment中设置`NavController`,以便管理导航逻辑。
- 深度链接(Deep Links)需要正确配置,以便应用能够响应特定的URI和动作。

这段代码展示了如何使用导航图来管理应用中的Fragment导航,包括定义Fragment、导航动作、参数和深度链接。

1.1.4 绑定视图 实现Fragment的切换

在Activity中注册使用这个底部导航栏,首先底部导航栏需要一些按钮 这就要说到试图中配置的一个属性。

 app:menu="@menu/bottom_nav_menu" 

这个就是配置底部导航栏的图标。这个就不多加说属性了。具体配置如下

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/home_fragment"
        android:icon="@drawable/shouye"
        android:title="@string/home_title" />
    <item
        android:id="@+id/tags_fragment"
        android:icon="@drawable/huanqiu"
        android:title="@string/tage_title" />
    <item
        android:id="@+id/category_fragment"
        android:icon="@drawable/faxian"
        android:title="@string/category_title" />
    <item
        android:id="@+id/mine_fragment"
        android:icon="@drawable/wode"
        android:title="@string/mine_title" />
</menu>

这样就算是把视图所要用的其他的图标配置好了。配置好的效果如下所示

那么UI部分算是做完了  那么就应该绑定使用了接下来我们简单的绑定使用

package com.neil.flicky

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.neil.flicky.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        val navView: BottomNavigationView = binding.navView
        val navHostFragment=supportFragmentManager.findFragmentById(R.id.nav_host_fragment_container) as NavHostFragment
        val navController = navHostFragment.findNavController()
        navView.setupWithNavController(navController)
    }
}

我们简单来说一下这些API
 

onCreate方法是活动生命周期的一部分,当活动第一次创建时会被调用。以下是方法中的步骤:

  1. 调用super.onCreate(savedInstanceState)来确保父类的onCreate方法被调用。
  2. 使用binding.root作为内容视图,这会设置活动的主布局。
  3. binding对象中获取BottomNavigationView实例。
  4. 使用supportFragmentManager找到NavHostFragment,并将其转换为NavHostFragment类型。
  5. NavHostFragment中获取NavController实例。
  6. 使用setupWithNavController方法将BottomNavigationViewNavController关联起来,这样底部导航视图就可以响应导航操作了。

用途

这段代码的目的是创建一个具有底部导航栏的Android活动,使用Jetpack Navigation库来管理导航。底部导航栏允许用户在不同的片段之间切换,而无需重新加载活动。

注意事项

  1. 确保在res/navigation目录下有一个名为nav_graph.xml的导航图文件,定义了导航结构。
  2. NavHostFragmentid必须与布局文件中的nav_host_fragment_container匹配。
  3. BottomNavigationViewid必须与布局文件中的navView匹配。

注意一下有个和官方创建不一样的地方

这是你用AS直接创建后生成的xml布局,这个布局中用的是fragment,我这边使用的是FragmentContainerView,而且 官方创建的MainActivity中没有这一行代码,那我写的布局和注册多的这一行代码是为什么?

当你在布局文件中直接使用 NavHostFragment 标签(或者使用 Fragment 标签来包裹 NavHostFragment)时,系统会自动处理与导航组件相关的所有配置。NavHostFragment 自带了对 NavController 的绑定,通常不需要手动获取它的实例来进行额外的初始化或绑定操作。

Fragment vs FragmentContainerView

  • Fragment 标签: 它直接指向一个 Fragment 类(如 NavHostFragment),在加载时,系统会自动创建并绑定该 Fragment。因此,导航控制器和 NavHostFragment 之间的绑定是自动完成的,通常不需要手动处理。

  • FragmentContainerView: 这是一个更通用的 ViewGroup 容器,专门用于容纳和管理 Fragment 的视图层次结构。在使用 FragmentContainerView 时,你需要手动管理和控制 Fragment 的生命周期和绑定。因此,如果你使用 FragmentContainerView 来包裹 NavHostFragment,系统不会自动完成绑定,这就是为什么你需要手动获取 NavHostFragment 实例。

手动 vs 自动初始化

  • 当你使用 Fragment 标签时,系统隐式地完成了初始化工作,因此代码更简洁,开发者无需手动获取 NavHostFragment 实例。
  • 当你使用 FragmentContainerView 时,你负责更细粒度的控制,因此需要手动获取和初始化相关的 Fragment

综上所述,如果你直接使用 Fragment 标签,那么导航组件的绑定是自动完成的;而如果你使用 FragmentContainerView,你需要手动完成这些绑定和初始化。

既然如此那么直接使用Fragment不是更好吗?其实不然 具体还是要看情况而定
 

使用 Fragment 标签

优点:
  • 简洁易用:适合大多数简单场景,系统会自动处理 Fragment 的管理和生命周期,开发者不需要做额外的工作。
  • 自动绑定:与 NavHostFragment 一起使用时,导航控制器和 Fragment 的绑定是自动完成的,减少了手动初始化的步骤。
缺点:
  • 灵活性较低:你对 Fragment 的生命周期和容器的控制较少。如果需要复杂的自定义操作(如动态添加/替换 Fragment),可能不够灵活。

使用 FragmentContainerView

优点:
  • 更高的灵活性:提供更细粒度的控制,你可以动态地添加、替换和管理 Fragment,适用于复杂的 UI 布局和交互。
  • 现代化设计FragmentContainerView 是 Android 官方推荐用于承载 Fragment 的容器,更符合现代开发最佳实践。
缺点:
  • 需要更多手动管理:你需要手动完成一些系统自动处理的工作,例如获取 NavHostFragment 实例并进行绑定,这可能增加代码的复杂
     

综合建议:

简单的项目用Fragment包裹就好了,如果较为复杂的app,比如需要动态添加删除Fragment的那种情况就用FragmentContainerView更适合,比如由服务器或者热更配置要更新底部导航栏,需要增加或者删除某个Fragment的时候

<------------------------------------------------------------------------------------------------------------------------>

                            注:学习笔记 有需要改进的请大佬们指点,勿喷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值