大家好! 我想谈谈
Navigation Architecture Component工作中的功能,因此我对库有一个模糊的印象。
本文不是循序渐进的指南;它省略了实现细节来重点关注关键点。 互联网上有许多
类似的用例(也有翻译)-它们将帮助您熟悉图书馆。 另外,在阅读之前,我建议研究
文档 。
我必须马上说,我绝对认为该库很有用,并且不排除滥用的可能性,但是我可能在写本文之前就尝试了所有方法。因此,以下是实现中的功能预期与实现中的实际情况不符的场景:
在菜单项之间切换
这是影响使用导航组件的决定的那些功能之一。
您只需要使菜单项ID相同
activity_main_drawer.xml<?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/importFragment" android:icon="@drawable/ic_menu_camera" android:title="Import"/> <item android:id="@+id/galleryFragment" android:icon="@drawable/ic_menu_gallery" android:title="Gallery"/> <item android:id="@+id/slideshowFragment" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow"/>
和屏幕ID(导航图中的目标)
mobile_navigation.xml <?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/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"/> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> </navigation>
那么您需要将菜单与导航控制器相关联:
MainActivity.kt class MainActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar)
菜单中的导航有效-这不是奇迹吗?
注意“汉堡”(菜单图标),当在菜单项之间切换时,它的状态变为“后退”按钮。 这种行为似乎
很不寻常 (
很熟悉 -就像在游戏市场应用程序中一样),并且有一段时间,我试图找出出什么问题了?
仅此而已! 在阅读了有关导航原理的文档(即,第二点和
第三点)之后,我意识到“汉堡”仅针对
startDestination显示 ,或者这样显示:除
startDestination之外,所有用户均显示后退按钮。 可以通过在订阅中应用各种技巧(
addOnNavigatedListener() )来更改
目的地 ,从而改变这种情况,但是甚至不应对其进行描述。 它的工作原理是这样的,您需要达成共识。
开启新活动
活动可以充当
导航主机 ,同时,在导航图中可以充当
目的地之一 。 打开没有嵌套导航图的Activity可以
按预期进行 ,即调用:
navController.navigate(R.id.editActivity)
将执行过渡(如片段情况)并打开请求的活动。
考虑目标Activity本身充当
导航主机的情况 (即,
文档中的选项2)的情况要有趣得多:

例如,让我们看一个添加注释的活动。 它将
包含带有
EditFragment输入
字段的主片段;它将在导航图中为
startDestination 。 假设在编辑时我们需要附加照片,为此,我们将转到
PhotoFragment从相机获取图片。 导航图将如下所示:
edit_navigation.xml <?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/edit_navigation" app:startDestination="@id/editFragment"> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
EditActivity与
MainActivity没有太大区别。 主要区别在于
EditActivity上没有菜单:
EditActivity.kt class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) // "" toolbar NavigationUI.setupWithNavController(toolbar, navController) } override fun onSupportNavigateUp() = navController.navigateUp() fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
活动打开,其中的导航有效:
同样,请注意工具栏上的导航按钮-在开始的
EditFragment上没有“返回父活动”按钮(但我想
这么做 )。 从文档的角度来看,这里的一切都是合法的:新的导航图,新的
startDestination值,在“
startDestination ”(末尾)处未显示“返回”按钮。
对于那些希望
通过父活动恢复其
正常行为 ,同时又保持在片段之间切换的功能的人,我可以提供这种
拐杖方法:
1.在清单中指定父活动 <activity android:name=".EditActivity" android:parentActivityName=".MainActivity" android:theme="@style/AppTheme.NoActionBar"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity>
2.添加一个我们将替换id startDestination的订阅 class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } private var isStartDestination = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) val startDestinationId = navController.graph.startDestination // id, NavigationUI.ActionBarOnNavigatedListener // destination startDestination navController.addOnNavigatedListener { controller, destination -> isStartDestination = destination.id == startDestinationId // R.id.fake_start_destination id controller.graph.startDestination = if (isStartDestination) R.id.fake_start_destination else startDestinationId } // "" toolbar NavigationUI.setupActionBarWithNavController(this, navController) } override fun onSupportNavigateUp(): Boolean { // startDestination Navigation Component return if (isStartDestination) super.onSupportNavigateUp() else navController.navigateUp() } fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
必须进行预订,以便对于
NavigationUI.ActionBarOnNavigatedListener而言, 所有目标都不是 startDestination 。 因此,
NavigationUI.ActionBarOnNavigatedListener将不会隐藏导航按钮(有关详细信息,请参阅源代码)。 在
startDestination处以常规方式
向其添加
onSupportNavigateUp()处理
,并获得我们想要的。
值得一提的是,该解决方案远非理想之举,仅因为这是对库行为的明显干预。 我相信使用
深层链接时可能会出现问题(我尚未测试过)。
将参数传递给startDestination
导航组件具有一种将参数从一个
目的地传递
到另一个
目的地的
机制 。 甚至还有一个
工具可以通过代码生成
来确保类型安全 (不错)。
现在,我们将分析该案例,因此,我无法为该功能添加固定的5个。
让我们回到
EditActivity ,这是一个相当熟悉的场景,其中一个Activity用于创建和编辑对象。 在“活动”中打开要编辑的对象时,您需要转移例如对象的ID-让我们以常规方式进行操作:
1.在图形中为EditActivity添加参数我直接将参数添加到了图形的根元素(导航),但可以将其添加到目标片段。 由此,仅获取参数的方法将改变。
<?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/edit_navigation" app:startDestination="@id/editFragment"> <argument android:name="id" app:argType="integer"/> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
2.将动作添加到主图中我在其中一个片段中添加了添加和编辑操作,因此只能从中使用它们。
<?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/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"> <action android:id="@+id/add" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer" android:defaultValue="0"/> </action> <action android:id="@+id/edit" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer"/> </action> </fragment> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> <activity android:id="@+id/editActivity" android:name="com.xiii.navigationapplication.EditActivity" android:label="activity_edit" tools:layout="@layout/activity_edit"/> </navigation>
3.准备参数并请求转换在此示例中,
ImportFragmentDirections是自动生成的safe-args类。
val direction = ImportFragmentDirections.edit(123 ) navController.navigate(direction)
3.获取片段中的ID class EditFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
您肯定会注意
EditFragment中获取参数的
功能 。 之所以可行,是因为edit动作(从点1开始)将参数传递给
EditActivity ,并且出于某种原因,它很
贪婪地没有将其传递给图形(例如,通过调用
navController.graph.setDefaultArguments() )。 可以通过手动准备
导航控制器来规避此功能。 在
StackOwerflow上描述了一种方法。
当同时用作
startDestination和通常的
目的地时,可能会遇到最大的困难。 也就是说,当从该图的任何其他
目标传递参数并将其传递给
startDestination时 ,该片段将必须独立地确定从以下位置提取参数的位置:参数或intent.extras。 在设计带有传递参数的过渡时必须牢记这一点。
总而言之,我想指出的是,我本人并没有停止使用该库,尽管列出
了该功能的
缺点 ,但我发现它足以推荐使用。 我真的希望在下一个发行版中,情况至少会发生变化,至少将参数传递给
startDestination 。
谢谢您的关注。 您的工作代码!
文章的来源发布在
GitHub上 。