处理Android导航组件中的后退按钮

问题描述 投票:14回答:8

我想知道如何使用导航控制器正确处理系统后退按钮操作。在我的应用程序中,我有两个片段(例如片段1和片段2),我在fragment1中有一个目标为fragment2的动作。一切都很好,除了一件事 - 当用户按下fragment2中的系统后退按钮时,我想显示一个对话框(例如使用DialogFragment)来确认退出。实现此行为的最佳方法是什么?如果我在我的宿主片段中使用app:defaultNavHost="true",它会自动返回忽略我的规则。此外,这个组件是什么?

enter image description here

我应该使用“pop to”吗?

android navigation fragment android-jetpack
8个回答
24
投票

最新更新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:您的活动应该扩展AppCompatActivityor FragmentActivity并在Gradle文件中:

 implementation 'androidx.appcompat:appcompat:{lastVersion}'

11
投票

所以,我创建了一个界面

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()方法,在其他地方我只是弹回堆栈并关闭应用程序,如果后栈是空的。


5
投票

这里的解决方案应该做你想要的,但我认为这是一个糟糕的解决方案,因为它违背了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()
        }
    }
} 

4
投票

recommended方法是将OnBackPressedCallback添加到活动的OnBackPressedDispatcher

requireActivity().onBackPressedDispatcher.addCallback { 
    // handle back event
}

3
投票

派对有点晚了,但是最新版本的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


1
投票

2.1.0-alpha06

如果您只想在当前片段中处理backpress

requireActivity().onBackPressedDispatcher.addCallback(this@LoginFragment) {
    // handle back event
}

对于整个活动

requireActivity().onBackPressedDispatcher.addCallback() {
    // handle back event
}

0
投票

试试这个。我想这会对你有所帮助。

覆盖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()
}

0
投票

这是我的解决方案

使用androidx.appcompat.app.AppCompatActivity作为包含NavHostFragment片段的活动。

定义以下接口并在所有导航目标片段中实现它

interface InterceptionInterface {

    fun onNavigationUp(): Boolean
    fun onBackPressed(): Boolean
}

在你的活动中覆盖onSupportNavigateUponBackPressed

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
}

该解决方案具有以下优点:导航目的地片段在分离时不需要担心其侦听器的注销。


0
投票

我尝试了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()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.