【问题标题】:How to retrive data from DialogFragment with ViewModel and Room如何使用 ViewModel 和 Room 从 DialogFragment 检索数据
【发布时间】:2021-05-12 20:18:49
【问题描述】:

我正在构建一个具有不同页面的 Android 应用,这些页面主要包含一些 EditText。我的目标是处理对 EditText 的单击并显示带有 EditText 的 DialogAlert,然后用户可以放置文本,单击“保存”和数据库中的相关字段(我正在使用 Room,并且我已经测试了查询一切正常)将被更新。现在我能够使用界面处理来自 DialogFragment 的文本,但我不知道如何说检索到的文本与我单击的 EditText 相关。做到这一点的最佳方法是什么? 提前感谢您的帮助。

我们以这个片段为例:

class StaticInfoResumeFragment : Fragment(), EditNameDialogFragment.OnClickCallback {

private val wordViewModel: ResumeStaticInfoViewModel by viewModels {
    WordViewModelFactory((requireActivity().application as ManagementCinemaApplication).resumeStaticInfoRepo)
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    val root = inflater.inflate(R.layout.fragment_static_info_resume, container, false)

    wordViewModel.resumeStaticInfo.observe(viewLifecycleOwner) { words ->
        println("test words: $words")
    }

    val testView = root.findViewById<TextInputEditText>(R.id.textInputEditText800)


    testView.setOnClickListener{
        val fm: FragmentManager = childFragmentManager
        val editNameDialogFragment = EditNameDialogFragment.newInstance("Some Title")
        editNameDialogFragment.show(fm, "fragment_edit_name")
    }

    resumeStaticInfoViewModel.firstName.observe(viewLifecycleOwner, Observer {
        testView.setText(it)
    })

    return root
}

override fun onClick(test: String) {
    println("ciao test: $test")
    wordViewModel.updateFirstName(testa)
}}

然后我有 ViewModel:

class ResumeStaticInfoViewModel(private val resumeStaticInfoRepo: ResumeStaticInfoRepo): ViewModel() {

val resumeStaticInfo: LiveData<ResumeStaticInfo> = resumeStaticInfoRepo.resumeStaticInfo.asLiveData()

fun updateFirstName(resumeStaticInfoFirstName: String) = viewModelScope.launch {
    resumeStaticInfoRepo.updateFirstName(resumeStaticInfoFirstName)
}
....

还有 DialogFragment:

class EditNameDialogFragment : DialogFragment() {

private lateinit var callback: OnClickCallback

interface OnClickCallback {
    fun onClick(test: String)
}

override fun onAttach(context: Context) {
    super.onAttach(context)
    try {
        callback = parentFragment as OnClickCallback
    } catch (e: ClassCastException) {
        throw ClassCastException("$context must implement UpdateNameListener")
    }
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val title = requireArguments().getString("title")
    val alertDialogBuilder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
    alertDialogBuilder.setTitle(title)
    val layoutInflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    val alertCustomView = layoutInflater.inflate(R.layout.alert_dialog_edit_item, null)
    val editText = alertCustomView.findViewById<EditText>(R.id.alert_edit)
    alertDialogBuilder.setView(alertCustomView)

    alertDialogBuilder.setPositiveButton(
        "Save",
        DialogInterface.OnClickListener { dialog, which ->
            callback.onClick(editText.text.toString())
        })
    alertDialogBuilder.setNegativeButton("No") { _: DialogInterface, _: Int -> }
    return alertDialogBuilder.create()
}

companion object {
    fun newInstance(title: String?): EditNameDialogFragment {
        val frag = EditNameDialogFragment()
        val args = Bundle()
        args.putString("title", title)
        frag.arguments = args
        return frag
    }
}

}

【问题讨论】:

    标签: android kotlin android-room android-dialogfragment android-mvvm


    【解决方案1】:

    您的意思是您只想显示一个用于输入一些文本的基本对话框,并且您希望能够将其用于多个EditTexts?并且您想要一种让对话框将结果传回的方法,而且还需要某种方法来识别它最初是为哪个 EditText 创建的?

    关于对话框的问题在于它们最终可能会被重新创建(例如,如果应用程序在后台被破坏,然后在用户切换回它时恢复),因此您可以在它上进行唯一真正的配置(无需进入无论如何,有些复杂)是通过它的arguments,就像你对标题文本所做的那样。

    因此,您可以使用的一种方法是将一些标识符参数发送到newInstance,将其存储在参数中,然后将其传递回点击侦听器。因此,您在 onClick 中为回调提供了两条数据 - 输入的文本和最初传入的参考 ID。这样,活动可以处理 ID 并决定如何处理它。

    您可以使用的一个简单值是 EditText 本身的资源 ID,您传递给 findViewById 的资源 ID - 它是唯一的,您可以轻松地使用它来设置视图本身的文本。您在这里使用的是ViewModel,因此当您在其中设置值时它应该会自动更新,但总的来说这是您可以做的事情。


    困难在于您需要在视图模型中存储一些 ID 到函数的映射,以便您可以处理每种情况。这只是使对话框不特定的本质,但它比为要更新的每个属性创建对话框更容易!您可以将其设为 when 块,例如:

    // you don't need the @ResId annotation but it can help you avoid mistakes!
    override fun onClick(text: String, @ResId id: Int) {
        when(id) {
            R.id.coolEditText -> viewModel.setCoolText(text)
            ...
        }
    } 
    

    您在哪里列出所有案例以及每个案例的要求。你也可以制作这样的地图

    val updateFunctions = mapOf<Int, (String) -> Unit>(
        R.id.coolEditText to viewModel::setCoolText
    )
    

    然后在您的onClick 中,您可以调用updateFunctions[id]?.invoke(text) 来获取该EditText 的相关函数并使用数据调用它。 (或者使用get,如果EditText 未添加到地图中,则会引发异常,这是您想要得到警告的设计错误,而不是默默地忽略它,这是空检查所做的)

    【讨论】:

    • 感谢您的回答。在等待答复的同时,我已经按照您的建议进行了操作。我向传递视图 id 的 newInstance 函数添加了另一个参数。然后在 Fragment 中,我使用了 when 在各种 editText 之间切换。我还在 ViewModel 和片段中添加了实时数据:val myVM: LiveData&lt;ResumeStaticInfo&gt; = resumeStaticInfoRepo.resumeStaticInfo.asLiveData(),我添加了观察者:myVM.getAllData.observe(viewLifecycleOwner, editTest.setText(it.first) editTest2.setText(it.last) } )
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多