我来详细讲解一下“Android Navigation重建Fragment问题分析及解决”的完整攻略。
什么是Navigation重建Fragment问题?
在使用Android Navigation组件时,如果使用了NavigationUI.setupWithNavController()
来设置BottomNavigationView或者使用了AppBarConfiguration
来配置Toolbar,那么在导航到其他Fragment或者旋转屏幕等操作后回到之前的Fragment时,有时候会重建Fragment。这就导致了之前Fragment中的数据丢失,用户体验差等问题,这就是Navigation重建Fragment问题。
Navigation重建Fragment问题分析
为什么会导致Fragment重建?
当我们导航到一个Fragment时,它会被添加到回退栈中。当我们点击返回按钮或者调用NavController.popBackStack()
方法时,会从回退栈中弹出当前Fragment并重新显示上一个Fragment。而当我们重新进入这个Fragment时,Fragment会尝试恢复之前未保存的UI状态。
然而,当我们使用NavigationUI.setupWithNavController()
或者AppBarConfiguration
来设置导航控制器时,会在活动的生命周期方法中调用不同的方法来保存和恢复导航控制器的状态,这会间接导致Fragment重建的问题。
Navigation重建Fragment的影响
- 丢失未保存的数据;
- 界面状态回到初始化状态;
- 采用ViewPager2+Fragment方式会重新加载当前和相邻的Fragment。
Navigation重建Fragment问题解决
Navigation重建Fragment问题的解决方案可以分为两类。
方案一:禁用Navigation控制器
这是禁用Navigation控制器来解决Navigation重建Fragment问题的一种方案。需要在onCreate()
中设置savedInstanceState
为null,以便在屏幕旋转等配置更改时,不会自动恢复之前的Fragment。
override fun onCreate(savedInstanceState: Bundle?) {
savedInstanceState = null
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
val navController = findNavController(R.id.nav_host_fragment)
NavigationUI.setupWithNavController(bottomNavigationView, navController)
}
方案二:子类化Fragment
重建问题的另一种解决方案是在Fragment中手动保存和恢复UI状态。这可以通过下面两种方式来实现:
1. 保存和恢复状态
在Fragment中创建一个成员变量,用于保存Fragment的状态:
private lateinit var state: Bundle
在onSaveInstanceState()
方法中将状态保存在Bundle实例中:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBundle("my_state", state)
}
在onCreateView()
方法中恢复状态:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
if (savedInstanceState != null) {
state = savedInstanceState.getBundle("my_state")
}
return inflater.inflate(R.layout.fragment_blank, container, false)
}
2. 阻止Fragment重建
重写Fragment中的onDestroyView()
方法,并且在方法中调用retainInstance = true
来阻止Fragment销毁并保留其UI状态:
override fun onDestroyView() {
super.onDestroyView()
retainInstance = true
}
示例一
下面是一个禁用Navigation控制器的示例。在MainActivity.kt
中设置默认的NavController即可:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
savedInstanceState = null
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!.findNavController()
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
NavigationUI.setupWithNavController(bottomNavigationView, navController)
}
}
示例二
现在我们来看一个更具体的示例,该示例中我们想在Fragment之间传递数据和保存UI状态。我们将创建一个具有EditText和Button的Fragment,以演示如何保存和恢复其内容。
1. 创建一个新的Kotlin文件,并实现想要添加保存状态和传递数据的Fragment。例如,你可以创建一个新的Fragment作为默认模板来完成这项工作。
class BlankFragment : Fragment() {
private lateinit var editText: EditText
private lateinit var inputText: String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_blank, container, false)
editText = view.findViewById(R.id.editText)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
inputText = editText.text.toString().trim()
val action = BlankFragmentDirections.actionBlankFragmentToDisplayFragment(inputText)
NavHostFragment.findNavController(this).navigate(action)
}
if (savedInstanceState != null) {
inputText = savedInstanceState.getString("saved_text").toString()
editText.setText(inputText)
}
return view
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("saved_text", editText.text.toString().trim())
}
}
2. 使用NavOptions.Builder
来为传递的数据创建新的Bundle对象。下面是DisplayFragment
的完整代码演示:
class DisplayFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_display, container, false)
val textView = view.findViewById<TextView>(R.id.textView)
val args = DisplayFragmentArgs.fromBundle(requireArguments())
val data = args.displayText
textView.text = data
return view
}
}
现在,我们已经学会了如何解决Navigation重建Fragment问题并且通过示例演示了如何保存和恢复Fragment中的状态和传递数据。希望这篇文章对你有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android Navigation重建Fragment问题分析及解决 - Python技术站