当前位置:网站首页>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
边栏推荐
- 杭电oj2054 A == B ? ???
- Parameter keywords final, flags, internal, mapping keywords internal
- VSCode 配置使用 PyLint 语法检查器
- 请问,PTS对数据库压测有好方案么?
- Data flow diagram, data dictionary
- The meaning of variables starting with underscores in PHP
- Wired network IP address of VMware shared host
- 多商戶商城系統功能拆解01講-產品架構
- 【服务器数据恢复】某品牌StorageWorks服务器raid数据恢复案例
- Leetcode - Sword finger offer 05 Replace spaces
猜你喜欢
随机推荐
通过 iValueConverter 给datagrid 的背景颜色 动态赋值
OAuth 2.0 + JWT protect API security
【愚公系列】2022年7月 Go教学课程 005-变量
Notes de l'imprimante substance: paramètres pour les affichages Multi - écrans et multi - Résolutions
c#利用 TCP 协议建立连接
Million data document access of course design
The longest ascending subsequence model acwing 1014 Mountaineering
多商戶商城系統功能拆解01講-產品架構
Navigation — 这么好用的导航框架你确定不来看看?
Bashrc and profile
docker部署oracle
Laravel form builder uses
XML文件的解析操作
Attribute keywords aliases, calculated, cardinality, ClientName
【网络安全】sql注入语法汇总
The difference between memory overflow and memory leak
Excellent open source system recommendation of ThinkPHP framework
Vmware共享主机的有线网络IP地址
Is the compass stock software reliable? Is it safe to trade stocks?
Substance Painter笔记:多显示器且多分辨率显示器时的设置