【问题标题】:How to give business logic to the ViewModel Kotlin?如何将业务逻辑赋予 ViewModel Kotlin?
【发布时间】:2021-07-20 11:34:43
【问题描述】:

我作为一名学生正在从事一个培训项目,但我不知道如何将我的业务逻辑从NewItemActivity 转移到NewItemViewModel。事实证明,我拥有在片段内验证和创建 ItemModel 的所有逻辑。这样做不好,这些都是业务逻辑中需要交给viewModel的部分。如何将业务逻辑转移到 ViewModel,但同时保持应用程序正常工作?否则我试过了,一切都毁了我。

NewItemActivity.kt

class NewItemActivity : AppCompatActivity() {

    private val calendar: Calendar = Calendar.getInstance()
    private val viewModel: NewItemViewModel by viewModels(factoryProducer = {
        NewItemViewModel.Factory()
    })

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_new_item)

        val saveButton = findViewById<Button>(R.id.saveButton)
        val editDate = findViewById<EditText>(R.id.editDate)

        val date = SimpleDateFormat("dd.MM.yyyy")
        val dateDefault = date.format(calendar.timeInMillis)
        editDate.setText(dateDefault)

        editDate.setOnClickListener {
            showDatePickerDialog()
        }

        saveButton.setOnClickListener {
            checkStateDescriptionLayout()

            if (checkStateTitleLayout()) return@setOnClickListener
            if (checkStateDescriptionLayout()) return@setOnClickListener

            val newItem = ItemModel(
                title = editTitle.text.toString(),
                description = editDescription.text.toString(),
                date = Date(),
                isFavorite = false
            )

            viewModel.saveNewItem(newItem)

            Toast.makeText(this, "New item added", Toast.LENGTH_SHORT).show()
            finish()
        }

        textChangedListener()
    }

    private fun showDatePickerDialog() {
        val datePickerDialog = DatePickerDialog(
            this@NewItemActivity,
            { _, year, monthOfYear, dayOfMonth ->
                val selectedDate: String =
                    dayOfMonth.toString() + "." + (monthOfYear + 1) + "." + year
                editDate?.setText(selectedDate)
            },
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        )
        datePickerDialog.datePicker.maxDate = calendar.timeInMillis
        datePickerDialog.show()
    }

    private fun checkStateTitleLayout(): Boolean {
        val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
        val checkTitleLayoutState = titleLayout.editText?.text?.toString()
        val fieldIsRequired = getString(R.string.fieldIsRequired)

        val error: Boolean = checkTitleLayoutState!!.isEmpty()
        if (error) titleLayout.error = fieldIsRequired

        return error
    }

    private fun checkStateDescriptionLayout(): Boolean {
        val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)
        val checkDescriptionLayoutState = descriptionLayout.editText?.text?.toString()
        val fieldIsRequired = getString(R.string.fieldIsRequired)

        val error: Boolean = checkDescriptionLayoutState!!.isEmpty()
        if (error) descriptionLayout.error = fieldIsRequired

        return error
    }

    private fun textChangedListener() {
        val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
        val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)

        titleLayout.editText?.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(s: Editable?) {
                titleLayout.error = null
                titleLayout.isErrorEnabled = false
            }
        })

        descriptionLayout.editText?.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(s: Editable?) {
                descriptionLayout.error = null
                descriptionLayout.isErrorEnabled = false
            }
        })
    }
}

NewItemViewModel.kt

class NewItemViewModel(
    private val repository: MyItemsRepository
) : ViewModel() {

    fun saveNewItem(item: ItemModel) = repository.saveNewItem(item)

    class Factory : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
        }
    }
}

【问题讨论】:

  • 基本答案是,您的每个点击侦听器应该只在视图模型中调用一个函数。如果您遇到特定问题,我建议缩小您的问题范围,否则我认为您将无法获得任何有用的答案。
  • @Tenfour04 我了解需要将验证和显示的逻辑分开?
  • 您可以使用任何您喜欢的设计策略。您不需要以任何特定方式进行操作。也许你可以更具体地了解你到底坚持了什么。

标签: android kotlin fragment android-viewmodel


【解决方案1】:

看起来你的主要业务逻辑包含在onCreate中,当你的业务逻辑很少时这不是什么大问题,而且你的逻辑基本上是一个事件(保存ItemModel对象)。

如果您仍想将更多逻辑移至 ViewModel,请将 ItemModel 的创建移至 ViewModel,并提供函数来更改那里的 ItemModel 的每个参数。它应该是这样的:

class NewItemViewModel(
    private val repository: MyItemsRepository
) : ViewModel() {

    private var title = ""
    private var description = ""
    private var date = Date()
    private var isFavorite = false

    fun setTitle(s: String) { title = s }
    fun setDescription(s: String) { description = s }
    fun setDate(d: Date) { date = d }
    fun setIsFavorite(b: Boolean) { isFavorite = b }
    fun saveNewItem() = repository.saveNewItem(Item(title, description, date, isFavorite))

    class Factory : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
        }
    }
}

你可能会认为这比它的价值更麻烦,你是对的。但是,为更复杂的逻辑做准备并没有什么坏处;)

附:这段代码我没有测试过,但应该基本正确。

【讨论】:

  • 没有理由用冗余函数重载您的属性。
  • 我了解需要将验证和显示的逻辑分开?
  • @Tenfour04 如何提高工作效率?
  • @Anderson 在逻辑很少的活动上跳过 ViewModel 是可以的。我有一个包含五个活动的应用程序,但我跳过了一个与您的活动非常相似的活动(获取两条信息以注册到数据库)。或者,您可以将您的 Activity 转换为片段以共享 ViewModel。
  • 不,您应该删除 setter 函数。 Kotlin 使用属性而不是 getter 和 setter。这是 Kotlin 的核心概念。
猜你喜欢
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2017-08-09
  • 2014-05-23
  • 2018-11-10
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
相关资源
最近更新 更多