当我导航回到片段时,片段的内容消失了
Fragment's content gone when i navigate back to them
我在单个 Activity 应用程序中使用导航组件进行导航,但我有一个片段的奇怪行为。简单的用图片来说明。
我有一个 ViewPager 片段。 ViewPager 包含另外两个片段,所以看起来:
Bird - 第一个片段,Test - 第二个。底部元素是底部导航,它不是片段的一部分。片段位于 工具栏 和 底部导航 之间。
这个包含 ViewPager 的片段不是起始片段,它位于堆栈中间的某个位置。
因此,当用户点击底部菜单项时,此导航代码 运行(from Main Activity):
bottom_navigation.apply {
itemIconTintList = null
setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.about_bottom -> {
findNavController(R.id.host).navigate(R.id.toAboutUs)
}
R.id.error_bottom -> {
findNavController(R.id.host).navigate(R.id.toMessage)
}
}
true
}
}
其中 toMessage
/toAboutUs
是另一个片段的全局点。
所以有什么问题。当用户点击底部菜单项时,一切正常。但是当他按下"back"的时候,fragments里面的内容就没有了。简直就是看:
如果是这样,我什至无法提出原因。我知道 "main" Fragment 和 ViewPager 上的 Fragments 不是 重新创建,那么为什么它们会丢失内容?
我不会在任何地方覆盖后退按钮的行为。我只使用 app:defaultNavHost="true"
的主机片段。
数据传输方式:
当用户点击按钮用 ViewPager 打开 Fragment 时,数据从 DB 加载并保存到 ViewModel,然后用户才会被传输到这个 Fragment。当两个子片段都创建时,它们会从 ViewModel 加载数据。而且我没有地方放置我清除 ViewModel 的代码,所以当用户按回它时,我的 ViewModel 100% 包含一些东西。但是它没有显示。
UPD: 花了一些时间,我意识到当我向后导航时,两个 "child" 片段并没有重新创建,而是重新创建了主片段。我认为问题在于它,但仍然不明白到底在哪里。
我需要你的帮助来了解发生了什么。
Upd: 提供一些创建Fragments的代码。 BaseCompatFragment 扩展 Fragment
MainFragment(另外两个片段的容器):
class QuestionFragment : BaseCompatFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_question, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
pager.adapter = QuestionViewPagerAdapter(fragmentManager!!)
activity?.toolbar_title?.text = getString(R.string.title_question,1)
layout_tab.apply {
setupWithViewPager(pager)
tabIconTint = null
getTabAt(0)?.setIcon(R.drawable.ic_type_bird)
getTabAt(1)?.setIcon(R.drawable.ic_hints)
}
}
}
QuestionViewPagerAdapter
class QuestionViewPagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) {
override fun getItem(position: Int): BaseCompatFragment {
when (position) {
0 -> return HintsFragment()
1 -> return BaseInfoFragment()
}
return HintsFragment()
}
override fun getCount(): Int {
return 2
}
}
提示片段(鸟)
class HintsFragment : BaseCompatFragment(), HintsFragmentContract.View {
@Inject
lateinit var presenter: HintsFragmentPresenter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_hints, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
LibApp.get().injector.inject(this)
presenter.attach(this)
val animalWithHints =
ViewModelProviders.of(activity!!).get(AnimalViewModel::class.java).getData().value
val adapter = HintsAdapter(callback = { id ->
//Some code will be here soon
})
//Do smth with content
adapter.hintsList = animalWithHints?.animal?.hints?.split("///") as ArrayList<String>
adapter.hintsStorage = animalWithHints.hints?.get(0) ?: Hints()
recycler_hints.layoutManager = verticalManager(context)
recycler_hints.adapter = adapter
}
}
BaseInfoFragment(测试)
class BaseInfoFragment : BaseCompatFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_base_info, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animalWithHints =
ViewModelProviders.of(activity!!).get(AnimalViewModel::class.java).getData().value
//A lot of logic to hide/show content, don't think it can be intresting
val isBaseInfoOpen = animalWithHints?.hints?.get(0)?.baseInfoOpened == 1
setBlockLayoutState(!isBaseInfoOpen)
if (isBaseInfoOpen) {
openContent(animalWithHints)
} else {
text_base_info.text = getString(R.string.base_info_price, 100)
btn_base_info_positive.setOnClickListener {
if (btn_base_info_negative.visibility == View.GONE) {
btn_base_info_negative.visibility = View.VISIBLE
text_base_info.text = getString(R.string.default_doubts)
} else {
openContent(animalWithHints)
}
}
btn_base_info_negative.setOnClickListener {
it.visibility = View.GONE
text_base_info.text = getString(R.string.base_info_price, 100)
}
}
}
private fun getRareIcon(rare: Int, context: Context): Drawable {
return when (rare) {
1 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_fine)!!
2 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_medium)!!
3 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_bad)!!
else -> ContextCompat.getDrawable(context, R.drawable.ic_warning)!!
}
}
private fun getRareText(rare: Int): String {
return when (rare) {
1 -> getString(R.string.base_info_rare_1)
2 -> getString(R.string.base_info_rare_2)
3 -> getString(R.string.base_info_rare_3)
else -> getString(R.string.base_info_rare_4)
}
}
private fun setBlockLayoutState(state: Boolean) {
base_info_closer.visibility = when {
state -> View.VISIBLE
else -> View.GONE
}
base_info_content.visibility = when {
state -> View.GONE
else -> View.VISIBLE
}
}
private fun openContent(animalWithHints: AnimalWithHints?) {
(img_closed_base_info.drawable as Animatable).start()
rare_img.setImageDrawable(getRareIcon(animalWithHints?.animal?.rare ?: 0, activity!!))
rare_text.text = getRareText(animalWithHints?.animal?.rare ?: 0)
setBlockLayoutState(false)
}
}
在MainFragment的onCreateView上,尝试放if (!isAdded()) return;某处。我之前遇到过同样的问题,但是您的代码与我的有点不同。我所做的是 if (!isAdded()) return;.
我想,问题在这里:QuestionViewPagerAdapter(fragmentManager!!)
当你直接在你的 activity 上添加片段时,你需要使用 getFragmentManager()
/getSupportFragmentManager()
。但是当你需要在另一个片段上添加片段时,你需要使用getChildFragmentManager()
.
来自 getSupportFragmentManager()
documentation:
Return the FragmentManager for interacting with fragments associated with this activity.
所以,这就是为什么你的鸟和测试片段没有在它们的父片段重新创建时重新创建的原因。
并且来自 getChildFragmentManager()
doc:
Return a private FragmentManager for placing and managing Fragments inside of this Fragment.
这应该可以解决问题。希望能帮助到你。
我在单个 Activity 应用程序中使用导航组件进行导航,但我有一个片段的奇怪行为。简单的用图片来说明。
我有一个 ViewPager 片段。 ViewPager 包含另外两个片段,所以看起来:
Bird - 第一个片段,Test - 第二个。底部元素是底部导航,它不是片段的一部分。片段位于 工具栏 和 底部导航 之间。
这个包含 ViewPager 的片段不是起始片段,它位于堆栈中间的某个位置。
因此,当用户点击底部菜单项时,此导航代码 运行(from Main Activity):
bottom_navigation.apply {
itemIconTintList = null
setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.about_bottom -> {
findNavController(R.id.host).navigate(R.id.toAboutUs)
}
R.id.error_bottom -> {
findNavController(R.id.host).navigate(R.id.toMessage)
}
}
true
}
}
其中 toMessage
/toAboutUs
是另一个片段的全局点。
所以有什么问题。当用户点击底部菜单项时,一切正常。但是当他按下"back"的时候,fragments里面的内容就没有了。简直就是看:
如果是这样,我什至无法提出原因。我知道 "main" Fragment 和 ViewPager 上的 Fragments 不是 重新创建,那么为什么它们会丢失内容?
我不会在任何地方覆盖后退按钮的行为。我只使用 app:defaultNavHost="true"
的主机片段。
数据传输方式: 当用户点击按钮用 ViewPager 打开 Fragment 时,数据从 DB 加载并保存到 ViewModel,然后用户才会被传输到这个 Fragment。当两个子片段都创建时,它们会从 ViewModel 加载数据。而且我没有地方放置我清除 ViewModel 的代码,所以当用户按回它时,我的 ViewModel 100% 包含一些东西。但是它没有显示。
UPD: 花了一些时间,我意识到当我向后导航时,两个 "child" 片段并没有重新创建,而是重新创建了主片段。我认为问题在于它,但仍然不明白到底在哪里。
我需要你的帮助来了解发生了什么。
Upd: 提供一些创建Fragments的代码。 BaseCompatFragment 扩展 Fragment
MainFragment(另外两个片段的容器):
class QuestionFragment : BaseCompatFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_question, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
pager.adapter = QuestionViewPagerAdapter(fragmentManager!!)
activity?.toolbar_title?.text = getString(R.string.title_question,1)
layout_tab.apply {
setupWithViewPager(pager)
tabIconTint = null
getTabAt(0)?.setIcon(R.drawable.ic_type_bird)
getTabAt(1)?.setIcon(R.drawable.ic_hints)
}
}
}
QuestionViewPagerAdapter
class QuestionViewPagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) {
override fun getItem(position: Int): BaseCompatFragment {
when (position) {
0 -> return HintsFragment()
1 -> return BaseInfoFragment()
}
return HintsFragment()
}
override fun getCount(): Int {
return 2
}
}
提示片段(鸟)
class HintsFragment : BaseCompatFragment(), HintsFragmentContract.View {
@Inject
lateinit var presenter: HintsFragmentPresenter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_hints, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
LibApp.get().injector.inject(this)
presenter.attach(this)
val animalWithHints =
ViewModelProviders.of(activity!!).get(AnimalViewModel::class.java).getData().value
val adapter = HintsAdapter(callback = { id ->
//Some code will be here soon
})
//Do smth with content
adapter.hintsList = animalWithHints?.animal?.hints?.split("///") as ArrayList<String>
adapter.hintsStorage = animalWithHints.hints?.get(0) ?: Hints()
recycler_hints.layoutManager = verticalManager(context)
recycler_hints.adapter = adapter
}
}
BaseInfoFragment(测试)
class BaseInfoFragment : BaseCompatFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_base_info, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animalWithHints =
ViewModelProviders.of(activity!!).get(AnimalViewModel::class.java).getData().value
//A lot of logic to hide/show content, don't think it can be intresting
val isBaseInfoOpen = animalWithHints?.hints?.get(0)?.baseInfoOpened == 1
setBlockLayoutState(!isBaseInfoOpen)
if (isBaseInfoOpen) {
openContent(animalWithHints)
} else {
text_base_info.text = getString(R.string.base_info_price, 100)
btn_base_info_positive.setOnClickListener {
if (btn_base_info_negative.visibility == View.GONE) {
btn_base_info_negative.visibility = View.VISIBLE
text_base_info.text = getString(R.string.default_doubts)
} else {
openContent(animalWithHints)
}
}
btn_base_info_negative.setOnClickListener {
it.visibility = View.GONE
text_base_info.text = getString(R.string.base_info_price, 100)
}
}
}
private fun getRareIcon(rare: Int, context: Context): Drawable {
return when (rare) {
1 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_fine)!!
2 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_medium)!!
3 -> ContextCompat.getDrawable(context, R.drawable.ic_rare_bad)!!
else -> ContextCompat.getDrawable(context, R.drawable.ic_warning)!!
}
}
private fun getRareText(rare: Int): String {
return when (rare) {
1 -> getString(R.string.base_info_rare_1)
2 -> getString(R.string.base_info_rare_2)
3 -> getString(R.string.base_info_rare_3)
else -> getString(R.string.base_info_rare_4)
}
}
private fun setBlockLayoutState(state: Boolean) {
base_info_closer.visibility = when {
state -> View.VISIBLE
else -> View.GONE
}
base_info_content.visibility = when {
state -> View.GONE
else -> View.VISIBLE
}
}
private fun openContent(animalWithHints: AnimalWithHints?) {
(img_closed_base_info.drawable as Animatable).start()
rare_img.setImageDrawable(getRareIcon(animalWithHints?.animal?.rare ?: 0, activity!!))
rare_text.text = getRareText(animalWithHints?.animal?.rare ?: 0)
setBlockLayoutState(false)
}
}
在MainFragment的onCreateView上,尝试放if (!isAdded()) return;某处。我之前遇到过同样的问题,但是您的代码与我的有点不同。我所做的是 if (!isAdded()) return;.
我想,问题在这里:QuestionViewPagerAdapter(fragmentManager!!)
当你直接在你的 activity 上添加片段时,你需要使用 getFragmentManager()
/getSupportFragmentManager()
。但是当你需要在另一个片段上添加片段时,你需要使用getChildFragmentManager()
.
来自 getSupportFragmentManager()
documentation:
Return the FragmentManager for interacting with fragments associated with this activity.
所以,这就是为什么你的鸟和测试片段没有在它们的父片段重新创建时重新创建的原因。
并且来自 getChildFragmentManager()
doc:
Return a private FragmentManager for placing and managing Fragments inside of this Fragment.
这应该可以解决问题。希望能帮助到你。