EmptyDatabaseAlert显示两次

问题描述 投票:0回答:1

我有一个RecyclerView片段,它执行房间操作的ViewModel-add()。如果数据库为空,则该Fragment应该显示一个AlertDialog,允许用户关闭或创建一个新条目。

CrimeListFragment和相关位:

class CrimeListFragment:
    Fragment(),
    EmptyAlertFragment.Callbacks {

    interface Callbacks {
        fun onCrimeClicked(crimeId: UUID)
    }

    //==========

    private var callback: Callbacks? = null
    private lateinit var crimeRecyclerView: RecyclerView
    private val crimeListViewModel: CrimeListViewModel by lazy {
        ViewModelProviders.of(this).get(CrimeListViewModel::class.java)
    }

    //==========

    override fun onAttach(context: Context) {
        super.onAttach(context)

        callback = context as Callbacks?
    }

    override fun onCreate(savedInstanceState: Bundle?) {}

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {}

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

        crimeListViewModel.crimeListLiveData.observe( //crimeListLiveData: LiveData<List<Crime>>
            viewLifecycleOwner,
            Observer { crimes ->
                crimes?.let {
                    Log.i(TAG, "Retrieved ${crimes.size} crimes.")
                    updateUI(crimes)
                }
            }
        )
    }

    override fun onDetach() {
        super.onDetach()

        callback = null
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {}

    override fun onOptionsItemSelected(item: MenuItem): Boolean {}

    override fun onCreateSelected() = createNewCrime()

    //==========

    private fun updateUI(crimes: List<Crime>) {
        if(crimes.isEmpty()) {
            Log.d(TAG, "empty crime list, show empty dialog")
            showEmptyDialog()
        }

        (crimeRecyclerView.adapter as CrimeListAdapter).submitList(crimes)
        Log.d(TAG, "list submitted")
    }

    private fun showEmptyDialog() {
        Log.d(TAG, "show empty dialog")
        EmptyAlertFragment.newInstance().apply {
            setTargetFragment(this@CrimeListFragment, REQUEST_EMPTY)
            show([email protected](), DIALOG_EMPTY)
        }
    }

    private fun createNewCrime() {
        val crime = Crime()
        crimeListViewModel.addCrime(crime)
        callback?.onCrimeClicked(crime.id)
        Log.d(TAG, "new crime added")
    }

    //==========

    companion object {}

    //==========

    private inner class CrimeHolder(view: View)
        : RecyclerView.ViewHolder(view), View.OnClickListener {}

    private inner class CrimeListAdapter
        : ListAdapter<Crime, CrimeHolder>(DiffCallback()) {}

    private inner class DiffCallback: DiffUtil.ItemCallback<Crime>() {}
}

我的EmptyAlertFragment

class EmptyAlertFragment: DialogFragment() {

    interface Callbacks {
        fun onCreateSelected()
    }

    //==========

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity!!)

        builder.setPositiveButton("Create") {
                _, _ ->
            targetFragment?.let { fragment ->
                (fragment as Callbacks).onCreateSelected()
            }
        }
        builder.setNegativeButton("Cancel") {
                dialog, _ ->
            dialog.dismiss()
        }

        val alert = builder.create()

        alert.apply {
            setTitle("Crime list empty!")
            setMessage("Do you want to create a new crime?")
        }

        return alert
    }

    //==========

    companion object {
        fun newInstance(): EmptyAlertFragment {
            return EmptyAlertFragment()
        }
    }
}

最后是我的MainActivity

class MainActivity:
    AppCompatActivity(),
    CrimeListFragment.Callbacks {

    override fun onCreate(savedInstanceState: Bundle?) {}

    //==========

    override fun onCrimeClicked(crimeId: UUID) {
        val crimeFragment = CrimeDetailFragment.newInstance(crimeId)

        supportFragmentManager
            .beginTransaction()
            .replace(R.id.fragment_container, crimeFragment)
            .addToBackStack("crime")
            .commit()
    }
}

基本上流程是这样:

  1. [App启动,CrimeListFragment观察数据库,updateUI()被调用,数据库为空,因此又弹出警报显示EmptyAlertFragment,单击创建-> onCreateSelected()回调到CrimeListFragment
  2. [onCreateSelected()调用createNewCrime(),后者使用ViewModel添加犯罪(房间,存储库模式),onCrimeClicked()回调到MainActivity
  3. MainActivity启动CrimeDetailFragment,其中显示了现有的或空白的(新的)犯罪供我们填充。我们将其填充并单击返回,可以保存犯罪:CrimeDetailFragment-onStop() { super.onStop; crimeDetailViewModel.saveCrime(crime) }
  4. 数据库已更新,CrimeListFragment观察到数据库更改,updateUI()被调用,数据库不为空,因此警报不应弹出,但会弹出。
  5. 我再次单击创建,再次犯下罪行,请点击,警报将不会再显示。

换句话说,警报显示的次数太多了。

Logcat显示此:

`Retrieved 0 crimes`
`empty crime list, show empty dialog`
`show empty dialog`
`list submitted`
`*(I add a crime)*`
`new crime added`
`Retrieved 0 crimes` <--- Why? I just created a crime, Observer should notify and `updateUI()` should get called with a non-empty list
`empty crime list, show empty dialog`
`show empty dialog`
`list submitted`
`Retrieved 1 crimes.` <--- Correct behavior from here on out

为什么我的对话框弹出两次而不是一次?

android kotlin android-alertdialog android-room observers
1个回答
0
投票

这是由于LiveData的工作原理:它在查询更新数据之前缓存并返回最后一个值。

[您的CrimeListFragment第一次开始observe时,它会得到一个空列表,正确显示您的对话框。

当您转到crimeListLiveData时,CrimeDetailFragment被破坏[[not。它保留了现有值-您的空白列表。

因此,当您返回crimeListViewModel.crimeListLiveData时,CrimeListFragment再次运行,并且您再次开始观察。 onCreateView()立即返回其拥有的缓存值,Room异步启动查询更新数据。因此,期望您首先获得一个空列表,然后再获取更新的非空列表。

[如果您在屏幕上显示LiveData而在EmptyAlertFragment后面的情况下旋转设备,则会看到相同的行为-由于相同的原因,您最终将创建第二个CrimeListFragment副本。如果继续旋转设备,则再按第三,第四,第五等等。

根据EmptyAlertFragment,对话框用于提供关键信息或重要决策,因此,满足“创建新犯罪”要求的最合适解决方案是根本不使用对话框,而在您的计算机中使用Material design guidelines for dialogs empty stateCrimeListFragment并排。然后,您的Floating Action Button方法将根据计数简单地在空状态和非空RecyclerView之间切换。

[另一种选择是updateUI应该跟踪是否已在CrimeListFragment字段中显示对话框,将该布尔值保存到booleanBundle中以确保其在旋转和过程死亡后仍然有效。 /娱乐。这样,您可以确保只显示给定onSaveInstanceState()的对话框一次。

© www.soinside.com 2019 - 2024. All rights reserved.