简体   繁体   English

当 DialogFragment 以屏幕旋转变化打开时,Observer 会被多次调用

[英]Observer is called multiple times when DialogFragment is opened with screen rotation changes

I have an activity that implements an observer inside the OnCreate for a MutableLiveData variable that is in a ViewModel.我有一个活动,它在 OnCreate 内为 ViewModel 中的 MutableLiveData 变量实现观察者。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    subscribeDialogFragmentManualInput()
}

private fun subscribeDialogFragmentManualInput() {
    this.sharedViewModel.inputBarcode.observe(this) { inputValue ->
        postInput(inputValue)
    }
}

My activity is always in landscape mode (default in Manifest) and when a button is pressed a DialogFragment is opened and it changes the rotation to Portrait, when it is closed the activity returns to Landscape mode.我的活动始终处于横向模式(Manifest 中的默认模式),当按下按钮时,会打开 DialogFragment 并将旋转更改为纵向,当它关闭时,活动将返回横向模式。

private fun showInvoiceInputDialog() {
    val inputDialog = InvoiceInputDialogFragment
    val transaction = supportFragmentManager.beginTransaction()
    inputDialog.show(transaction, InvoiceInputDialogFragment.TAG)
}
class InvoiceInputDialogFragment : DialogFragment() {

    lateinit var binding: DialogFragmentInvoiceInputBinding
    private val sharedViewModel by sharedViewModel<ManualInvoiceInputViewModel>()
    private var invoiceInput: String = ""
...


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        setStyle(
            STYLE_NORMAL,
            R.style.FullScreenDialog
        )

        binding = DataBindingUtil.inflate(
            inflater,
            R.layout.dialog_fragment_invoice_input,
            container,
            false
        )

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ...

        activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT

        setViews()
    }

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)

        activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        if(invoiceInput.isNotEmpty()) {
            sharedViewModel.sendInputInvoice(invoiceInput)
        }
    }

    private fun setViews() {
        binding.btConfirmInput.setOnClickListener {
            invoiceInput = binding.scannerManualInput.text
            dismiss()
        }
    }
}

This flow makes the observer triggered multiple times making my app having and undesirable comportment because the livedata result open a dialog and it is displayed multiple times.此流程使观察者多次触发,使我的应用程序具有不良行为,因为实时数据结果会打开一个对话框并多次显示。

I want to call postInput method only one time after DialogFragment is closed.我只想在 DialogFragment 关闭后调用postInput方法一次。

Thanks!谢谢!

LiveData is by default setup in near circular logic. LiveData默认设置为近乎循环的逻辑。 This raises some problems when the view updates LiveData the LiveData will then want to update the view again, creating an infinite loop.当视图更新LiveData时,这会引发一些问题,然后LiveData将要再次更新视图,从而创建一个无限循环。 Although you are doing it indirectly by provoking the Android Lifecycle to re-create your Activity and when its re-created it attaches another observer thus re-emitting the old value.尽管您是通过激发Android Lifecycle来重新创建您的Activity来间接执行此操作的,但在重新创建Activity时,它会附加另一个观察者,从而重新发出旧值。

You have 2 options:您有 2 个选择:

Save the fact that the dialog was already called in the savedInstanceState Bundle保存已经在savedInstanceState Bundle调用对话框的事实

overide fun onSaveInstanceState(savedInstanceState : Bundle?) {
// Save the user's current game state
 savedInstanceState.putBoolean("dialogCausedRecreate", dialogWasCreated);
 super.onSaveInstanceState(savedInstanceState); 
} 

//in onCreate
this.sharedViewModel.inputBarcode.observe(this) { inputValue ->
    if(!savedInstanceState.getBoolean("dialogCausedRecreate",false)){
         postInput(inputValue)
    }
}

OR或者

Create a new wrapper class that can keeps track if its been emitted.创建一个新的包装类,它可以跟踪它是否被发出。 Here is an example LiveData prevent receive the last value when start observing这是一个示例LiveData 在开始观察时阻止接收最后一个值

The accepted answer will have you wrap your barcode inside an Event class and that will allow you to tell if it already been emitted previously.接受的答案将让您将条形码包装在一个 Event 类中,这将允许您判断它之前是否已经发出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM