以前Fragment、View里面没有返回事件,需要自己处理,目前官方提供了OnBackPressedDispatcher
对事件进行拦截处理,这个类也主要是处理这个问题
注意事项:
OnBackPressedDispatcher
并不是对onBackPressed()
的替换,只是对它的补充,最终返回的话还是要使用onBackPressed()
代码使用如下
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This callback will only be called when MyFragment is at least Started.
requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.d("YM", "handleOnBackPressed(): name=${tag}")
}
})
// The callback can be enabled or disabled here or in the lambda
}
...
}
OnBackPressedDispatcher
通过定义的先后顺序来进行触发。他们触发的先后顺序为倒叙方式。例如您按顺序添加了三个名为 one
、two
和 three
的回调,则它们的调用顺序分别为 three
、two
和 one
。
回调遵循责任链模式。责任链中的每个回调仅在前面的回调处于未启用状态时调用。这意味着,在前面的示例中,仅当 three
回调处于未启用状态时,系统才会调用 two
回调。仅当 two
回调处于未启用状态时,系统才会调用 one
回调,以此类推。
回调状态可以通过OnBackPressedDispatcher::setEnabled(boolean enabled)
动态启用。
需要注意的是官方推荐使用OnBackPressedCallback
来替换Activity::onBackPressed()
。但是有这么一个情况:
因为OnBackPressedDispatcher
最终是在Lifecycle.Event.ON_START
事件收到后进行注册的。表现在生命周期中为onStart()
函数。但是据Android10.0版本的手机测试,其Activity::onStart()
执行会在Fragment::onStart()
之后。因此,在OnBackPressedDispatcher
栈中的顺序中Activity
会为最后。所以这样在Activity
和Fragment
嵌套使用的时候,会只能收到Activity
的返回拦截事件。除非Fragment
的添加是在Activity::onStart()
函数之后进行添加的。但是在Android8.0测试则是先执行Activity,后执行Fragment。这样的话,程序就很正常了
这里定义一个页面来演示下该功能,每次点击返回键就返回移除该Fragment
。整体页面如下:
这个页面由两个Fragment
组成。代码如下:
fragment_call_back.xml
<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=".CallBackFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
FrameLayout>
CallBackFragment.kt
package com.hello.world
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
class CallBackFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.d("YM", "handleOnBackPressed(): name=${tag}")
closeFragment()
}
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_call_back, container, false)
}
private fun closeFragment() = requireActivity().supportFragmentManager.beginTransaction()
.remove(this).commit()
}
activity_call_back.xml
<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=".CallBackActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/call_back_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:tag="fragment1"
android:name="com.hello.world.CallBackFragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/call_back_container_2"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/call_back_container_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:tag="fragment2"
android:name="com.hello.world.CallBackFragment"
app:layout_constraintTop_toBottomOf="@+id/call_back_container"
app:layout_constraintBottom_toBottomOf="parent"/>
androidx.constraintlayout.widget.ConstraintLayout>
CallBackActivity.kt
class CallBackActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_call_back)
}
}
假设有一个场景,一个文件列表管理功能,点击列表的条目会显示该条目中的文件列表,按返回键会返回到父文件夹到目录,如果为顶层目录则直接返回到上一个页面。具体实现逻辑一个Fragment,该Fragment可以嵌套进其他页面。所以所有返回逻辑都在Fragment中处理。关键代码如下:
class FileManagerToolsFragment : BaseFragment() {
private val backPressedCallback: OnBackPressedCallback by lazy {
object : OnBackPressedCallback(!viewModel.isRootFile()) {
override fun handleOnBackPressed() {
lifecycleScope.launch(Dispatchers.IO) {
if (!viewModel.isRootFile()) {
viewModel.backParent()
}
isEnabled = !viewModel.isRootFile()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This callback will only be called when Fragment is at least Started.
activity?.onBackPressedDispatcher?.addCallback(
this, backPressedCallback
)
}
private fun initRecyclerView() {
adapter.onItemClickListener = { fileEntity ->
if (fileEntity.isDirectory) {
lifecycleScope.launch(Dispatchers.IO) {
viewModel.loadFileList(fileEntity.path)
backPressedCallback.isEnabled = !viewModel.isRootFile()
}
}
}
}
}
提供自定义返回导航
https://developer.android.com/guide/navigation/navigation-custom-back?hl=zh-cn
Android | Jetpack 处理回退事件的新姿势 —— OnBackPressedDispatcher
https://www.jianshu.com/p/75bc5108628f