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
用于定义 destination
或 action
可以接受的参数。这些参数可以从一个页面传递到另一个页面,并在目标页面中使用。
具体作用:
- 参数传递: 允许在页面间传递数据,如 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
方法是活动生命周期的一部分,当活动第一次创建时会被调用。以下是方法中的步骤:
- 调用
super.onCreate(savedInstanceState)
来确保父类的onCreate
方法被调用。 - 使用
binding.root
作为内容视图,这会设置活动的主布局。 - 从
binding
对象中获取BottomNavigationView
实例。 - 使用
supportFragmentManager
找到NavHostFragment
,并将其转换为NavHostFragment
类型。 - 从
NavHostFragment
中获取NavController
实例。 - 使用
setupWithNavController
方法将BottomNavigationView
与NavController
关联起来,这样底部导航视图就可以响应导航操作了。
用途
这段代码的目的是创建一个具有底部导航栏的Android活动,使用Jetpack Navigation库来管理导航。底部导航栏允许用户在不同的片段之间切换,而无需重新加载活动。
注意事项
- 确保在
res/navigation
目录下有一个名为nav_graph.xml
的导航图文件,定义了导航结构。 NavHostFragment
的id
必须与布局文件中的nav_host_fragment_container
匹配。BottomNavigationView
的id
必须与布局文件中的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的时候
<------------------------------------------------------------------------------------------------------------------------>
注:学习笔记 有需要改进的请大佬们指点,勿喷