当我导航回片段时,片段的内容消失了

Posted

技术标签:

【中文标题】当我导航回片段时,片段的内容消失了【英文标题】:Fragment's content gone when i navigate back to them 【发布时间】:2019-09-05 12:34:51 【问题描述】:

我在 Single Activity 应用程序中使用 Navigation 组件进行导航,但我的一个片段有奇怪的行为。只是用图片来解释。

我有一个带有 ViewPager 的片段。 ViewPager 包含另外两个片段,所以看起来:

Bird - 第一个片段,Test - 第二个。底部元素是底部导航,它不是片段的一部分。片段位于 工具栏底部导航 之间。

这个包含 ViewPager 的片段不是起始片段,它位于堆栈中间的某个位置。

因此,当用户点击底部菜单项时,此导航代码运行(来自 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 是另一个片段的全局点。

那么有什么问题。当用户单击底部菜单项时,一切正常。但是当他按下“返回”时,片段中的内容就消失了。只是为了看看:

如果是这样,我什至无法提出原因。我知道 ViewPager 上的“主要”片段和片段重新创建,那么为什么它们会丢失内容?

我不会在任何地方覆盖后退按钮的行为。我只是使用主机片段的app:defaultNavHost="true"


数据的传输方式: 当用户单击按钮以使用 ViewPager 打开 Fragment 时,从 DB 加载数据并保存到 ViewModel,然后用户才会被传输到该 Fragment。创建两个子片段后,它们会从 ViewModel 加载数据。而且我没有地方可以清除 ViewModel 的代码,所以当用户按下它时,我的 ViewModel 100% 包含一些东西。但它不显示。

UPD:花了一些时间,我意识到当我向后导航时,两个“子”片段并没有重新创建,而是主片段重新创建。我认为问题在于它,但仍然不明白到底在哪里。

我需要你的帮助来了解发生了什么。


更新:提供一些片段创建的代码。 BaseCompatFragment 扩展 Fragment

MainFragment(另外两个 Fragment 的容器):

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)
    

【问题讨论】:

能否提供一些创建片段的代码? 如果您不介意,能否请您显示片段的代码? 当然。添加了我现在拥有的内容。 【参考方案1】:

我想,问题就在这里:QuestionViewPagerAdapter(fragmentManager!!) 当您直接在您的活动上添加片段时,您需要使用getFragmentManager()/getSupportFragmentManager()。但是当你需要在另一个fragment上添加fragment时,你需要使用getChildFragmentManager()

来自getSupportFragmentManager()documentation:

返回 FragmentManager 以与与此活动关联的片段进行交互。

所以,这就是为什么您的鸟和测试片段没有在它们的父片段重新创建时重新创建的原因。

来自getChildFragmentManager()doc:

返回一个私有的 FragmentManager 用于在这个 Fragment 中放置和管理 Fragment。

这应该可以解决问题。希望能帮助到你。

【讨论】:

是的,它有效。没有一天没有关于 android 的新消息。 =) @KirstenLy 在片段中使用片段时很常见)【参考方案2】:

在MainFragment的onCreateView上,尝试把if (!isAdded()) return;某处。我之前遇到过同样的问题,但是你的代码和我的有点不同。我所做的是把 if (!isAdded()) return;.

【讨论】:

Ty,但它不起作用:我尝试在很多地方使用它。但是有了你的回答,我知道 MainFragment 正在重新创建,我认为问题与它有某种联系。

以上是关于当我导航回片段时,片段的内容消失了的主要内容,如果未能解决你的问题,请参考以下文章

在导航抽屉片段中保存视图页面片段的状态

当我回到片段时防止重复项目

Android 目的地片段未出现在导航中

焦点在 EditText 上时 ViewPager2 片段内容消失

导航抽屉活动:在按钮单击时从片段移动到片段

跟踪活动中的片段[关闭]