【问题标题】:Cannot go back to Main Fragment after going into SettingsFragment using ActionBar使用 ActionBar 进入 SettingsFragment 后无法返回 Main Fragment
【发布时间】:2022-01-04 15:16:51
【问题描述】:

希望所有人都安全。在使用 ActionBar 中的 ActionBar 和 Up 按钮时,我遇到了一个令人沮丧的问题。我的应用中有以下片段结构:

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,然后更改为顶部的设置(这是因为我用 @987654327 告诉它@ 在我的onCreateOptionsMenu 方法中。然后,当我点击返回时,这会按预期工作。但是,当我然后点击可怕的东西(转到 ScaryStuffSettingsFragment)或常规设置(转到 GeneralSettingsFragment)时,它会做同样的事情并且在顶部闪烁 Fragment 的名称,然后更改为我设置的名称,当我点击向上箭头时,它会将我送回 Main Fragment。

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

【问题讨论】:

  • 有谁知道我如何做到这一点?
  • 你能发布导航到可怕的东西或一般的代码

标签: android kotlin android-fragments android-architecture-components android-navigation


【解决方案1】:

如果我想从 SettingsFragment 一旦我点击了可怕的东西或一般 使用向上按钮...没有任何反应。

什么都没有发生意味着导航组件没有管理 UP 按钮的点击;显然您按照以下方式手动管理后台导航:

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

为了解决这个问题,请确保在托管这些片段 (documentation reference) 的活动中添加启用 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 & popUpToInclusiveThis的回答用一个例子来说明区别。

将其应用于您的案例;你通过从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()
)

【讨论】:

  • 嗨 Zain,这种作品,但不完全是!有关详细信息,请参阅 OP 编辑​​ 2 :)
  • 嘿@JamieRhys 可能是由于onOptionsItemSelected() 导致的闪烁.. 你删除它了吗?
  • 我有,但仍然存在。我还看到,如果我点击常规设置,然后返回设置,它会粘在 SettingsFragment
  • @JamieRhys 您在哪里使用activity as AppCompatActivity).supportActionBar?.title =... 您可以在要更改actionBar 标题的每个片段中使用android:label="fragmentSettings" 来代替;那是因为您已经允许导航组件使用setupActionBarWithNavController() 控制actionBar;希望它现在可以工作
  • @Zain 我已经在 Fragment 而不是 MainActivity 中实现了 NavHost 。先说说如何从 Fragment 设置 AppBarConfiguration
【解决方案2】:

将 setHasOptionsMenu 添加到 onViewCreated 中

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

【讨论】:

  • 用我为每个片段所拥有的更新了 OP
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 2021-03-27
  • 1970-01-01
  • 2017-08-03
  • 1970-01-01
相关资源
最近更新 更多