更新:通过解决方案接受了解释(错误)的答案点,但也看到我的基于Kotlin的解决方案作为下面的答案附加。
此代码在Kotlin中,但我认为这是一个基本的Android片段生命周期问题。
我有一个片段,其中包含对其他“subfragment”的引用
这基本上就是我在做什么:
retainInstance
设置为true的主片段onCreateView
中,我检查子片段字段是否为空,如果是,我创建subFragment的实例并将其分配给字段当设备旋转时,我确实观察到被调用的子片段的onPaused()
和onDestroyView()
方法,但是我没有看到在添加过程中在子片段上调用任何生命周期方法重新创建主片段视图时,保留对子片段的引用,以及对child_container的引用。
净影响是我没有在主片段中看到子片段视图。如果我注释掉if(subfragment == null)并且每次只创建一个新的子片段,我执行查看视图中的子片段。
下面的答案确实指出了一个错误,其中未在配置更改中保留childFragmentManager。这最终会破坏我的预期用途,即在旋转后保留背斜,但我认为我所看到的是不同的东西。
我在活动onWindowFocusChanged
方法中添加了代码,我在第一次启动应用时看到类似的内容:
activity is in view
fm = FragmentManager{b13b9b18 in Tab1Fragment{b13b2b98}}
tab 1 fragments = [DefaultSubfragment{b13bb610 #0 id=0x7f0c0078}]
然后轮换:
activity is in view
fm = FragmentManager{b13f9c30 in Tab1Fragment{b13b2b98}}
tab 1 fragments = null
这里fm是childFragmentManager,正如你所看到的,我们仍然有相同的Tab1Fragment实例,但它有一个新的childFragmentManager,我认为这是不需要的,并且由于下面的答案中报告了错误。
问题是我确实将子片段添加到此新 childFragmentManger。
所以看起来事务永远不会执行对保留的片段的引用,但是如果我创建一个全新的片段就完成了。 (我确实尝试在新的childFragmentManager上调用executePendingTransactions
)
class Tab1Fragment: Fragment() {
var subfragment: DefaultSubfragment? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
if (subfragment == null ) {
subfragment = DefaultSubfragment()
subfragment!!.sectionLabel = "label 1"
subfragment!!.buttonText = "button 1"
}
addRootContentToContainer(R.id.child_container, content = subfragment!!)
return rootView
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
inline fun Fragment.addRootContentToContainer(containerId: Int, content: Fragment) {
val transaction = childFragmentManager.beginTransaction()
transaction.replace(containerId, content)
transaction.commit()
}
答案 0 :(得分:2)
您的问题与此处描述的问题类似:
https://code.google.com/p/android/issues/detail?id=74222
不幸的是,谷歌可能无法修复此问题。
将保留的片段用于UI或嵌套片段不是一个好主意 - 建议使用它们代替onRetainNonConfigurationInstance,即ie。适用于大型馆藏/数据结构。您还可以找到比保留片段更好的Loaders,它们也会在配置更改期间保留。
顺便说一句。我发现保留的片段更像是一个黑客 - 比如使用android:configChanges
来“修复”由屏幕旋转引起的问题。这一切都有效,直到用户按下主屏幕并且android决定杀死你的应用程序进程。一旦用户想要返回您的应用程序 - 您保留的片段将被销毁 - 您仍然需要重新创建它。因此,如果您的资源可以随时被销毁,那么编码所有内容总是更好。
答案 1 :(得分:2)
上面我的问题的接受答案指出了支持库v4中报告的错误,其中嵌套片段(和子片段管理器)在配置更改时不再保留。
其中一个posts提供了解决方法(似乎效果很好)。 解决方法涉及创建Fragment的子类并使用反射。
由于我的原始问题使用了Kotlin代码,我想我会分享我的Kotlin版本的工作,以防其他人点击这个。最后,我不确定我会坚持使用这个解决方案,因为它仍然是一个黑客,它仍然操纵私有字段,但是如果字段名称被更改,错误将在编译时而不是运行时找到。
这种方式是这样的:
以下是一个例子:
// some convenience functions
inline fun Fragment.pushContentIntoContainer(containerId: Int, content: Fragment) {
val transaction = fragmentManager.beginTransaction()
transaction.replace(containerId, content)
transaction.addToBackStack("tag")
transaction.commit()
}
inline fun Fragment.addRootContentToContainer(containerId: Int, content: Fragment) {
val transaction = childFragmentManager.beginTransaction()
transaction.replace(containerId, content)
transaction.commit()
}
// here we address the bug
inline fun Fragment.reattachRetainedChildFragmentManager(childFragmentManager: FragmentManager) {
setChildFragmentManager(childFragmentManager)
updateChildFragmentsHost()
}
fun Fragment.setChildFragmentManager(childFragmentManager: FragmentManager) {
if (childFragmentManager is FragmentManagerImpl) {
mChildFragmentManager = childFragmentManager // mChildFragmentManager is private to Fragment, but the extension can touch it
}
}
fun Fragment.updateChildFragmentsHost() {
mChildFragmentManager.fragments.forEach { fragment -> // fragments is hidden in Fragment
fragment?.mHost = mHost // mHost is private also
}
}
class Tab1Fragment : Fragment() , TabRootFragment {
var subfragment: DefaultSubfragment? = null
var retainedChildFragmentManager: FragmentManager? = null
override val title = "Tab 1"
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
if (subfragment == null ) {
subfragment = DefaultSubfragment()
subfragment!!.sectionLable = "label 1x"
subfragment!!.buttonText = "button 1"
addRootContentToContainer(R.id.child_container, content = subfragment!!)
}
return rootView
}
override fun onAttach(context: Context?) {
super.onAttach(context)
if (retainedChildFragmentManager != null) {
reattachRetainedChildFragmentManager(retainedChildFragmentManager!!)
} else {
retainedChildFragmentManager = childFragmentManager
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
}