Jetpack Navigation Drawer 总是重新创建片段

Posted

技术标签:

【中文标题】Jetpack Navigation Drawer 总是重新创建片段【英文标题】:Jetpack Navigation Drawer always recreate fragment 【发布时间】:2021-07-13 09:34:11 【问题描述】:

我尝试使用由 AS IDE 生成的 Jetpack Navigation Drawer 的默认设置,但我遇到了这个问题,它总是在切换/导航时重新创建片段,而不是仅在顶部添加新片段?他们说这是有意的,但有没有什么解决方案可以在不处理 ViewModel 的情况下不重新创建片段?

这是活动

class MainActivity : AppCompatActivity() 

    private lateinit var appBarConfiguration: AppBarConfiguration
    private var _binding: ActivityMainBinding? = null

    // This property is only valid between onCreate and
    // onDestroyView.
    private val binding get() = _binding!!

    private lateinit var drawerLayout: DrawerLayout

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.appBarMain.toolbar)

        drawerLayout = binding.drawerLayout
        val navView: NavigationView = binding.navView
        val navController = findNavController(R.id.nav_host_fragment_content_main)
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        appBarConfiguration = AppBarConfiguration(setOf(
                R.id.nav_home, R.id.nav_marketcap, R.id.nav_about), drawerLayout)
        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)

    

    override fun onCreateOptionsMenu(menu: Menu): Boolean 
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.main, menu)
        menu.findItem(R.id.action_settings).isChecked = AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
        return true
    


    override fun onSupportNavigateUp(): Boolean 
        val navController = findNavController(R.id.nav_host_fragment_content_main)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    

    override fun onDestroy() 
        super.onDestroy()
        _binding = null
    

    override fun onBackPressed() 
        if (drawerLayout.isDrawerOpen(GravityCompat.START))
            drawerLayout.closeDrawer(GravityCompat.START)
        else
            super.onBackPressed()
    


片段

class AssetFragment : Fragment() 

    companion object 
        fun newInstance() = AssetFragment()
    

    private lateinit var viewModel: AssetViewModel

    private var _binding: FragmentAssetsBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    private lateinit var logTxt: AppCompatTextView
    private lateinit var recyclerView: RecyclerView
    private lateinit var swipeRefreshLayout: SwipeRefreshLayout

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View 

        _binding = FragmentAssetsBinding.inflate(inflater, container, false)
        val root: View = binding.root

        recyclerView = binding.recyclerView
        swipeRefreshLayout = binding.refreshLayout
        logTxt = binding.errorLog

        recyclerView.layoutManager = LinearLayoutManager(context)
        adapter = AssetAdapter(requireContext(), this)
        recyclerView.adapter = adapter

        swipeRefreshLayout.isRefreshing = true
        fetchAssets("30")

        swipeRefreshLayout.setOnRefreshListener 
            swipeRefreshLayout.isRefreshing = true
            fetchAssets("30")
        

        return root

    

    private fun fetchAssets(limit: String) 

        //Network stuff
    

    override fun onActivityCreated(savedInstanceState: Bundle?) 
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(this).get(AssetViewModel::class.java)
        // TODO: Use the ViewModel
    

    override fun onDestroyView() 
        super.onDestroyView()
        _binding = null
    


导航xml

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/nav_home">

    <fragment
        android:id="@+id/nav_home"
        android:name=".ui.home.HomeFragment"
        android:label="@string/home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/nav_marketcap"
        android:name=".ui.marketcap.MarketCapFragment"
        android:label="@string/marketCap"
        tools:layout="@layout/fragment_marketcap" />

    <fragment
        android:id="@+id/nav_about"
        android:name=".ui.about.AboutFragment"
        android:label="@string/about"
        tools:layout="@layout/fragment_about" />

</navigation>

菜单xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single">

        <item android:title="@string/menu">
            <menu>

                <item
                    android:id="@+id/nav_home"
                    android:icon="@drawable/ic_crypto"
                    android:title="@string/home" />

                <item
                    android:id="@+id/nav_marketcap"
                    android:icon="@drawable/ic_marketcap"
                    android:title="@string/marketCap" />

                <item
                    android:id="@+id/nav_about"
                    android:icon="@drawable/ic_about"
                    android:title="@string/about" />

            </menu>
        </item>

    </group>

        <item android:title="@string/connect">
            <menu>
                <item
                    android:id="@+id/email_connect"
                    android:icon="@drawable/ic_email"
                    android:title="@string/fui_email_hint" />
            </menu>
        </item>

</menu>

onCreateView 每次片段切换时都会被调用,甚至按下返回按钮。我需要避免重新创建Home 片段,就像您经常使用片段管理器在顶部添加和弹出片段并且不重新创建主片段一样。

【问题讨论】:

发布您的代码。 @GabrieleMariotti 更新 您找到解决方案了吗?我也有同样的问题。 @lazy 您可以使用 ViewModel 保存视图所需的所有数据,例如RecyclerView 但请记住,Jetpack 导航中正在替换片段,因此即使您只需按下 onBack 即可将用户恢复到主/基本片段,您仍然会重新创建/膨胀视图。 @BitwiseDEVS 提前感谢您花时间回答我,但不幸的是,viewModel 无法解决我的问题。我有一个侧边抽屉,有几个会导航到同一个 web 片段(不同的 url),但是每次点击,片段都会重新创建/膨胀,Webview 初始化导致体验很差 【参考方案1】:

https://developer.android.com/guide/navigation/navigation-programmatic#share_ui-related_data_between_destinations_with_viewmodel

以这种方式创建的任何 ViewModel 对象都存在,直到关联 NavHost 及其 ViewModelStore 被清除或直到导航 图从后栈中弹出。

在片段中, 而不是

    private lateinit var viewModel: AssetViewModel

放在那里

val viewModel: AssetViewModel
        by navGraphViewModels(R.id.nav_host_fragment_content_main)

然后删除

viewModel = ViewModelProvider(this).get(AssetViewModel::class.java)

【讨论】:

以上是关于Jetpack Navigation Drawer 总是重新创建片段的主要内容,如果未能解决你的问题,请参考以下文章

有条件的 Drawer.Navigator 的 openByDefault 属性无法正常工作(“@react-navigation/drawer”:“^5.12.5”)

android navigation drawer怎么用?

VueJs - Vuetify - v-navigation-drawer 与智能手机的迷你变体

v-navigation-drawer 在窗口调整大小时掉入失控循环

怎么自定义有navigation drawer效果的Android控件

VuetifyJS - 滚动 v-navigation-drawer