当 DialogFragment 通过屏幕旋转更改打开时,多次调用观察者

Posted

技术标签:

【中文标题】当 DialogFragment 通过屏幕旋转更改打开时,多次调用观察者【英文标题】:Observer is called multiple times when DialogFragment is opened with screen rotation changes 【发布时间】:2021-12-27 03:41:19 【问题描述】:

我有一个在 OnCreate 中为 ViewModel 中的 MutableLiveData 变量实现观察者的活动。

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


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

我的 Activity 始终处于横向模式(Manifest 中的默认模式),当按下按钮时,DialogFragment 会打开并将旋转更改为纵向,当它关闭时,Activity 会返回横向模式。

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

此流程使观察者多次触发,使我的应用程序出现不良行为,因为 livedata 结果打开一个对话框并多次显示。

我只想在 DialogFragment 关闭后调用一次postInput 方法。

谢谢!

【问题讨论】:

您能帮忙附上sharedViewModel.inputBarcode.postValue()sharedViewModel.inputBarcode.setValue() 的代码吗?并确保那些没有被多次触发 【参考方案1】:

LiveData 默认设置为近似循环逻辑。当视图更新LiveData 时,这会引发一些问题,LiveData 然后会想要再次更新视图,从而创建一个无限循环。尽管您是通过激发 android Lifecycle 重新创建您的 Activity 来间接做到这一点的,但当它重新创建时,它会附加另一个观察者,从而重新发出旧值。

你有两个选择:

保存对话框已在 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)
    

创建一个新的包装类,它可以跟踪它是否被发出。 这是一个例子 LiveData prevent receive the last value when start observing

接受的答案将让您将条形码包装在 Event 类中,这样您就可以判断它之前是否已经发出过。

【讨论】:

谢谢,我决定使用包装类,它正在工作

以上是关于当 DialogFragment 通过屏幕旋转更改打开时,多次调用观察者的主要内容,如果未能解决你的问题,请参考以下文章

使用 DialogFragment 创建对话框

如何通过旋转正确保留 DialogFragment?

旋转时调用 DatePickerDialog onDateSet

Android屏幕旋转不生效

重叠DialogFragment,在方向更改时以错误的顺序重新创建

安卓片段。在屏幕旋转或配置更改期间保留 AsyncTask