欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Navigation 组件(进阶篇)

程序员文章站 2022-03-19 16:13:15
1、使用 NavigationUI 更新界面组件Navigation 组件包含 NavigationUI 类。此类包含使用顶部应用栏、抽屉式导航栏和底部导航栏管理导航的静态方法。可参阅:Android Material 常用组件详解(七)—— BottomNavigationView 使用详解Android Material 常用组件详解(九)—— NavigationView 使用详解Android Material 常用组件详解(十)—— ToolBar、AppBarLayout、Coordi...

1、使用 NavigationUI 更新界面组件

Navigation 组件包含 NavigationUI 类。此类包含使用顶部应用栏、抽屉式导航栏和底部导航栏管理导航的静态方法。

可参阅:
Android Material 常用组件详解(七)—— BottomNavigationView 使用详解
Android Material 常用组件详解(九)—— NavigationView 使用详解
Android Material 常用组件详解(十)—— ToolBar、AppBarLayout、CoordinatorLayout、CollapsingToolbarLayout 使用详解

1.1 顶部应用栏

顶部应用栏在应用顶部提供了一个固定位置,用于显示当前屏幕的信息和操作。如需详细了解应用栏,请参阅设置应用栏
Android Navigation 组件(进阶篇)

NavigationUI 包含在用户浏览您的应用时自动更新顶部应用栏中内容的方法。例如,NavigationUI 使用导航图中的目的地标签android:label及时更新顶部应用栏的标题,即应用栏的标题会显示与之对应的android:label内容。

<navigation>
    <fragment ...
              android:label="Page title">
      ...
    </fragment>
</navigation>

NavigationUI 支持以下顶部应用栏类型:

  • Toolbar
  • CollapsingToolbarLayout
  • ActionBar

Toolbar

NavigationUI 使用 AppBarConfiguration 对象管理在应用显示区域左上角的导航按钮行为。导航按钮的行为会根据用户是否位于*目的地而变化。
当用户位于*目的地时,如果目的地使用 DrawerLayout,导航按钮会变为抽屉式导航栏图标Android Navigation 组件(进阶篇) 。如果目的地没有使用 DrawerLayout,导航按钮处于隐藏状态。当用户位于任何其他目的地上时,导航按钮会显示为向上按钮 Android Navigation 组件(进阶篇)

<LinearLayout>
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar" />
     <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        ... />
    ...
</LinearLayout>
        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
//        val appBarConfiguration = AppBarConfiguration(setOf( R.id.fragmentA, R.id.fragmentB, R.id.fragmentC))
        toolbar.setupWithNavController(navController, appBarConfiguration)

CollapsingToolbarLayout

<LinearLayout>
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/tall_toolbar_height">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="top"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"/>
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        ... />
    ...
</LinearLayout>
override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)
    
    val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
    val toolbar = findViewById<Toolbar>(R.id.toolbar)
    
    val navController = findNavController(R.id.nav_host_fragment)
    val appBarConfiguration = AppBarConfiguration(navController.graph)
    layout.setupWithNavController(toolbar, navController, appBarConfiguration)
}

默认应用栏

如需向默认操作栏添加导航支持,请通过主 Activity 的 onCreate() 方法调用 setupActionBarWithNavController(),如下所示。请注意,您需要在 onCreate() 之外声明 AppBarConfiguration,因为您在替换 onSupportNavigateUp() 时也使用该方法:

private lateinit var appBarConfiguration: AppBarConfiguration

override fun onCreate(savedInstanceState: Bundle?) {

	val navController = findNavController(R.id.nav_host_fragment)
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.nav_host_fragment)
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

2.2 抽屉式导航栏

抽屉式导航栏是显示应用主导航菜单的界面面板。当用户触摸应用栏中的抽屉式导航栏图标 Android Navigation 组件(进阶篇)或用户从屏幕的左边缘滑动手指时,就会显示抽屉式导航栏。
Android Navigation 组件(进阶篇)

XML布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" />

        <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/mobile_navigation" />

    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>

NavigationView的菜单每个选项的id必须要和导航图中的每个fragment的id相同,这样才能当用户点击菜单项时,应用会自动使用相同的 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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/nav_home">

    <fragment
        android:id="@+id/nav_home"
        android:name="com.matt.myapplication3.ui.home.HomeFragment"
        android:label="@string/menu_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/nav_gallery"
        android:name="com.matt.myapplication3.ui.gallery.GalleryFragment"
        android:label="@string/menu_gallery"
        tools:layout="@layout/fragment_gallery" />

    <fragment
        android:id="@+id/nav_slideshow"
        android:name="com.matt.myapplication3.ui.slideshow.SlideshowFragment"
        android:label="@string/menu_slideshow"
        tools:layout="@layout/fragment_slideshow" />
</navigation>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/nav_gallery"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/nav_slideshow"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
    </group>
</menu>

将抽屉菜单与导航组件、应用栏关联

class MainActivity : AppCompatActivity() {

    private lateinit var appBarConfiguration: AppBarConfiguration

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 将自定义的toolbar取代原本的actionbar
        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val navController = findNavController(R.id.nav_host_fragment)

        // 将导航组件与应用栏关联
//        appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
//        appBarConfiguration = AppBarConfiguration(navView.menu, drawerLayout)
       appBarConfiguration = AppBarConfiguration(setOf(
                R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow), drawerLayout)
        setupActionBarWithNavController(navController, appBarConfiguration)

        // 将抽屉式导航栏与导航组件
        navView.setupWithNavController(navController)
    }


    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
}
  • setSupportActionBar
    setSupportActionBar是用自定义的Toolbar替换原本的actionbar,在替换之后才能使用actionbar的方法,例如,上面的示例中,在调用 setupActionBarWithNavController()方法自动修改Toolbar的标题为导航图中的目的地标签android:label

  • AppBarConfiguration
    AppBarConfiguration有3种创建方式

@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
    navGraph: NavGraph,
    drawerLayout: Openable? = null,
    noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(navGraph)
    .setOpenableLayout(drawerLayout)
    .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
    .build()

@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
    topLevelMenu: Menu,
    drawerLayout: Openable? = null,
    noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(topLevelMenu)
    .setOpenableLayout(drawerLayout)
    .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
    .build()


@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
    topLevelDestinationIds: Set<Int>,
    drawerLayout: Openable? = null,
    noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(topLevelDestinationIds)
    .setOpenableLayout(drawerLayout)
    .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
    .build()

第一种和第二种方式的效果相同,在切换fragment之后,图标Android Navigation 组件(进阶篇) 自动变成 Android Navigation 组件(进阶篇)

Android Navigation 组件(进阶篇)
第三种方式图标不会改变
Android Navigation 组件(进阶篇)

2.3 底部导航栏

Android Navigation 组件(进阶篇)
XML布局

<?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"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="?attr/actionBarSize">

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

    <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:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

BottomNavigationView的菜单每个选项的id必须要和导航图中的每个fragment的id相同,这样才能当用户点击菜单项时,应用会自动使用相同的 id 导航到相应目的地。和抽屉式导航栏一致,这里不再说明

将底部导航与导航组件、应用栏关联

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val navView: BottomNavigationView = findViewById(R.id.nav_view)

        val navController = findNavController(R.id.nav_host_fragment)
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        val appBarConfiguration = AppBarConfiguration(setOf(
                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications))
        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)
    }
}

2、使用 ViewModel 在目标间共享界面相关数据

在 Kotlin 中,您可以让共享数据的Fragment使用 navGraphViewModels() 属性委托从 Fragment 目标中检索 ViewModel,创建一个范围限定于导航图的 ViewModel,从而使您能够在图表的目标间共享与界面相关的数据。可以以这种方式创建的任何 ViewModel 对象都会一直存在,直至关联的 NavHost 及其 ViewModelStore 遭到清除,或导航图从返回栈中弹出为止。

ShareDataModel .kt

class ShareDataModel : ViewModel() {
    var share: String? = null
}

BlankFragment.kt

class BlankFragment : Fragment() {

    private val shareDataModel by navGraphViewModels<ShareDataModel>(R.id.nav_graph)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        shareDataModel.share = "share data from navGraphViewModels"

        val binding = FragmentBlankBinding.inflate(inflater, container, false)
        binding.button.setOnClickListener {
            findNavController().navigate(BlankFragmentDirections.actionBlankFragmentToBlankFragment2())
        }
        return binding.root
    }
}

BlankFragment2.kt

class BlankFragment2 : Fragment() {

    private val shareDataModel by navGraphViewModels<ShareDataModel>(R.id.nav_graph)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = FragmentBlank2Binding.inflate(inflater, container, false)
        binding.textview.text = shareDataModel.share
        return binding.root
    }

}

Android Navigation 组件(进阶篇)

本文地址:https://blog.csdn.net/weixin_42046829/article/details/110491443

相关标签: Android