使用 ActionBar 进入 SettingsFragment 后无法返回主片段

Cannot go back to Main Fragment after going into SettingsFragment using ActionBar

希望大家平安。我在使用 ActionBar 和 ActionBar 中的向上按钮时遇到了一个令人沮丧的问题。我的应用程序中有以下片段结构:

MainFragment -
             |- SettingsFragment -
                                 |- GeneralSettingsFragment
                                 |- ScaryStuffSettingsFragment

上面的所有片段都启用了 ActionBar。 SettingsFragment、GeneralSettingsFragment 和 ScaryStuffFragment 都启用了向上按钮,这样用户就可以按上面的顺序在需要时返回。

在我的 MainFragment 中,我有以下代码可以访问 SettingsFragment:

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        super.onOptionsItemSelected(item)

        when(item.itemId) {
            R.id.action_main_to_settings_menu -> {
                onOptionSettingsClick()
            }

        }
        return true
    }

    /** When the user taps the options menu settings button, we'll take them to the
     * settings menu.
     */
    private fun onOptionSettingsClick() {
        findNavController().navigate(R.id.settingsFragment)
    }

在我的 SettingsFragment 中有以下内容:

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId) {
            android.R.id.home -> findNavController().navigateUp()
        }
        return super.onOptionsItemSelected(item)
    }

SettingsFragment 中使用的代码与 ScaryStuffSettingsFragment 和 GeneralSettingsFragment 中使用的代码相同。

现在,当我点击 MainFragment 中的设置时,它会将我带到 SettingsFragment。那里没有问题。如果我再次点击 SettingsFragment ActionBar 中的向上按钮,它会将我发送到 MainFragment。

如果我点击恐怖的东西或一般设置,我会毫无问题地转到相关的片段。如果我点击任一片段中的向上按钮,它会将我带到 SettingsFragment。再次。这里没问题。

但是,如果我想在使用向上按钮点击可怕的东西或常规后从 SettingsFragment 返回 MainFragment...什么都没有发生。

我发现,如果我点击设备本身的后退按钮,它会带我回到 MainFragment,从而完全规避了问题,但我不希望用户在大多数情况下都使用向上按钮设置,然后必须点击后退按钮(如果有意义的话)。

一旦选择了其中一个子片段,它会“忘记”MainFragment 和 SettingsFragment 之间的传输,这是怎么回事?

谢谢!

编辑:

MainFragment 是一个普通片段。它的onCreateView如下:

override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstance: Bundle?): View {
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        setHasOptionsMenu(true)

        return binding.root
    }

我的所有三个设置应用程序都是 PreferenceFragments,因此具有以下内容:

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.general_settings_preferences, rootKey)
        setHasOptionsMenu(true)
    }

编辑 2:

我现在有以下内容:

主要活动:

override fun onCreate(savedInstanceState: Bundle?) {
    navController = findNavController(R.id.nav_host)

        appBarConfiguration = AppBarConfiguration.Builder(R.id.FragmentMain).build()
        val hostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_container) as NavHostFragment?
        //navController = hostFragment?.navController!! // Commented out due to throwing a nullpointerexception
        setupActionBarWithNavController(navController, appBarConfiguration)
}

override fun onSupportNavigateUp(): Boolean {
        return (NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp())
}

当我这样做时(也更新我的导航图),然后点击设置(进入我的 SettingsFragment)它短暂地显示 SettingsFragment 然后更改为顶部的设置(这是因为我告诉它用(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.menu_option_settings) 在我的 onCreateOptionsMenu 方法中。然后,当我返回时,它按预期工作。但是,当我随后点击 Scary Stuff(转到 ScaryStuffSettingsFragment)或常规设置(转到 GeneralSettingsFragment)时它做同样的事情并在顶部闪烁片段的名称,然后更改为我设置的内容,当我点击向上箭头时,它会将我送回主片段。

我只希望设置菜单返回到 MainFragment,其他设置应该转到设置菜单(如果有意义的话)。

将 setHasOptionsMenu 添加到 onViewCreated

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setHasOptionsMenu(true)
     }

if I then want to go back to MainFragment from SettingsFragment once I have tapped either Scary Stuff or General using the Up Button... Nothing happens.

没有任何反应意味着导航组件没有管理向上按钮的点击;显然你按照以下方式手动管理返回堆栈导航:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when(item.itemId) {
        android.R.id.home -> findNavController().navigateUp()
    }
    return super.onOptionsItemSelected(item)
}

为了解决这个问题,请确保在承载这些片段 (documentation reference) 的 activity 中添加启用 supportActionBar 导航:

  • 调用 setupActionBarWithNavController()
  • 覆盖onSupportNavigateUp()
class MainActivity : AppCompatActivity() {

    lateinit var navController: NavController

    private lateinit var mAppBarConfiguration: AppBarConfiguration

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mAppBarConfiguration = AppBarConfiguration.Builder(
            R.id.main_fragment // Adjust that to your top level fragments (that you don't want the UP button appear)
        ).build()

        val hostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment1) as NavHostFragment?
        navController = hostFragment?.navController!!
        setupActionBarWithNavController(this, navController)

    }

    override fun onSupportNavigateUp(): Boolean {
        return (NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp())
    }

}

注意: 如果您没有使用默认 supportActionBar,要启用导航组件控件您的自定义工具栏:

toolbar.setupWithNavController(navController, appBarConfiguration)

到目前为止,这使得 actionBar 及其导航由导航组件/navController 控制。并且通过UP按钮的导航会正常注册到backstack中。

但是要从任何导航操作的返回堆栈中删除一些片段,您可以使用 popUpTo & popUpToInclusive. 答案通过示例描述了不同之处。

将其应用到您的案例中;你通过从 SettingsFragmentGeneralSettingsFragment|ScaryStuffSettingsFragment

的动作来做到这一点

navGraph:

<navigation ...>

    ....
    
    <fragment
        android:id="@+id/fragmentSettings"
        android:name="com.example.android.ktnavarchcomptwoactivities.SettingsFragment"
        android:label="fragmentSettings" >
        <action
            android:id="@+id/action_fragmentSettings_to_fragmentScaryStuffSettings"
            app:destination="@id/fragmentScaryStuffSettings"         
            app:popUpTo="@id/fragment_main"
            app:popUpToInclusive="false"/>

        <action
            android:id="@+id/action_fragmentSettings_to_fragmentGeneralSettings"
            app:destination="@id/fragmentGeneralSettings" 
            app:popUpTo="@id/fragment_main"
            app:popUpToInclusive="false"/>
    </fragment>


</navigation>

或者通过在导航期间定义 NavOptions 参数以编程方式:

在 SettingsFragment 中:

// Navigation from SettingsFragment to GeneralSettingsFragment
findNavController().navigate(
    R.id.action_fragmentSettings_to_fragmentGeneralSettings,
    null,
    NavOptions.Builder().setPopUpTo(R.id.fragment_main, false).build()
)


// Navigation from SettingsFragment to ScaryStuffSettingsFragment
findNavController().navigate(
    R.id.action_fragmentSettings_to_fragmentScaryStuffSettings,
    null,
    NavOptions.Builder().setPopUpTo(R.id.fragment_main, false).build()
)