当前位置:网站首页>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
边栏推荐
- Verilog implementation of a simple legv8 processor [4] [explanation of basic knowledge and module design of single cycle implementation]
- 数据流图,数据字典
- The reason why data truncated for column 'xxx' at row 1 appears in the MySQL import file
- Million data document access of course design
- Multi merchant mall system function disassembly lecture 01 - Product Architecture
- NDK beginner's study (1)
- Transferring files between VMware and host
- wpf dataGrid 实现单行某个数据变化 ui 界面随之响应
- Reverse non return to zero code, Manchester code and differential Manchester code of common digital signal coding
- OAuth 2.0 + JWT protect API security
猜你喜欢
The longest ascending subsequence model acwing 1012 Sister cities
UML sequence diagram (sequence diagram)
LeetCode每日一题(636. Exclusive Time of Functions)
常用数字信号编码之反向不归零码码、曼彻斯特编码、差分曼彻斯特编码
docker部署oracle
Data flow diagram, data dictionary
Assign a dynamic value to the background color of DataGrid through ivalueconverter
云上“视界” 创新无限 | 2022阿里云直播峰会正式上线
手把手教会:XML建模
UML 顺序图(时序图)
随机推荐
Data flow diagram, data dictionary
Parameter keywords final, flags, internal, mapping keywords internal
Vmware 与主机之间传输文件
Is the spare money in your hand better to fry stocks or buy financial products?
IP address home location query
The reason why data truncated for column 'xxx' at row 1 appears in the MySQL import file
NDK beginner's study (1)
【立体匹配论文阅读】【三】INTS
Seven propagation behaviors of transactions
UML state diagram
CSMA/CD 载波监听多点接入/碰撞检测协议
Excellent open source system recommendation of ThinkPHP framework
Leetcode——236. The nearest common ancestor of binary tree
[network security] SQL injection syntax summary
Démontage de la fonction du système multi - Merchant Mall 01 - architecture du produit
IP and long integer interchange
手把手教会:XML建模
FC连接数据库,一定要使用自定义域名才能在外面访问吗?
Mrs offline data analysis: process OBS data through Flink job
低代码平台中的数据连接方式(下)