片段在方向改变时失去听众
Fragment loses listener at orientation change
我有一个 activity 使用片段。为了从片段与 activity 进行通信,我使用了接口。这是简化的代码:
Activity:
class HomeActivity : AppCompatActivity(), DiaryFragment.IAddEntryClickedListener, DiaryFragment.IDeleteClickedListener {
override fun onAddEntryClicked() {
//DO something
}
override fun onEntryDeleteClicked(isDeleteSet: Boolean) {
//Do something
}
private val diaryFragment: DiaryFragment = DiaryFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
diaryFragment.setOnEntryClickedListener(this)
diaryFragment.setOnDeleteClickedListener(this)
supportFragmentManager.beginTransaction().replace(R.id.content_frame, diaryFragment)
}
}
片段:
class DiaryFragment: Fragment() {
private var onEntryClickedListener: IAddEntryClickedListener? = null
private var onDeleteClickedListener: IDeleteClickedListener? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_diary, container, false)
//Some user interaction
onDeleteClickedListener!!.onEntryDeleteClicked()
onDeleteClickedListener!!.onEntryDeleteClicked()
return view
}
interface IAddEntryClickedListener {
fun onAddEntryClicked()
}
interface IDeleteClickedListener {
fun onEntryDeleteClicked()
}
fun setOnEntryClickedListener(listener: IAddEntryClickedListener) {
onEntryClickedListener = listener
}
fun setOnDeleteClickedListener(listener: IDeleteClickedListener) {
onDeleteClickedListener = listener
}
}
这有效,但是当片段处于活动状态并且方向从纵向变为横向或其他方式时,侦听器为空。我不能将它们放到 savedInstanceState 中,或者我能以某种方式吗?或者有其他方法可以解决这个问题吗?
无需重写代码的简短回答是,您必须在 activiy 恢复时恢复侦听器,并且当您检测到 activity 失去焦点时,您 "should" 将其删除。 activity 视图完全销毁并在旋转时重新绘制,因此自然不会有全新对象上的事件。
旋转时,"onDestroy" 会在其他任何事情发生之前被调用。重建时,调用 "onCreate" 。 (参见 https://developer.android.com/guide/topics/resources/runtime-changes)
这样做的原因之一是,没有任何东西会强制您在旋转后使用相同的布局。可能有不同的控件。
您真正需要做的就是确保您的事件挂钩在 OnCreate 中分配。
有关在 oncreate 中分配事件的示例,请参阅此问题的答案。
onSaveInstanceState not working
您的问题:
当你切换方向时,系统会为你保存和恢复碎片的状态。但是,您没有在代码中考虑到这一点,实际上您最终得到了片段的两个 (!!) 实例 - 一个是系统恢复的(没有侦听器),另一个是您自己创建的。当您观察到该片段的侦听器为空时,这是因为为您恢复的实例尚未重置其侦听器。
解决方案
首先,阅读the docs on how you should structure your code。
然后将您的代码更新为如下内容:
class HomeActivity : AppCompatActivity(), DiaryFragment.IAddEntryClickedListener, DiaryFragment.IDeleteClickedListener {
override fun onAddEntryClicked() {
//DO something
}
override fun onEntryDeleteClicked(isDeleteSet: Boolean) {
//Do something
}
// DO NOT create new instance - only if starting from scratch
private lateinit val diaryFragment: DiaryFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
// Null state bundle means fresh activity - create the fragment
if (savedInstanceState == null) {
diaryFragment = DiaryFragment()
supportFragmentManager.beginTransaction().replace(R.id.content_frame, diaryFragment)
}
else { // We are being restarted from state - the system will have
// restored the fragment for us, just find the reference
diaryFragment = supportFragmentManager().findFragment(R.id.content_frame)
}
// Now you can access the ONE fragment and set the listener on it
diaryFragment.setOnEntryClickedListener(this)
diaryFragment.setOnDeleteClickedListener(this)
}
}
希望对您有所帮助!
我有一个 activity 使用片段。为了从片段与 activity 进行通信,我使用了接口。这是简化的代码:
Activity:
class HomeActivity : AppCompatActivity(), DiaryFragment.IAddEntryClickedListener, DiaryFragment.IDeleteClickedListener {
override fun onAddEntryClicked() {
//DO something
}
override fun onEntryDeleteClicked(isDeleteSet: Boolean) {
//Do something
}
private val diaryFragment: DiaryFragment = DiaryFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
diaryFragment.setOnEntryClickedListener(this)
diaryFragment.setOnDeleteClickedListener(this)
supportFragmentManager.beginTransaction().replace(R.id.content_frame, diaryFragment)
}
}
片段:
class DiaryFragment: Fragment() {
private var onEntryClickedListener: IAddEntryClickedListener? = null
private var onDeleteClickedListener: IDeleteClickedListener? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_diary, container, false)
//Some user interaction
onDeleteClickedListener!!.onEntryDeleteClicked()
onDeleteClickedListener!!.onEntryDeleteClicked()
return view
}
interface IAddEntryClickedListener {
fun onAddEntryClicked()
}
interface IDeleteClickedListener {
fun onEntryDeleteClicked()
}
fun setOnEntryClickedListener(listener: IAddEntryClickedListener) {
onEntryClickedListener = listener
}
fun setOnDeleteClickedListener(listener: IDeleteClickedListener) {
onDeleteClickedListener = listener
}
}
这有效,但是当片段处于活动状态并且方向从纵向变为横向或其他方式时,侦听器为空。我不能将它们放到 savedInstanceState 中,或者我能以某种方式吗?或者有其他方法可以解决这个问题吗?
无需重写代码的简短回答是,您必须在 activiy 恢复时恢复侦听器,并且当您检测到 activity 失去焦点时,您 "should" 将其删除。 activity 视图完全销毁并在旋转时重新绘制,因此自然不会有全新对象上的事件。
旋转时,"onDestroy" 会在其他任何事情发生之前被调用。重建时,调用 "onCreate" 。 (参见 https://developer.android.com/guide/topics/resources/runtime-changes) 这样做的原因之一是,没有任何东西会强制您在旋转后使用相同的布局。可能有不同的控件。 您真正需要做的就是确保您的事件挂钩在 OnCreate 中分配。 有关在 oncreate 中分配事件的示例,请参阅此问题的答案。 onSaveInstanceState not working
您的问题:
当你切换方向时,系统会为你保存和恢复碎片的状态。但是,您没有在代码中考虑到这一点,实际上您最终得到了片段的两个 (!!) 实例 - 一个是系统恢复的(没有侦听器),另一个是您自己创建的。当您观察到该片段的侦听器为空时,这是因为为您恢复的实例尚未重置其侦听器。
解决方案
首先,阅读the docs on how you should structure your code。 然后将您的代码更新为如下内容:
class HomeActivity : AppCompatActivity(), DiaryFragment.IAddEntryClickedListener, DiaryFragment.IDeleteClickedListener {
override fun onAddEntryClicked() {
//DO something
}
override fun onEntryDeleteClicked(isDeleteSet: Boolean) {
//Do something
}
// DO NOT create new instance - only if starting from scratch
private lateinit val diaryFragment: DiaryFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
// Null state bundle means fresh activity - create the fragment
if (savedInstanceState == null) {
diaryFragment = DiaryFragment()
supportFragmentManager.beginTransaction().replace(R.id.content_frame, diaryFragment)
}
else { // We are being restarted from state - the system will have
// restored the fragment for us, just find the reference
diaryFragment = supportFragmentManager().findFragment(R.id.content_frame)
}
// Now you can access the ONE fragment and set the listener on it
diaryFragment.setOnEntryClickedListener(this)
diaryFragment.setOnDeleteClickedListener(this)
}
}
希望对您有所帮助!