我想知道如何使用导航控制器正确处理系统后退按钮操作。在我的应用程序中,我有两个片段(例如片段1和片段2),我在fragment1中有一个目标为fragment2的动作。一切都很好,除了一件事 - 当用户按下fragment2中的系统后退按钮时,我想显示一个对话框(例如使用DialogFragment)来确认退出。实现此行为的最佳方法是什么?如果我在我的宿主片段中使用app:defaultNavHost="true"
,它会自动返回忽略我的规则。此外,这个组件是什么?
我应该使用“pop to”吗?
最新更新2019年4月25日
新版本androidx.activity ver. 1.0.0-alpha07带来了一些变化
android官方指南中的更多解释:Provide custom back navigation
例:
public class MyFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This callback will only be called when MyFragment is at least Started.
OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
@Override
public boolean handleOnBackPressed() {
// Handle the back button event
}
});
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
// The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}
旧的更新
UPD:2019年4月3日
现在它简化了。更多信息here
例:
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), this);
@Override
public boolean handleOnBackPressed() {
//Do your job here
//use next line if you just need navigate up
//NavHostFragment.findNavController(this).navigateUp();
//Log.e(getClass().getSimpleName(), "handleOnBackPressed");
return true;
}
不推荐使用(自版本1.0.0-alpha06 2019年3月3日):
从this开始,它可以在你的片段中使用JetPack实现OnBackPressedCallback
实现并将其添加到activity:getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);
您的片段应如下所示:
public MyFragment extends Fragment implements OnBackPressedCallback {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);
}
@Override
public boolean handleOnBackPressed() {
//Do your job here
//use next line if you just need navigate up
//NavHostFragment.findNavController(this).navigateUp();
//Log.e(getClass().getSimpleName(), "handleOnBackPressed");
return true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().removeOnBackPressedCallback(this);
}
}
UPD:您的活动应该扩展AppCompatActivity
or FragmentActivity
并在Gradle文件中:
implementation 'androidx.appcompat:appcompat:{lastVersion}'
所以,我创建了一个界面
public interface OnBackPressedListener {
void onBackPressed();
}
并由需要处理后退按钮的所有片段实现它。在主要活动中,我凌驾于onBackPressed()
方法:
@Override
public void onBackPressed() {
final Fragment currentFragment = mNavHostFragment.getChildFragmentManager().getFragments().get(0);
final NavController controller = Navigation.findNavController(this, R.id.nav_host_fragment);
if (currentFragment instanceof OnBackPressedListener)
((OnBackPressedListener) currentFragment).onBackPressed();
else if (!controller.popBackStack())
finish();
}
所以,如果我的导航主机的顶部片段实现了OnBackPressedListener
接口,我调用它的onBackPressed()
方法,在其他地方我只是弹回堆栈并关闭应用程序,如果后栈是空的。
这里的解决方案应该做你想要的,但我认为这是一个糟糕的解决方案,因为它违背了Android导航组件的想法(让android处理导航)。
在您的活动中覆盖“onBackPressed”
override fun onBackPressed() {
when(NavHostFragment.findNavController(nav_host_fragment).currentDestination.id) {
R.id.fragment2-> {
val dialog=AlertDialog.Builder(this).setMessage("Hello").setPositiveButton("Ok", DialogInterface.OnClickListener { dialogInterface, i ->
finish()
}).show()
}
else -> {
super.onBackPressed()
}
}
}
recommended方法是将OnBackPressedCallback
添加到活动的OnBackPressedDispatcher
。
requireActivity().onBackPressedDispatcher.addCallback {
// handle back event
}
派对有点晚了,但是最新版本的Navigation Component 1.0.0-alpha09,现在我们有了一个AppBarConfiguration.OnNavigateUpListener。
有关更多信息,请参阅以下链接:https://developer.android.com/reference/androidx/navigation/ui/AppBarConfiguration.OnNavigateUpListener https://developer.android.com/jetpack/docs/release-notes
在2.1.0-alpha06
如果您只想在当前片段中处理backpress
requireActivity().onBackPressedDispatcher.addCallback(this@LoginFragment) {
// handle back event
}
对于整个活动
requireActivity().onBackPressedDispatcher.addCallback() {
// handle back event
}
试试这个。我想这会对你有所帮助。
覆盖fun onBackPressed(){when(mNavController.getCurrentDestination()!!。getId()){
R.id.loginFragment -> {
onWarningAlertDialog(this, "Alert", "Do you want to close this application ?")
}
R.id.registerFragment -> {
super.onBackPressed()
}
}
}
私有乐趣onWarningAlertDialog(mainActivity:MainActivity,s:String,s1:String){
val dialogBuilder = AlertDialog.Builder(this) dialogBuilder.setMessage(/*""*/s1) .setCancelable(false) .setPositiveButton("Proceed", DialogInterface.OnClickListener { dialog, id -> finish() }) .setNegativeButton("Cancel", DialogInterface.OnClickListener { dialog, id -> dialog.cancel() }) // create dialog box val alert = dialogBuilder.create() // set title for alert dialog box alert.setTitle("AlertDialogExample") // show alert dialog alert.show() }
这是我的解决方案
使用androidx.appcompat.app.AppCompatActivity
作为包含NavHostFragment
片段的活动。
定义以下接口并在所有导航目标片段中实现它
interface InterceptionInterface {
fun onNavigationUp(): Boolean
fun onBackPressed(): Boolean
}
在你的活动中覆盖onSupportNavigateUp
和onBackPressed
:
override fun onSupportNavigateUp(): Boolean {
return getCurrentNavDest().onNavigationUp() || navigation_host_fragment.findNavController().navigateUp()
}
override fun onBackPressed() {
if (!getCurrentNavDest().onBackPressed()){
super.onBackPressed()
}
}
private fun getCurrentNavDest(): InterceptionInterface {
val currentFragment = navigation_host_fragment.childFragmentManager.primaryNavigationFragment as InterceptionInterface
return currentFragment
}
该解决方案具有以下优点:导航目的地片段在分离时不需要担心其侦听器的注销。
我尝试了Jurij Pitulja解决方案,但我只是无法找到getOnBackPressedDispatcher或addOnBackPressedCallback也使用Kiryl Tkach的解决方案无法找到当前片段,所以这是我的:
interface OnBackPressedListener {
fun onBackPressed(): Boolean
}
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
val currentFragment = navHostFragment?.childFragmentManager!!.fragments[0]
if (currentFragment !is OnBackPressedListener || !(currentFragment as OnBackPressedListener).onBackPressed()) super.onBackPressed()
通过这种方式,您可以在片段中决定活动是否应该控制背压。
或者,您有所有活动的BaseActivity,您可以这样实现
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
if (navHostFragment != null){
val currentFragment = navHostFragment.childFragmentManager.fragments[0]
if (currentFragment !is AuthContract.OnBackPressedListener ||
!(currentFragment as AuthContract.OnBackPressedListener).onBackPressed()) super.onBackPressed()
} else {
super.onBackPressed()
}
}