1. 导包
// Java language implementation
implementation("androidx.navigation:navigation-fragment:2.3.5")
implementation("androidx.navigation:navigation-ui:2.3.5")
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:2.3.5")
implementation("androidx.navigation:navigation-ui-ktx:2.3.5")
// Feature module Support
implementation("androidx.navigation:navigation-dynamic-features-fragment:2.3.5")
// Testing Navigation
androidTestImplementation("androidx.navigation:navigation-testing:2.3.5")
// Jetpack Compose Integration
implementation("androidx.navigation:navigation-compose:2.4.0-alpha08")
2. 创建Activity和fragment
(1) 创建MainActivity和MessageActivity
package com.example.mvvmdemo.NavigationCourse.fragment
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.mvvmdemo.R
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nav_main)
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_config"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
package com.example.mvvmdemo.NavigationCourse.Activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.mvvmdemo.R
class MessageActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_message)
}
}
(2)创建Fragment
首先创建一个BaseFragment
package com.example.mvvmdemo.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
abstract class BaseFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView: View = inflater.inflate(getLayoutResId(), container, false)
//初始化View
initView(rootView)
return rootView
}
//子类需要使用时,重写即可
open fun initView(rootView: View) {
}
//抽象函数,子类必须继承 传入View
abstract fun getLayoutResId(): Int
}
其他Fragment继承BaseFragment
package com.example.mvvmdemo.NavigationCourse.fragment
import android.view.View
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import com.example.mvvmdemo.R
import com.example.mvvmdemo.base.BaseFragment
import kotlinx.android.synthetic.main.fragment_login.*
import kotlinx.android.synthetic.main.fragment_login.view.*
class LoginFragment : BaseFragment() {
override fun getLayoutResId(): Int {
return R.layout.fragment_login
}
override fun initView(rootView: View) {
super.initView(rootView)
rootView.toRegister.setOnClickListener {
val imagePair = Pair<View, String>(loginCover, "coverTn")
//可以传多个
val extras = FragmentNavigatorExtras(imagePair)
//跳转到注册页面
findNavController().navigate(
R.id.to_register_fragment,
null,
null,
extras
)
}
rootView.toForget.setOnClickListener {
//跳转到忘记密码页面
findNavController().navigate(R.id.to_forget_fragment)
}
rootView.toMessageActivity.setOnClickListener {
//跳转到messageActivity
findNavController().navigate(R.id.to_message_activity)
}
}
}
<?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">
<TextView
android:id="@+id/registerTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="login"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/toRegister"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转到注册页面"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.121" />
<Button
android:id="@+id/toForget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转到忘记密码页面"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.243" />
<Button
android:id="@+id/toMessageActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转到messageActivity"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.392" />
<ImageView
android:id="@+id/loginCover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:transitionName="coverTn"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.828" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?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">
<TextView
android:id="@+id/registerTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="register"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/registerCover"
android:layout_width="220dp"
android:layout_height="221dp"
android:transitionName="coverTn"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.109"
android:src="@mipmap/ic_launcher"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.mvvmdemo.NavigationCourse.fragment
import android.os.Bundle
import androidx.transition.TransitionInflater
import com.example.mvvmdemo.R
import com.example.mvvmdemo.base.BaseFragment
class RegisterFragment : BaseFragment() {
override fun getLayoutResId(): Int {
return R.layout.fragment_register
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(R.transition.shared_image)
}
}
<?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">
<TextView
android:id="@+id/registerTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="register"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/registerCover"
android:layout_width="220dp"
android:layout_height="221dp"
android:transitionName="coverTn"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.109"
android:src="@mipmap/ic_launcher"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.mvvmdemo.NavigationCourse.fragment
import com.example.mvvmdemo.R
import com.example.mvvmdemo.base.BaseFragment
class ForgetFragment : BaseFragment() {
override fun getLayoutResId(): Int {
return R.layout.fragment_forget
}
}
<?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">
<TextView
android:id="@+id/registerTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="forget"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. 跳转
创建nav_config.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"
android:id="@+id/nav_config"
app:startDestination="@id/login_fragment">
<fragment
android:id="@+id/login_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.LoginFragment">
<action
android:id="@+id/to_register_fragment"
app:destination="@+id/register_fragment"
/>
<action
android:id="@+id/to_forget_fragment"
app:destination="@+id/forget_fragment"
app:enterAnim="@anim/slid_from_right_to_left_in"
app:exitAnim="@anim/slid_from_right_to_left_out"
app:popEnterAnim="@anim/slid_from_left_to_right_in"
app:popExitAnim="@anim/slid_from_left_to_right_out" />
<action
android:id="@+id/to_message_activity"
app:destination="@+id/message_activity" />
</fragment>
<fragment
android:id="@+id/forget_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.ForgetFragment"></fragment>
<fragment
android:id="@+id/register_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.RegisterFragment"></fragment>
<activity
android:id="@+id/message_activity"
android:name="com.example.mvvmdemo.NavigationCourse.Activity.MessageActivity"></activity>
</navigation>
(1)Fragment之间的跳转
LoginFragment 跳转到 RegisterFragment
在nav_config.xml的login_fragment的<fragment>中设置action标签,设置id,destination(想要跳转的fragment),在LoginFragment中的toRegister按钮设置点击事件,实现跳转
<fragment
android:id="@+id/login_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.LoginFragment">
<action
android:id="@+id/to_register_fragment"
app:destination="@+id/register_fragment"/>
</fragment>
<fragment
android:id="@+id/register_fragment
android:name="com.example.mvvmdemo.NavigationCourse.fragment.RegisterFragment"></fragment>
rootView.toRegister.setOnClickListener {
//跳转到注册页面
findNavController().navigate(R.id.to_register_fragment)
}
(2)Activity之间的跳转
与Fragment跳转类似
<fragment
android:id="@+id/login_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.LoginFragment">
<action
android:id="@+id/to_message_activity"
app:destination="@+id/message_activity" />
</fragment>
<activity
android:id="@+id/message_activity"
android:name="com.example.mvvmdemo.NavigationCourse.Activity.MessageActivity">
</activity>
rootView.toMessageActivity.setOnClickListener {
//跳转到messageActivity
findNavController().navigate(R.id.to_message_activity)
}
(3)跳转之间的动画实现
创建slid_from_left_to_right_in.xml ,
slid_from_left_to_right_out.xml ,
slid_from_right_to_left_in.xml,
slid_from_right_to_left_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="400"
android:fromXDelta="-100%p"
android:toXDelta="0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="400"
android:fromXDelta="0"
android:toXDelta="100%p" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="400"
android:fromXDelta="100%p"
android:toXDelta="0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="400"
android:fromXDelta="0"
android:toXDelta="-100%p"/>
</set>
然后添加在相应的action后面
<action
android:id="@+id/to_forget_fragment"
app:destination="@+id/forget_fragment"
app:enterAnim="@anim/slid_from_right_to_left_in"
app:exitAnim="@anim/slid_from_right_to_left_out"
app:popEnterAnim="@anim/slid_from_left_to_right_in"
app:popExitAnim="@anim/slid_from_left_to_right_out" />
还有就是Activity的全局配置如下的动画跳转
在themes.xml文件中配置
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MVVMDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="android:windowAnimationStyle">@style/activityAnimation</item>
</style>
<style name="activityAnimation" parent="@android:style/Animation">
<item name="android:activityOpenEnterAnimation">@anim/slid_from_right_to_left_in</item>
<item name="android:activityOpenExitAnimation">@anim/slid_from_right_to_left_out</item>
<item name="android:activityCloseEnterAnimation">@anim/slid_from_left_to_right_in</item>
<item name="android:activityCloseExitAnimation">@anim/slid_from_left_to_right_out</item>
</style>
</resources>
在AndroidManifest.xml文件中添加,不过一般创建项目时就已经添加了
android:theme="@style/Theme.MVVMDemo">
4. 返回
(1)返回到上一页
//一般用于左上角的返回箭头back
findNavController().navigateUp()
//类似手机底部返回按钮,退栈
findNavController().popBackStack()
在RegisterFragment的布局文件中添加一个按钮
<Button
android:id="@+id/back2Login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="返回"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="482dp"
tools:ignore="MissingConstraints" />
找到控件设置事件
override fun initView(rootView: View) {
rootView.back2Login.setOnClickListener {
findNavController().navigateUp()
// findNavController().popBackStack()
}
(2)跳页返回
从loginFragment(登录) 到 registerFragment(注册) 到 verifyFragment (认证)
直接从verifyFragment 到 loginFragment
//跳页的实现
app:popUpTo="@id/to_login_fragment"
app:popUpToInclusive="true"
<?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"
android:id="@+id/nav_config"
app:startDestination="@id/login_fragment">
<fragment
android:id="@+id/login_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.LoginFragment">
<action
android:id="@+id/to_register_fragment"
app:destination="@+id/register_fragment" />
</fragment>
<fragment
android:id="@+id/register_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.RegisterFragment">
<action
android:id="@+id/to_verify_fragment"
app:destination="@id/avatar_verify_fragment" />
</fragment>
<fragment
android:id="@+id/avatar_verify_fragment"
android:name="com.example.mvvmdemo.NavigationCourse.fragment.AvatarVerifyFragment">
<action
android:id="@+id/to_login_fragment"
app:destination="@id/login_fragment"
app:popUpTo="@id/login_fragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
5. Fragment实现元素共享
在loginFragment中创建布局
<Button
android:id="@+id/toRegister"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转到注册页面" />
<ImageView
android:id="@+id/loginCover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:transitionName="coverTn" />
按钮注册响应事件
rootView.toRegister.setOnClickListener {
val imagePair = Pair<View, String>(loginCover, "coverTn")
//可以传多个
val extras = FragmentNavigatorExtras(imagePair)
//跳转到注册页面
findNavController().navigate(
R.id.to_register_fragment,
null,
null,
extras
)
}
在registerFragment创建布局
<ImageView
android:id="@+id/registerCover"
android:layout_width="220dp"
android:layout_height="221dp"
android:transitionName="coverTn"
android:src="@mipmap/ic_launcher"
/>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(R.transition.shared_image)
}
在res中创建transition包创建share_image.xml文件 设置时间
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<autoTransition android:duration="400" />
</transitionSet>
6. Activity实现元素共享
loginFragment给相关按钮设置事件
rootView.toMessageActivity.setOnClickListener {
//跳转到messageActivity
val pair = androidx.core.util.Pair<View, String>(loginCover, "coverTn")
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), pair)
val extras: ActivityNavigator.Extras = ActivityNavigator.Extras.Builder()
.setActivityOptions(options)
.build()
findNavController().navigate(
R.id.to_message_activity, null, null, extras
)
}
messageActivity布局 注意:android:transitionName="coverTn" transitionName要一样
<ImageView
android:id="@+id/messageCover"
android:layout_width="219dp"
android:layout_height="204dp"
android:src="@mipmap/ic_launcher"
android:transitionName="coverTn"/>
7. 传参
(1)Fragment传参
创建Bundle,传入navigate()的args参数
rootView.toRegister.setOnClickListener {
//图像
val imagePair = Pair<View, String>(loginCover, "coverTn")
//账号
val userNamePair = Pair<View, String>(userNameEt, "userNameTn")
val extras = FragmentNavigatorExtras(imagePair, userNamePair)
//数据
val bundle = Bundle()
bundle.putString("userName", userNameEt.text.toString())
//跳转到注册页面
findNavController().navigate(
R.id.to_register_fragment,
//传入args
bundle,
null,
extras
)
}
在另一个Fragment中通过arguments获取
注意:在布局中保证transitionName属性的值一致
var userName: String? = arguments?.getString("userName")
rootView.registerTv.setText(userName)
(2)Activity传参
rootView.toMessageActivity.setOnClickListener {
//跳转到messageActivity
//两个参数
//图像
val imagePair = androidx.core.util.Pair<View, String>(loginCover, "coverTn")
//账号
val userNamePair = androidx.core.util.Pair<View, String>(userNameEt, "userNameTn")
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
requireActivity(),
imagePair,
userNamePair
)
//数据
val bundle = Bundle()
bundle.putString("userName", userNameEt.text.toString())
val extras: ActivityNavigator.Extras = ActivityNavigator.Extras.Builder()
.setActivityOptions(options)
.build()
findNavController().navigate(
R.id.to_message_activity, bundle, null, extras
)
}
在Activity中,通过Intent获取
private fun initView() {
val userName = intent.getStringExtra("userName")
messageTv.text = userName
}
网友评论