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”)
VueJs - Vuetify - v-navigation-drawer 与智能手机的迷你变体
v-navigation-drawer 在窗口调整大小时掉入失控循环