本文地址:https://blog.csdn.net/qq_40785165/article/details/113484751,转载需附上此地址
大家好,我是小黑,一个还没秃头的程序员~~~
水再浑浊,只要长久沉淀,依然会分外清澈;人再愚钝,只要足够努力,一样能改写命运。
这次小黑带来的是Android Jetpack中的导航组件-----Navigation,引用官方的一段话介绍导航组件
在不同屏幕和应用之间导航是用户体验的核心组成部分。以下原则为跨各种应用提供一致且直观的用户体验设定了基准。Navigation 组件设计为默认实现这些原则,从而确保用户在各应用之间切换时能够使用相同的启发法和模式进行导航。
注:如果要使用导航和 Android Studio,则必须使用 Android Studio 3.3 或更高版本。
这次文章的内容是Navigation组件的入门使用,使用Kotlin编写,进行简单的Fragment与Fragment/Activity的页面跳转,源码地址:https://gitee.com/fjjxxy/navigation-demo.git,效果图如下
注意事项一:AndroidStudio更新到4.1版本之后kotlin不能再直接用id进行操作控件了,可以改用ViewBinding或者在build(app)中手动添加kotlin-android-extensions插件id
话不多说,正文开始
(一)添加相关依赖,官方版本是2.3.2,但是这个版本下找不到FragmentContainerView,原因未知,所以这里就改用版本2.3.0
def nav_version = "2.3.0"
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// Feature module Support
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
// Jetpack Compose Integration
implementation "androidx.navigation:navigation-compose:1.0.0-alpha05"
(二)准备多个用来切换的Fragment,本案例中有两个Fragment
NavigationFragment1.kt以及fragment_navigation1.xml代码如下:第四行是Fragment跳转Activity的代码,继续往下看就会看到相关内容了
class NavigationFragment1 : BaseFragment() {
override fun initView(rootView: View) {
rootView.tv_to_second.setOnClickListener {
findNavController().navigate(R.id.to_second)
}
}
override fun getLayoutId(): Int {
return R.layout.fragment_navigation1
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#9999FF"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment_navigation1" />
<TextView
android:id="@+id/tv_to_second"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="40dp"
android:background="#e8e8e8"
android:gravity="center"
android:text="跳转到SecondActivity" />
</LinearLayout>
NavigationFragment2.kt以及fragment_navigation2.xml代码如下:
class NavigationFragment2 : BaseFragment() {
override fun initView(rootView: View) {
}
override fun getLayoutId(): Int {
return R.layout.fragment_navigation2
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0033"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment_navigation2" />
</LinearLayout>
公共类BaseFragment.kt代码如下:只是一个抽象类,方便开发者加载布局和初始化布局
abstract class BaseFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var inflate = inflater.inflate(getLayoutId(), container, false)
initView(inflate)
return inflate;
}
abstract fun initView(rootView: View)
abstract fun getLayoutId(): Int
}
(三)碎片(Fragment)准备好了,现在应该找个容器来装了,就写在MainActivity里,布局文件为activity_main.xml,以往我们的做法是准备一个FrameLayout进行replace替换fragment,这里官方的建议控件是
androidx.fragment.app.FragmentContainerView
activity_main.xml代码如下:布局很简单,FragmentContainerView用来存放不同的Fragment,底下两个TextView用来处理切换Fragment的逻辑
注意事项二:这里FragmentContainerView在指定了name属性后一定要添加id属性,否则会报错
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_config" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_to_fragment1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="navigation1" />
<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="#000" />
<TextView
android:id="@+id/tv_to_fragment2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="navigation2" />
</LinearLayout>
</LinearLayout>
- android:name属性用来指定实现NavHost的类名称
- app:navGrap属性用来配置导航器中的fragment以及和fragment跳转有关的目的activity,创建一个资源文件夹,命名为nagivation(别的命名不知道有没有关系,大家可以试一下),新建xml文件-nav_config.xml,用来配置切换的fragment以及activity,以及设置fragment跳转的目的地
- app:defaultNavHost="true"属性指定默认的NavHost
nav_config.xml代码如下:action设置跳转的id以及目的地
注意事项三:startDestination属性不能不写,否则会报错,页面的id也不能不写,否则也会报错
<?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"
android:id="@+id/nav_config"
app:startDestination="@id/fragment_nav_1">
<!--对应fragment的id不能不写-->
<fragment
android:id="@+id/fragment_nav_1"
android:name="com.example.navigationdemo.NavigationFragment1">
<action
android:id="@+id/to_fragment2"
app:destination="@id/fragment_nav_2" />
<action
android:id="@+id/to_second"
app:destination="@id/activity_second" />
</fragment>
<fragment
android:id="@+id/fragment_nav_2"
android:name="com.example.navigationdemo.NavigationFragment2">
<action
android:id="@+id/to_fragment1"
app:destination="@id/fragment_nav_1" />
</fragment>
<activity
android:id="@+id/activity_second"
android:name="com.example.navigationdemo.SecondActivity" />
</navigation>
- android:id属性①fragment中的id指定自身id,或者作为其他页面的目标id,②action用来指定操作的id,在逻辑代码中告知跳转的目的地
- app:destination属性指定目标Fragment/Activity的id
- action用来表示操作,操作至少要有id和目的地,缺少后会报错
- fragment标签用来设置fragment,activity标签用来设置activity
配置文件写完后可以在右方打开Design模式看见导航图,如下图
最后,在MainActivity.kt中实现点击切换Fragment的逻辑,代码如下:
注意事项四:这里需要判断当前fragment是不是没变,没变就不跳转,因为navigation不运行当前页面和目标页面都是自己,否则会报错
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//这里需要判断当前fragment是不是没变,没变就不跳转,因为navigation不运行当前页面和目标页面都是自己,否则会报错
tv_to_fragment1.setOnClickListener {
if (getFragment(NavigationFragment1::class.java) == null) {
findNavController(R.id.fragment_container_view).navigate(R.id.to_fragment1)
}
}
tv_to_fragment2.setOnClickListener {
if (getFragment(NavigationFragment2::class.java) == null) {
findNavController(R.id.fragment_container_view).navigate(R.id.to_fragment2)
}
}
}
@Suppress("UNCHECKED_CAST")
fun <F : Fragment> AppCompatActivity.getFragment(fragmentClass: Class<F>): F? {
val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
navHostFragment.childFragmentManager.fragments.forEach {
if (fragmentClass.isAssignableFrom(it.javaClass)) {
return it as F
}
}
return null
}
}
在Activity中导航到目的地有两种方法,我用的第一种
- Activity.findNavController(R.id.xxx):通过指定NavHostFragment的id找到NavController
- supportFragmentManager.findFragmentById(R.id.xxx):通过指定NavHostFragment的id找到NavHostFragment,然后再取出NavController
接着调用navigate函数进行导航到目标
NavigationFragment1.kt文件中实现了 Fragment跳转另外一个Activity的代码:同样是通过指定目标的id进行跳转,目标Activity的id和类名称已在上面的nav_config.xml文件中定义于<activity>标签中
class NavigationFragment1 : BaseFragment() {
override fun initView(rootView: View) {
tv_to_second.setOnClickListener {
findNavController().navigate(R.id.tv_to_second)
}
}
override fun getLayoutId(): Int {
return R.layout.fragment_navigation1
}
}
总结:
- 设计Fragment布局
- 设计存放NavHostFragment的布局,比如这里的activity_main.xml
- 设计navigation的xml配置文件,设置各个目标Fragment/Activity的ID以及类名称
- 在逻辑代码中实现导航到目标的逻辑
- *上面内容提到的不能不写的属性要注意
目前为止,Navigation关于Fragment与Fragment/Activity页面跳转的内容就已经完成了,这篇文章是Navigation系列的入门使用记录,后面我将会带来关于Navigation其他的使用内容,敬请期待,也希望喜欢我文章朋友们可以帮忙点赞、收藏、评论,也可以关注一下,如果有问题可以在评论区提出,也欢迎大家订阅我的个人微信公众号,谢谢大家的支持!