I try to learn the MVVM Architecture by implementing a very simple app that takes three inputs from the user and stores them in a Room Database then display the data in a RecyclerView.
From the first try it seems to work well, then the app crashes if one of the inputs is left empty. Now, I want to add some input validations (for now the validations must just check for empty string), but I can't figure it out. I found many answers on stackoverflow and some libraries that validates the inputs, but I couldn't integrate those solutions in my app (most probably it is due to my poor implementation of the MVVM).
This is the code of my ViewModel:
class MetricPointViewModel(private val repo: MetricPointRepo): ViewModel(), Observable {
val points = repo.points
@Bindable
val inputDesignation = MutableLiveData<String>()
@Bindable
val inputX = MutableLiveData<String>()
@Bindable
val inputY = MutableLiveData<String>()
fun addPoint(){
val id = inputDesignation.value!!.trim()
val x = inputX.value!!.trim().toFloat()
val y = inputY.value!!.trim().toFloat()
insert(MetricPoint(id, x , y))
inputDesignation.value = null
inputX.value = null
inputY.value = null
}
private fun insert(point: MetricPoint) = viewModelScope.launch { repo.insert(point) }
fun update(point: MetricPoint) = viewModelScope.launch { repo.update(point) }
fun delete(point: MetricPoint) = viewModelScope.launch { repo.delete(point) }
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}
}
and this is the fragment where everything happens:
class FragmentList : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
//Binding object
private lateinit var binding: FragmentListBinding
//Reference to the ViewModel
private lateinit var metricPointVm: MetricPointViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Setting up the database
val metricPointDao = MetricPointDB.getInstance(container!!.context).metricCoordDao
val repo = MetricPointRepo(metricPointDao)
val factory = MetricPointViewModelFactory(repo)
metricPointVm = ViewModelProvider(this, factory).get(MetricPointViewModel::class.java)
// Inflate the layout for this fragment
binding = FragmentListBinding.inflate(inflater, container, false)
binding.apply {
lifecycleOwner = viewLifecycleOwner
myViewModel = metricPointVm
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerview()
}
private fun displayPoints(){
metricPointVm.points.observe(viewLifecycleOwner, Observer {
binding.pointsRecyclerview.adapter = MyRecyclerViewAdapter(it) { selecteItem: MetricPoint -> listItemClicked(selecteItem) }
})
}
private fun initRecyclerview(){
binding.pointsRecyclerview.layoutManager = LinearLayoutManager(context)
displayPoints()
}
private fun listItemClicked(point: MetricPoint){
Toast.makeText(context, "Point: ${point._id}", Toast.LENGTH_SHORT).show()
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment FragmentList.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
FragmentList().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
I'm planning also to add a long click to the recyclerview and display a context menu in order to delete items from the database. Any help would be appreciated.
My recycler view adapter implementation:
class MyRecyclerViewAdapter(private val pointsList: List<MetricPoint>,
private val clickListener: (MetricPoint) -> Unit): RecyclerView.Adapter<MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding: RecyclerviewItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.recyclerview_item, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(pointsList[position], clickListener)
}
override fun getItemCount(): Int {
return pointsList.size
}
}
class MyViewHolder(private val binding: RecyclerviewItemBinding): RecyclerView.ViewHolder(binding.root){
fun bind(point: MetricPoint, clickListener: (MetricPoint) -> Unit){
binding.idTv.text = point._id
binding.xTv.text = point.x.toString()
binding.yTv.text = point.y.toString()
binding.listItemLayout.setOnClickListener{
clickListener(point)
}
}
}
question from:
https://stackoverflow.com/questions/65848205/input-validation-with-mvvm-and-data-binding 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…