Android Fragment 在按下后退按钮时未保存 UI 状态

Posted

技术标签:

【中文标题】Android Fragment 在按下后退按钮时未保存 UI 状态【英文标题】:Android Fragment not saving UI State on pressing Back Button 【发布时间】:2021-09-18 12:15:32 【问题描述】:

我的用例遇到了问题。它非常基本的用例。我有一个主要活动和 2 个片段。主要活动没有太多的用户界面。它所做的只是用 FirstFragment 替换它的容器。

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    if (savedInstanceState == null) 
        supportFragmentManager.beginTransaction()
                .replace(R.id.container, FirstFragment.newInstance())
            .addToBackStack(null)
                .commit()
    

FirstFragment 有 2 个按钮 - initializeButton 和 button1,其中 initializeButton 默认处于启用状态,而 button1 仅在按下 initializeButton 后启用。 在 initializeButton 之后按下 Button1 后,它将启动 SecondFragment。以下是执行此操作的代码。

        requireActivity().supportFragmentManager.beginTransaction()
            .replace(R.id.container, SecondFragment.newInstance())
            .addToBackStack(null)
            .commit()

现在,这会正确启动 Secondfragment。现在,当我在 Fragment2 中按下后退按钮时,我想要的行为是第一个片段在两个按钮都处于启用状态时启动。相反,它将进入第一个片段中的默认状态(初始化按钮处于启用状态,而按钮 1 处于禁用状态)。感谢任何反馈。谢谢

主活动

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState == null) 
            supportFragmentManager.beginTransaction()
                    .replace(R.id.container, FirstFragment.newInstance())
                .addToBackStack(null)
                    .commit()
        
    

MainActivity 布局

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_
    android:layout_
    tools:context=".MainActivity" />

第一个片段

class FirstFragment : Fragment() 

    companion object 
        fun newInstance() = FirstFragment()
    

    private lateinit var viewModel: SharedViewModel
    private lateinit var initializeButton: Button
    private lateinit var button1: Button
    private lateinit var newview: View

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        if (savedInstanceState == null) 
            newview = inflater.inflate(R.layout.fragment_first, container, false)
        
        return  newview
    

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        initializeButton = view.findViewById(R.id.button1)
        initializeButton.setOnClickListener
            button1.isEnabled = true
        
        button1 = view.findViewById(R.id.button2)
        button1.setOnClickListener
            requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.container, SecondFragment.newInstance())
                .addToBackStack(null)
                .commit()
        
    

    override fun onSaveInstanceState(outState: Bundle) 
        super.onSaveInstanceState(outState)
        outState.putBoolean("initState", initializeButton.isEnabled)
        outState.putBoolean("button1State", button1.isEnabled)
    

    override fun onViewStateRestored(savedInstanceState: Bundle?) 
        super.onViewStateRestored(savedInstanceState)
        if (savedInstanceState != null) 
            initializeButton.isEnabled = savedInstanceState.getBoolean("initState")
            button1.isEnabled = savedInstanceState.getBoolean("button1State")
        
    

片段优先布局

<LinearLayout 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:layout_
    android:layout_
    tools:context=".FirstFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_
        android:layout_>

    <Button
        android:id="@+id/button1"
        android:layout_
        android:layout_
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:text="@string/initialize"
        android:enabled="true"
        android:layout_marginTop="5dp"/>

    <Button
        android:id="@+id/button2"
        android:layout_
        android:layout_
        app:layout_constraintTop_toBottomOf="@id/button1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:text="@string/button2"
        android:enabled="false"
        android:layout_marginTop="5dp"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

第二个片段

class SecondFragment : Fragment() 
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? 
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_second, container, false)
    

    companion object 
            fun newInstance() = SecondFragment()
    

二次碎片布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical">

    <TextView
        android:id="@+id/info"
        android:layout_
        android:layout_
        android:gravity="center"
        android:text="@string/second_fragment"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:textStyle="bold"
        android:visibility="visible"/>

    <ListView
        android:id="@+id/listView"
        android:layout_
        android:layout_ />

</LinearLayout>

【问题讨论】:

您是否尝试保存 onPause 状态? 【参考方案1】:

您更换片段有什么特别的原因吗?如果没有,您可以简单地添加它而不是替换它,这将确保状态得到维护。

你的代码会这样改变

   .add(R.id.container, SecondFragment.newInstance())
            .addToBackStack(null)
            .commit()

【讨论】:

添加会覆盖第一个片段。我想移动到新屏幕 @KSP 它将重叠,但如果您具有适当的 XML 属性,即背景颜色,则顶部的片段将优先,可单击为真,可聚焦为真。休息一下,如果您在 onViewStateRestored 中获得正确的数据,我建议您检查和调试一次。 .add(R.id.container, SecondFragment.newInstance()) .hide(this) .addToBackStack(null) .commit() - 所以上面的改变对我的用例有用。只是当我按下后退按钮时,它会删除第二个片段。 @KSP 如果它对您有用,您可以将其标记为正确答案以供其他人参考。

以上是关于Android Fragment 在按下后退按钮时未保存 UI 状态的主要内容,如果未能解决你的问题,请参考以下文章

Android Fragment句柄后退按钮按下[重复]

颤振:如何在按下后退按钮时不退出应用程序

React-Native:按下 android 硬件后退按钮返回

如何在按下单个片段的手动后退按钮时返回上一个片段?

Razor 页面搜索表单在按下后退按钮时不保留旧值

如果在 Fragment 的 WebView 中按下后退按钮,如何返回上一页?