赞
踩
以前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
<?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=".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
<?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=".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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。