当前位置:网站首页>Navigation - are you sure you want to take a look at such an easy-to-use navigation framework?
Navigation - are you sure you want to take a look at such an easy-to-use navigation framework?
2022-07-07 14:26:00 【InfoQ】
Preface
Use Navigation What's the advantage ?
- Handle Fragment Business
- By default , Can correctly handle round-trip operations
- Provide standardized resources for animation and transformation
- Implement and handle deep links
- Including navigation interface mode , For example, drawer navigation bar and bottom navigation , We only need to complete a small amount of code
- Safe Args - Provides type safe navigation and data transfer between targets Gradle plug-in unit
- ViewModel Support - You can use ViewModel The scope of is limited to the navigation map , To share data related to the interface between the targets of the icon
How to use Navigation Well ?
Navigation
Navigation
- Navigation graph: One that contains all navigation related information
XML
resources
- NavHostFragment: A special kind
Fragment
, Container used to hold navigation content
- NavController: Manage application navigation objects , Realization
Fragment
Between the jump and other operations
First step : Add dependency
//project Of Navigation Dependency settings
dependencies {
// The latest stable version when the article is released :
def nav_version = "2.4.2"
// Use java Add the following two lines as the development language :
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"
}
//Compose edition :
implementation "androidx.navigation:navigation-compose:$nav_version"
The second step : Create a navigation map
res
New
Android Resource Directory
New Resource Directory
Directory name
navigation
Resource type
navigation
navigation
new
Navigation Resource File
File name

The third step : establish Fragment
Fragment
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="hello world" />
</FrameLayout>
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}
}
FirstFragement
SecondFragment
ThirdFragment
Navigation graph
nav_graph_main.xml
Design
Navigation Editor
Create new destination
Fragment
Finish
Fragment

Step four : take Fragment Drag into the panel and configure the jump
Navigation Editor
Fragment
Fragment
Fragment
FirstFragment
SecondFragment
ThirdFragment
FirstFragment

nav_graph_main.xml
Code
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/nav_graph_main"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="com.taxze.jetpack.navigation.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFragment2"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.taxze.jetpack.navigation.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" >
<action
android:id="@+id/action_secondFragment_to_thirdFragment2"
app:destination="@id/thirdFragment" />
</fragment>
<fragment
android:id="@+id/thirdFragment"
android:name="com.taxze.jetpack.navigation.ThirdFragment"
android:label="fragment_third"
tools:layout="@layout/fragment_third" >
<action
android:id="@+id/action_thirdFragment_to_firstFragment"
app:destination="@id/firstFragment" />
</fragment>
</navigation>
navigation
It's a label , adoptstartDestination
Configure the first page launched by default , The configuration here isfirstFragment
, We can change it manually in the codemainFragment
( The first one at startup Fragment), You can also clickFragment
, Click againAssign Start Destination
, You can also modifymainFragment

fragment
The label means this is a Fragment
action
Tags define the behavior of page jumps , Each line in the above figure ,destination
Define the target page for jump , You can also add animation during jump
Step five : Handle MainActivity
MainActivity
NavHostFragment
Navigation
Activity
Fragment
NavHostFragment
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment
Label underandroid:name
Is used to designateNavHostFragment
app:navGraph
Is used to specify the Navigation view
app:defaultNavHost=true
Every timeFragment
When switching , Save the click record in the stack , When you need to exit , After pressing the back key , You'll get it from the stack last timeFragment
Display . But in some cases , Such operation is not very friendly , But fortunately, we just need toapp:defaultNavHost=true
Change it toapp:defaultNavHost=false
Or delete this line . When it isfalse
Under the circumstances , No matter how you switchFragment
, Click the back button again and exit directlyapp
. Of course, we can also listen to its stack , So as to achieve , Click the back button once to return to the home page , Click the back button again to exitapp
.
MainActivity
onSupportNavigateUp
Activity
back
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp()
}
}
Step six : Handle Fragment Corresponding jump event of
Fragment
TextView
Fragment
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<Button
android:id="@+id/firstButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text=" Click to jump to the second fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/hello_first_fragment" />
</FrameLayout>
//secondFragment Add :
<Button
android:id="@+id/firstButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text=" Click to jump to the third fragment" />
//thirdFragment Add :
<Button
android:id="@+id/firstButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text=" Return to the first fragment" />
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.firstButton).apply {
setOnClickListener {
it.findNavController().navigate(R.id.action_firstFragment_to_secondFragment2)
}
}
}
}
//secondFragment Add :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.firstButton).apply {
setOnClickListener {
it.findNavController().navigate(R.id.action_secondFragment_to_thirdFragment2)
}
}
}
//thirdFragment Add :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.firstButton).apply {
setOnClickListener {
it.findNavController().navigate(R.id.action_thirdFragment_to_firstFragment)
}
}
}
R.id.action_firstFragment_to_secondFragment2
nav_graph_main.xml
action
<action
android:id="@+id/action_firstFragment_to_secondFragment2"
app:destination="@id/secondFragment" />

Jump animation & Custom animation
nav_graph_main.xml
Design
Fragment
Animations
enterAnim
Pick a Resoure
nav_default_enter_anim
exitAnim
nav_default_exit_anim
action
<fragment
android:id="@+id/firstFragment"
android:name="com.taxze.jetpack.navigation.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFragment2"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
/>
</fragment>

enterAnim
: Animation of the target page when jumping
exitAnim
: The original page animation when jumping
popEnterAnim
: Animation of the target page when backing back
popExitAnim
: Back to the original page animation
res
New
Android Resource File
New Resource File
File name
slide_from_left
Resource type
Animation
res
anim
// Left slide effect
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="-100%"
android:toXDelta="0%">
</translate>
</set>
// Right slide effect
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="0%"
android:toXDelta="100%">
</translate>
</set>
// Rotation effect
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="1000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0" />
<rotate
android:duration="1000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
</set>

How to pass data ?
nav_graph_main.xml
Design
Fragment
Attributes
Arguments
Action
Argument Default Values
userId
// Pseudo code , Do not directly cv
<fragment
...
>
...
<argument
android:name="userId"
android:defaultValue="1"
app:argType="integer" />
</fragment>
// By default arrow Action Pass the parameters set in
it.findNavController().navigate(R.id.action_firstFragment_to_secondFragment2)
Transfer data dynamically
// Pseudo code , Do not directly cv
view.findViewById<Button>(R.id.firstButton).setOnClickListener {
val bundle = Bundle()
bundle.putString("userId", "1")
val navController = it.findNavController()
navController.navigate(R.id.action_firstFragment_to_secondFragment2, bundle)
}
val tv = view.findViewById<TextView>(R.id.textView)
tv.text = arguments?.getString("userId")
stay Activity Use setGraph Switch between different Navigation
navigation
setGraph
Navigation
activity_main
fragment
app:navGraph
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"/>
setGraph
app:navGraph
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initNav(1)
}
private fun initNav(id: Int) {
var controller = Navigation.findNavController([email protected], R.id.nav_host_fragment)
if (id == 1) {
// Set the corresponding app:navGraph
controller.setGraph(R.navigation.first)
}
if (id == 2) {
controller.setGraph(R.navigation.second)
}
[email protected]()
}
}
Navigation
NavController
Navigation
Fragment
findNavController
Navigation
NavController
api
NavController
Navigation
Fragment
Fragment
Fragment
TabLayout
Navigation
How to get
NavController
What's the example? ?
// Pseudo code , Do not directly cv
//activity:
//Activity.findNavController(viewId: Int)
findNavController(R.id.nav_host_fragment).navigateUp()
//Fragment:
//Fragment.findNavController()
//View.findNavController()
findNavController().navigate(R.id.action_thirdFragment_to_firstFragment)
Navigation Common operations :
①popBackStack eject Fragment
oneFragment
secondFragment
thirdFragment
thirdFragment
secondFragment
popBackStack
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
.....
btn.setOnClickListener{
// eject Fragment
controller.popBackStack()
}
}
② Pop up to the specified Fragment
popBackStack
//xxxFragment Pop up to the specified Fragment.
// If the Boolean value of the second parameter is true It means that our parameter one Fragment Pop up together , It means that if it is false Just popBackStack To
//xxxFragment, If it is true, It's just xxxFragment stay popBackStack() once
controller.popBackStack(xxxFragment,true)
③navigateUp() Up Navigation
findNavController(R.id.nav_host_fragment).navigateUp()
navigateUp
Fragment
popBackStack
navigateUp
popBackStack
navigateUp
Navigation
Fragment
popBackStack()
navigateUp()
④ Add navigation monitoring
val listener: NavController.OnDestinationChangedListener =
object : OnDestinationChangedListener() {
fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
@Nullable arguments: Bundle?
) {
Log.d(TAG, "onDestinationChanged: id = ${destination.getId()}")
}
}
// Add listening
controller.addOnDestinationChangedListener(listener)
// Remove monitor
controller.removeOnDestinationChangedListener(listener)
⑤ Get the current navigation destination
getCurrentDestination
// obtain
val destination = controller.getCurrentDestination()
Log.d(TAG, "onCreate: NavigatorName = ${destination.getNavigatorName()}")
Log.d(TAG, "onCreate: id = ${destination.getId()}")
Log.d(TAG, "onCreate: Parent = ${destination.getParent()}")
⑥ Judge what is displayed on the current page Fragment Is it a goal Fragment
// Directly cv
fun <F : Fragment> isActiveFragment(fragmentClass: Class<F>): Boolean {
val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
navHostFragment.childFragmentManager.fragments.forEach {
if (fragmentClass.isAssignableFrom(it.javaClass)) {
return true
}
}
return false
}
Use Safe Args Make sure the type is safe
Safe Args
build.gradle
// Put it on plugins{} Before
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.4.2"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
app
module
build.gradle
Java
Java
Kotlin
Java
apply plugin: "androidx.navigation.safeargs"
Kotlin
Kotlin
apply plugin: "androidx.navigation.safeargs.kotlin"
android.useAndroidX=true
android.enableJetifier=true
// Pseudo code , Do not directly cv
<fragment
android:id="@+id/blankFragment"
android:name="com.taxze.jetpack.navigation.BlankFragment"
android:label="fragment_blank"
tools:layout="@layout/fragment_blank" >
<action
android:id="@+id/toSecond"
Modify here id
app:destination="@id/blankFragment2" />
</fragment>
BlankFragment
BlankFragment2
var action = BlankFragment.actionJump("111")
action.setParam("222")
adopt Navigation imitation WeChat Bottom jump

res
New
Android Resource File
New Resource File
File name
menu
Resource type
Menu
res
menu
item

menu.xml
Design
Item
id
title
icon

Fragment
Navigation
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Home" />
</FrameLayout>
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
}
nav_graph_main.xml
Fragment
id
menu.xml
id

activity_main
BottomNavigationView
drawable
selector_menu_text_color.xml
Item
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#818181" android:state_checked="false"/>
<item android:color="#45C01A" android:state_checked="true"/>
</selector>
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:itemIconTint="@drawable/selector_menu_text_color"
app:labelVisibilityMode="labeled"
app:itemTextColor="@drawable/selector_menu_text_color"
app:menu="@menu/menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Remove the title bar
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.activity_main)
val navController: NavController = Navigation.findNavController(this, R.id.nav_host_fragment)
val navigationView = findViewById<BottomNavigationView>(R.id.nav_view)
NavigationUI.setupWithNavController(navigationView, navController)
}
}
Git
Epilogue
About me
边栏推荐
- Parameter keywords final, flags, internal, mapping keywords internal
- Cesium 已知一点经纬度和距离求另一个点的经纬度
- 手里的闲钱是炒股票还是买理财产品好?
- LeetCode 648. 单词替换
- Substance painter notes: settings for multi display and multi-resolution displays
- Leetcode——344. Reverse string /541 Invert string ii/151 Reverse the word / Sword finger in the string offer 58 - ii Rotate string left
- 一文读懂数仓中的pg_stat
- Reading and understanding of eventbus source code
- SAKT方法部分介绍
- 【愚公系列】2022年7月 Go教学课程 005-变量
猜你喜欢
2022年13个UX/UI/UE最佳创意灵感网站
Equipment failure prediction machine failure early warning mechanical equipment vibration monitoring machine failure early warning CNC vibration wireless monitoring equipment abnormal early warning
一个简单LEGv8处理器的Verilog实现【四】【单周期实现基础知识及模块设计讲解】
LeetCode 648. 单词替换
Mrs offline data analysis: process OBS data through Flink job
Vmware 与主机之间传输文件
Selenium Library
【立体匹配论文阅读】【三】INTS
Vscode configuration uses pylint syntax checker
手把手教会:XML建模
随机推荐
ARM Cortex-A9,MCIMX6U7CVM08AD 处理器应用
Excellent open source system recommendation of ThinkPHP framework
The longest ascending subsequence model acwing 1014 Mountaineering
Common response status codes
ndk初学习(一)
Cascading update with Oracle trigger
今日睡眠质量记录78分
Leetcode——236. 二叉树的最近公共祖先
用例图
First choice for stock account opening, lowest Commission for stock trading account opening, is online account opening safe
c#通过frame 和 page 切换页面
Vmware 与主机之间传输文件
Leetcode - Sword finger offer 05 Replace spaces
最长上升子序列模型 AcWing 1014. 登山
Oracle non automatic submission solution
Leetcode——剑指 Offer 05. 替换空格
Data flow diagram, data dictionary
Cesium 已知一点经纬度和距离求另一个点的经纬度
XML文件的解析操作
Search engine interface