【问题标题】:Handling back button in Android Navigation Component处理 Android 导航组件中的后退按钮
【发布时间】:2018-12-05 04:58:11
【问题描述】:

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

我应该使用“pop to”吗?

【问题讨论】:

  • 使用“Pop to”,您可以定义点击后退/向上按钮时的去向(目的地)。
  • @Alex 那么,如果设置为 none,它应该如何响应返回按钮?
  • 当它设置为“none”时,默认行为,用户将被导航到上一个目的地(片段1)
  • @Alex,好的,有没有办法通过第二个片段处理后退按钮?

标签: android navigation fragment android-jetpack


【解决方案1】:

最新更新 - 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 void handleOnBackPressed() {
                // Handle the back button event
            }
        };
        requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

        // The callback can be enabled or disabled here or in handleOnBackPressed()
    }
    ...
}

旧更新

更新日期: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 年 4 月 3 日):

由于this,它可以在你的片段中使用JetPack实现OnBackPressedCallback 并将其添加到活动中: 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);
    }
}

统一更新: 您的活动应扩展 AppCompatActivityFragmentActivity 并在 Gradle 文件中:

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

【讨论】:

  • Jetpack 中还存在这个吗?
  • 如果您的 AppCompat 活动不是来自支持库而是来自 androidX 库,那么是的,您应该
  • 使用 AndroidX AppCompatActivity 和 Androidx Fragment 我也不认为这是一个可用的选项
  • OnBackPressedCallback 现在是一个抽象类!所以这个解决方案对我不起作用。
  • 在做了一些逻辑后如何进行正常的后退操作——handleIOnBackPressed()?
【解决方案2】:

所以,我创建了一个界面

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();

}

所以,如果我的 Navigation 主机的顶部片段实现了OnBackPressedListener 接口,我调用它的onBackPressed() 方法,在其他地方我只是简单地弹出堆栈并在返回堆栈为空时关闭应用程序。

【讨论】:

  • 一个不错的解决方案。虽然我一直想知道,如果导航组件缺少这种开箱即用的功能,那么它的意义何在。
  • 检查@Jurij Pitulja 的答案是解决问题的推荐方法
  • 为什么我们需要一个单独的接口而不是使用 Jetpack 中的接口?
  • @IgorGanapolsky 在回答此问题时,Jetpack 中没有接口。
  • @KirylTkach mNavHostFragment 的声明是什么?
【解决方案3】:

对于寻找 Kotlin 实现的任何人,请参见下文。

请注意,OnBackPressedCallback 似乎仅适用于为内置软件/硬件后退按钮提供自定义后退行为,而不是操作栏/工具栏中的后退箭头按钮/主页作为向上按钮。为了还覆盖操作栏/工具栏后退按钮的行为,我提供了适合我的解决方案。如果这是一个错误,或者您知道针对这种情况的更好解决方案,请发表评论。

build.gradle

...
implementation "androidx.appcompat:appcompat:1.1.0-rc01"
implementation "androidx.navigation:navigation-fragment-ktx:2.0.0"
implementation "androidx.navigation:navigation-ui-ktx:2.0.0"
...

MainActivity.kt

...
import androidx.appcompat.app.AppCompatActivity
...

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_main)

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)

        // This line is only necessary if using the default action bar.
        setupActionBarWithNavController(navController, appBarConfiguration)

        // This remaining block is only necessary if using a Toolbar from your layout.
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        toolbar.setupWithNavController(navController, appBarConfiguration)
        // This will handle back actions initiated by the the back arrow 
        // at the start of the toolbar.
        toolbar.setNavigationOnClickListener {
            // Handle the back button event and return to override 
            // the default behavior the same way as the OnBackPressedCallback.
            // TODO(reason: handle custom back behavior here if desired.)

            // If no custom behavior was handled perform the default action.
            navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
        }
    }

    /**
     * If using the default action bar this must be overridden.
     * This will handle back actions initiated by the the back arrow 
     * at the start of the action bar.
     */
    override fun onSupportNavigateUp(): Boolean {
        // Handle the back button event and return true to override 
        // the default behavior the same way as the OnBackPressedCallback.
        // TODO(reason: handle custom back behavior here if desired.)

        // If no custom behavior was handled perform the default action.
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
}

MyFragment.kt

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // Handle the back button event
            }
        }
        requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback)
    }
}

官方文档可以在https://developer.android.com/guide/navigation/navigation-custom-back查看

【讨论】:

  • 这是最好的答案。您应该能够处理来自工具栏的后按和正常的后按。竖起大拇指
  • 这对我帮助很大!我的工具栏后退按钮没有触发OnBackPressedCallback,但在添加toolbar.setNavigationOnClickListener { onBackPressed() } 之后它起作用了,因此现在硬件返回和工具栏返回工作相同。感谢您清晰详细的回答!
  • 这是正确且最合乎逻辑的解决方案。一定会经得起时间的考验。
  • 我只使用了 Fragment back override 的代码,效果很好!
【解决方案4】:

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

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

【讨论】:

  • 如果回调的所有者也像这样.addCallback(viewLifecycleOwner) {}传递更好,否则即使片段被销毁,您也会继续接收回调。
【解决方案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()
        }
    }
} 

【讨论】:

  • 感谢您的解决方案,但在我决定切换到导航组件之前,我已经使用了它。似乎为时过早:(
  • 现在还为时过早。我实现了一个类似的 hackey 解决方案来处理 appbar 向上导航
【解决方案6】:

我在主要活动中这样写,

override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
    }   

【讨论】:

    【解决方案7】:

    2.1.0-alpha06

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

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

    对于整个活动

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

    【讨论】:

      【解决方案8】:

      更新 21 年 4 月 22 日

      我正在更新我的答案以展示推荐方法的示例,该示例也是上面的the accepted answer

      class MyFragment : Fragment() {
      
          ...
          
          private val backPressedDispatcher = object : OnBackPressedCallback(true) {
              override fun handleOnBackPressed() {
                  // Redirect to our own function
                  this@MyFragment.onBackPressed()
              }
          }
      
          override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
              ...
      
              setHasOptionsMenu(true) //Set this to true in order to trigger callbacks to Fragment#onOptionsItemSelected
      
              (requireActivity() as AppCompatActivity).apply {
                  // Redirect system "Back" press to our dispatcher
                  onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressedDispatcher)
      
                  // Set toolbar if it is in Fragment's layout. If you have a global toolbar that lives in Activity layout, then you don't need this line.
                  setSupportActionBar(view.findViewById(R.id.toolbar))
      
                  // Setup action bar to work with NavController
                  setupActionBarWithNavController(findNavController())
              }
          }
      
          override fun onOptionsItemSelected(item: MenuItem): Boolean {
              return if (item.itemId == android.R.id.home) {
                  // Redirect "Up/Home" button clicks to our own function
                  this@MyFragment.onBackPressed()
                  true
              } else {
                  super.onOptionsItemSelected(item)
              }
          }
      
          private fun onBackPressed() {
              // Work your magic! Show dialog etc.
          }
      
          override fun onDestroyView() {
              // It is optional to remove since our dispatcher is lifecycle-aware. But it wouldn't hurt to just remove it to be on the safe side.
              backPressedDispatcher.remove() 
              super.onDestroyView()
          }
      
       }
      

      原答案 2019 年 1 月 3 日

      聚会有点晚了,但随着导航组件 1.0.0-alpha09 的最新版本,现在我们有了 AppBarConfiguration.OnNavigateUpListener。

      有关详细信息,请参阅以下链接: https://developer.android.com/reference/androidx/navigation/ui/AppBarConfiguration.OnNavigateUpListener https://developer.android.com/jetpack/docs/release-notes

      【讨论】:

      • 感谢您向我展示发行说明!发现 android:menuCategory="secondary" 避免弹出回栈!
      • 对我来说,这仅适用于工具栏,但不适用于后退按钮
      • 同样的^适用于工具栏,如何处理后退按钮?
      • @GauriGadkari 感谢您的反馈!不幸的是,您是对的,我的原始答案不包括处理系统后压机。所以我用推荐方法的示例更新了我的答案。另请参阅已接受的答案。
      【解决方案9】:

      这是 2 行代码可以监听回压,来自片段,[TESTED and WORKING]

        requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
              @Override
              public void handleOnBackPressed() {
      
                  //setEnabled(false); // call this to disable listener
                  //remove(); // call to remove listener
                  //Toast.makeText(getContext(), "Listing for back press from this fragment", Toast.LENGTH_SHORT).show();
           }
      

      【讨论】:

        【解决方案10】:

        推荐的方法对我有用,但在更新我的库后implementation 'androidx.appcompat:appcompat:1.1.0'

        如下实现

         val onBackPressedCallback = object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    // Handle the back button event
                }
            }
            requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
        

        使用 Kotlin

        【讨论】:

          【解决方案11】:

          您可以使用 OnBackPressedDispatcher 提供自定义的后退导航

          class MyFragment : Fragment() {
          
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
          
              // This callback will only be called when MyFragment is at least Started.
              val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
                  // Handle the back button event
          // and if you want to need navigate up
          //NavHostFragment.findNavController(this).navigateUp()
              }
          
              // The callback can be enabled or disabled here or in the lambda
          }
          }
          

          更多解释见android官方指南:https://developer.android.com/guide/navigation/navigation-custom-back

          【讨论】:

            【解决方案12】:

            如果您使用片段或将其添加到按钮单击侦听器中,请使用此选项。这对我有用。

            requireActivity().onBackPressed()
            

            当 Activity 检测到用户按下返回键时调用。 getOnBackPressedDispatcher() OnBackPressedDispatcher} 将有机会在调用 android.app.Activity#onBackPressed()} 的默认行为之前处理后退按钮。

            【讨论】:

              【解决方案13】:

              FragmentExtensions.kt

              fun Fragment.onBackPressedCustomAction(action: () -> Unit) {
                requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
                  override
                  fun handleOnBackPressed() {
                    action()
                  }
                })
              }
              

              YourPrettyFragment.kt

              onBackPressedCustomAction {
                // Your custom action here
              }
              

              【讨论】:

                【解决方案14】:

                只需添加这些行

                     override fun onBackPressed() {
                            if(navController.popBackStack().not()) {
                            //Last fragment: Do your operation here 
                            finish()
                   }
                

                navController.popBackStack() 如果这不是您的最后一个片段,则只会弹出您的片段

                【讨论】:

                  【解决方案15】:

                  如果您使用导航组件,请在 onCreateView() 方法中遵循以下代码(在此示例中,我只想通过此片段关闭我的应用程序)

                   OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
                          @Override
                          public void handleOnBackPressed() {
                              new AlertDialog.Builder(Objects.requireNonNull(getActivity()))
                                      .setIcon(R.drawable.icon_01)
                                      .setTitle(getResources().getString(R.string.close_app_title))
                                      .setMessage(getResources().getString(R.string.close_app_message))
                                      .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                                          @Override
                                          public void onClick(DialogInterface dialog, int which) {
                                              getActivity().finish();
                                          }
                                      })
                                      .setNegativeButton(R.string.no, null)
                                      .show();
                          }
                      };
                      requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
                  

                  【讨论】:

                    【解决方案16】:

                    使用导航组件 这对我有好处:

                    Navigation.findNavController(requireView()).popBackStack()
                    

                    android documentation

                    【讨论】:

                    • 这不允许自定义实现。
                    【解决方案17】:

                    Kotlin 答案

                    使用 popBackStack() 示例:

                    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
                        super.onViewCreated(view, savedInstanceState)
                    
                        mButton.setOnClickListener {
                            Navigation.findNavController(view).popBackStack() // You need this line.
                        }
                    }
                    

                    【讨论】:

                    • 什么是mBackButton
                    • @Zain 这是普通的按钮和点击监听功能。
                    • 但是他们需要处理点击back button不是普通按钮的回调
                    • 你可以使用你想要的按钮。
                    【解决方案18】:

                    如果您在应用中使用 BaseFragment,那么您可以将 onBackPressedDispatcher 添加到您的基础片段中。

                    //Make a BaseFragment for all your fragments
                    abstract class BaseFragment : Fragment() {
                    
                    private lateinit var callback: OnBackPressedCallback
                    
                    /**
                     * SetBackButtonDispatcher in OnCreate
                     */
                    
                    override fun onCreate(savedInstanceState: Bundle?) {
                        super.onCreate(savedInstanceState)
                        setBackButtonDispatcher()
                    }
                    
                    /**
                     * Adding BackButtonDispatcher callback to activity
                     */
                    private fun setBackButtonDispatcher() {
                        callback = object : OnBackPressedCallback(true) {
                            override fun handleOnBackPressed() {
                                onBackPressed()
                            }
                        }
                        requireActivity().onBackPressedDispatcher.addCallback(this, callback)
                    }
                    
                    /**
                     * Override this method into your fragment to handleBackButton
                     */
                      open fun onBackPressed() {
                      }
                    
                    }
                    

                    通过扩展 basefragment 覆盖片段中的 onBackPressed()

                    //How to use this into your fragment
                    class MyFragment() : BaseFragment(){
                    
                    private lateinit var mView: View
                    
                    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
                        mView = inflater.inflate(R.layout.fragment_my, container, false)
                        return mView.rootView
                    }
                    
                    override fun onBackPressed() {
                        //Write your code here on back pressed.
                    }
                    

                    }

                    【讨论】:

                      【解决方案19】:

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

                      override fun onBackPressed() {
                          when (mNavController.getCurrentDestination()!!.getId()) {
                      
                              R.id.loginFragment -> {
                                  onWarningAlertDialog(this, "Alert", "Do you want to close this application ?")
                              }
                              R.id.registerFragment -> {
                                  super.onBackPressed()
                              }
                          }
                      }
                      
                      
                      
                      private fun 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()
                          }
                      

                      【讨论】:

                        【解决方案20】:

                        如果您希望工具栏后退按钮也具有相同的行为,只需将其添加到您的活动中:

                        @Override
                        public boolean onOptionsItemSelected(MenuItem item) {
                            if (item.getItemId() == android.R.id.home) {
                                getOnBackPressedDispatcher().onBackPressed();
                                return true;
                            }
                            return super.onOptionsItemSelected(item);
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          只需为片段创建一个扩展函数

                          fun Fragment.onBackPressedAction(action: () -> Boolean) {
                              requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object :
                                  OnBackPressedCallback(true) {
                                  override fun handleOnBackPressed() {
                                      this.isEnabled = action()
                                      if (!this.isEnabled) {
                                          requireActivity().onBackPressed()
                                      }
                                  }
                              })
                          }
                          

                          然后在片段中将代码放入onCreateView(动作必须返回false才能调用活动onBackPressed)

                          onBackPressedAction { //do something }
                          

                          【讨论】:

                            【解决方案22】:

                            如果您实际上是在尝试专门处理后退按钮,那么您可以使用@Jurij Pitulja 回答。

                            但是,如果你想弹出 SecondFragment(开始片段 FirstFragment)而不返回 FirstFragment,那么你可以使用:

                            Navigation.findNavController(view).popBackStack()
                            

                            来自 SecondFragment。这样,当您从 FirstFragment 按下返回按钮时,您将弹出返回堆栈的 SecondFragmetn,并且不会返回到 SecondFragment

                            【讨论】:

                              【解决方案23】:

                              这是我的解决方案

                              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
                              }
                              

                              此解决方案的优势在于,导航目的地片段无需担心在分离后立即注销其侦听器。

                              【讨论】:

                                【解决方案24】:

                                我尝试了 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()
                                    }
                                }
                                

                                【讨论】:

                                  【解决方案25】:

                                  根据您的逻辑,如果您只想关闭当前片段,您必须通过 viewLifecycleOwner,代码如下所示:

                                     requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
                                          override fun handleOnBackPressed() {
                                              requireActivity().finish()
                                          }
                                      })
                                  

                                  但是,如果您想在 backPressed 上关闭应用程序,无论从哪个片段(可能您不希望那样!),都不要传递 viewLifecycleOwner。此外,如果您想禁用后退按钮,请不要在 handleOnBackPressed() 中执行任何操作,请参见下文:

                                   requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
                                          override fun handleOnBackPressed() {
                                              // do nothing it will disable the back button
                                          }
                                      })
                                  

                                  【讨论】:

                                    【解决方案26】:

                                    我搜索了很多线程,但没有一个有效。终于找到了:

                                    MainActivity.java

                                    @Override
                                    protected void onCreate(Bundle savedInstanceState) {
                                        super.onCreate(savedInstanceState);
                                        setContentView(R.layout.activity_main);
                                    
                                        Toolbar mToolbar = findViewById(R.id.topAppBar);
                                        setSupportActionBar(mToolbar);
                                    }
                                    
                                    @Override
                                    public boolean onSupportNavigateUp() {
                                        navController.navigateUp();
                                        return super.onSupportNavigateUp();
                                    }
                                    

                                    MyFragment.java

                                    @Override
                                    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
                                        Toolbar mToolbar = (MainActivity) getActivity().findViewById(R.id.topAppBar);
                                        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
                                            @Override
                                            public void onClick(View v) {
                                                // Do something when uses presses back button (showing modals, messages,...)
                                                // Note that this will override behaviour of back button
                                            }
                                        });
                                    }
                                    
                                    @Override
                                    public void onStop() {
                                        // Reset back button to default behaviour when we leave this fragment
                                        Toolbar mToolbar = (MainActivity) getActivity().findViewById(R.id.topAppBar);
                                        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
                                            @Override
                                            public void onClick(View v) {
                                                mainActivity.onBackPressed();
                                            }
                                        });
                                    
                                        super.onStop();
                                    }
                                    

                                    【讨论】:

                                      【解决方案27】:

                                      我需要同时支持真正的后退按钮和工具栏后退按钮,并能够在这两种情况下覆盖“后退”单击(以显示对话框或其他内容)。我在片段中创建了一个额外的方法和相应的布尔检查(在我的例子中是'onBackPressed'):

                                      // Process hardware Back button
                                      override fun onBackPressed() {
                                          if (canCloseActivity()) {
                                              super.onBackPressed()
                                          }
                                      }
                                      
                                      // Process toobar Back and Menu button
                                      override fun onSupportNavigateUp(): Boolean {
                                          if (canCloseActivity()) {
                                              return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
                                          }
                                          return false
                                      }
                                      
                                      // Do real check if has unfinished tasks, return false to override activity closing
                                      private fun canCloseActivity(): Boolean {
                                          val currentFragment = navHostFragment.childFragmentManager.primaryNavigationFragment
                                      
                                          return when {
                                              currentFragment is MyFragment && currentFragment.onBackPressed() -> false
                                      
                                              drawerLayout.isOpen -> {
                                                  drawerLayout.close()
                                                  false
                                              }
                                              fullScreenPreviewLayout.visibility == View.VISIBLE -> {
                                                  closeFullscreenPreview()
                                                  false
                                              }
                                              else -> true
                                          }
                                      }
                                      

                                      【讨论】:

                                        【解决方案28】:

                                        简单地说,在FragmentonCreate() 方法中,在super.onCreate(savedInstanceState) 之后使用此代码:

                                        // This callback will only be called when MyFragment is at least Started.
                                        val callback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
                                                    // Handle the back button event
                                        }
                                        

                                        【讨论】:

                                        • 由于Fragment 的生命周期与View 的生命周期略有不同,我认为最好改为通过viewLifecycleOwner。像这样:requreActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner)
                                        【解决方案29】:

                                        ktx它的版本:

                                        fun Fragment.handleBackButtonEvent(
                                            onBackPressed: OnBackPressedCallback.() -> Unit
                                        ) {
                                            requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
                                                onBackPressed()
                                            }
                                        }
                                        

                                        你可以简单地在Fragmnet中使用它。

                                        【讨论】:

                                          【解决方案30】:

                                          我的意见 requireActivity().onBackPressed()

                                          requireActivity().onBackPressed()
                                          

                                          【讨论】:

                                            猜你喜欢
                                            • 1970-01-01
                                            • 2020-02-19
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 2018-09-26
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 2020-05-07
                                            • 2011-04-02
                                            相关资源
                                            最近更新 更多