使用导航架构组件添加(而不是替换)片段
Posted
技术标签:
【中文标题】使用导航架构组件添加(而不是替换)片段【英文标题】:Add (not replace) fragment with navigation architecture component 【发布时间】:2019-03-13 23:48:34 【问题描述】:我有一个包含产品列表片段和许多其他片段的活动,我正在尝试使用架构组件导航控制器。
问题是:它替换了(起始目的地)产品列表片段,我不希望在用户单击返回按钮时重新加载列表。
如何使分片事务为添加而不是替换?
【问题讨论】:
您必须提供更多关于什么transaction 正在替换Start Destination 的信息。当您导航到其他活动时,它是否会被替换,或者当您导航回开始目的地时,开始目的地中的列表是否会重新加载? 不幸的是,这似乎是不可能的(至少在当前的 2.0.0 版本中)。如果您检查androidx.navigation.fragment.FragmentNavigator#navigate
方法,您将看到它在内部使用ft.replace(mContainerId, frag);
。我认为这里唯一的选择是开始一个新的活动作为目的地。
这太恐怖了!今天我遇到了一个问题,WebView
从不同的Fragment
回来时总是重新加载!而且我看不出有什么办法可以阻止它。
任何人都可以找到有关此问题的任何解决方案或解决方法,以便在返回时可以保留列表位置并且用户不必再次滚动!
@UtkuKUTLU 页面未找到
【参考方案1】:
Android 导航组件只是替换,但您想添加片段而不是替换对话框,您可以使用它但需要最小化。 "Version 2.1.0" 用于导航组件。
Solution
你可以看到“Dialog destinations”
【讨论】:
非常好的解决方案!! 这个问题是 【参考方案2】:我遇到了同样的问题,在等待add
和片段事务的其他选项时,我实施了这项工作以在回击时保留状态。
我只是添加了一个检查绑定是否存在然后我只是恢复以前的状态,与网络调用相同,我添加了一个检查数据是否存在于视图模型中然后不进行网络重新获取。经过测试,它按预期工作。
编辑:
对于回收站视图,我相信它会自动返回到您从片段导航之前的列表状态,但也可以将位置存储在 onSavedInstanceSate
中
private lateinit var binding: FragmentSearchResultsBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
viewModel =
ViewModelProviders.of(this, mViewModelFactory).get(SearchResultsViewModel::class.java)
return if (::binding.isInitialized)
binding.root
else
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_search_results, container, false)
with(binding)
//some stuff
root
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
//reload only if search results are empty
if (viewModel.searchResults.isEmpty())
args.searchKey.let
binding.toolbarHome.title = it
viewModel.onSearchResultRequest(it)
【讨论】:
【参考方案3】:您必须重写 NavHostFragment 的 createFragmentNavigator
方法并返回 YourFragmentNavigator
。
YourFragmentNavigator
必须覆盖 FragmentNavigator 的 navigate
方法。
将 FragmentNavigator 的 navigate
方法复制并粘贴到您的 YourFragmentNavigator
。
在导航方法中,将ft.replace(mContainerId, frag);
行更改为
if (fragmentManager.fragments.size <= 0)
ft.replace(containerId, frag)
else
ft.hide(fragmentManager.fragments[fragmentManager.fragments.size - 1])
ft.add(containerId, frag)
解决方案将如下所示:
class YourNavHostFragment : NavHostFragment()
override fun createFragmentNavigator(): Navigator<...>
return YourFragmentNavigator(...)
....
class YourFragmentNavigator(...) : FragmentNavigator(...)
override fun navigate(...)
....
if (fragmentManager.fragments.size <= 0)
ft.replace(containerId, frag)
else
ft.hide(fragmentManager.fragments[fragmentManager.fragments.size - 1])
ft.add(containerId, frag)
....
在你的 xml 中使用YourNavHostFragment
。
【讨论】:
我看过这个,但您的子类将无法访问 FragmentNavigator 私有字段 mBackStack(至少对于导航组件 2.3.0)。为此,您需要将此字段添加到您的子类中,并创建引用 mBackStack 的覆盖 onSaveState、onRestoreState 和 popBackStack。并跟上导航组件的新版本,以确保您的覆盖仍然可以。 不要忘记 ft.addToBackStack(entry.id) 所以 popBackStack 稍后会弹出顶部片段【参考方案4】:我遇到了同样的问题,但就我而言,我更新了代码以使用 livedata 和 viewmodel。 当您按下时,视图模型不会再次创建,因此您的数据将被保留。
确保在 viewmodel 的 init 方法中调用 api,以便在创建 viewmodel 时只发生一次
【讨论】:
github.com/cult-mentality/thank_you_tree_app 请注意,我建议将整个架构更新为 MVVM。我提供的代码是一个 MVVM 架构驱动的应用程序。【参考方案5】:只需复制FragmentNavigator
的代码(300 行)并将replace()
替换为add()
。这是目前对我来说最好的解决方案。
@Navigator.Name("fragment")
public class CustomFragmentNavigator extends
Navigator<...>
...
public NavDestination navigate(...)
...
ft.add(mContainerId, frag);
...
...
【讨论】:
【参考方案6】:@Rainmaker 在我看来是对的,我也做了同样的事情。 我们还可以在 onSaveInstanceState 中保存回收站视图位置/状态 以便在导航回列表片段时返回相同的回收站视图位置。
【讨论】:
【参考方案7】:搜索了一下,是不可能的,但是问题本身可以用viewmodel和livedata或者rxjava来解决。 所以片段状态在交易后保持,我的产品列表不会每次都重新加载
【讨论】:
一个代码示例或几个链接来支持你的答案会很好。 据我所知,viewmodel 无济于事。片段被销毁,因此,它实际上创建了一个全新的视图模型。我自己也有同样的问题。我目前的解决方法是在活动范围内将状态对象作为视图模型。以上是关于使用导航架构组件添加(而不是替换)片段的主要内容,如果未能解决你的问题,请参考以下文章