片段在方向改变时失去听众

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)        
    }
}

希望对您有所帮助!