Hello I am trying to run a BMI calculator with a ViewModel, yet I haven't had a successful run. I am new to coding in kotlin and android studio. Any help would be much appreciated. I am hoping it is just a small bug. I think the issue lies in the ViewModel code yet, I am un sure. As in my logcat is Caused by: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
ViewModel Code:
import android.util.Log
import androidx.lifecycle.ViewModel
lateinit var binding: ActivityMainBinding
private const val TAG = "inc, wt, ft"
class bmiViewModel() : ViewModel() {
val inc =(binding.inch.text.toString()).toInt()
val wt =(binding.lbs.text.toString()).toInt()
val ft =(binding.feet.text.toString()).toInt()
val total = formula(inc,ft,wt)
private fun formula(inc: Int, ft: Int, wt: Int): Float {
val htTotal = (ft.toFloat() / 12) inc.toFloat()
val BMI = (wt.toFloat()*703) / (htTotal * htTotal)
Log.d(TAG,"BMI", Exception())
return BMI
}
fun updatebmi(): Unit{
if (total < 18.5) {
binding.status.text = "Under Weight"
} else if (total >= 18.5 && total < 24.9) {
binding.status.text = "Healthy"
} else if (total >= 24.9 && total < 30) {
binding.status.text = "Overweight"
} else if (total >= 30){
binding.status.text = "Obese"
}
}
}
MainActivity:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val bmiViewModel: bmiViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "OnCreate(Bundle?) called")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d(TAG, "Got a BMIViewModel: $bmiViewModel")
binding.calc.setOnClickListener {
if(binding.height.text.isNotEmpty() && binding.weight.text.isNotEmpty()){
bmiViewModel.updatebmi()
binding.bmiTV.visibility = View.VISIBLE
binding.status.visibility = View.VISIBLE
}
else {
Toast.makeText(this,
"Please add a Height & Weight", Toast.LENGTH_SHORT).show()
}
}
binding.clear.setOnClickListener {
reset()
}
}
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart() called")
}
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume() called")
}
override fun onPause() {
super.onPause()
Log.d(TAG, "onPause() called")
}
override fun onStop() {
super.onStop()
Log.d(TAG, "onStop() called")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy() called")
}
private fun reset() {
binding.inch.text.clear()
binding.feet.text.clear()
binding.lbs.text.clear()
binding.status.text = " "
binding.bmiTV.visibility = View.GONE
}
}
CodePudding user response:
In your bmiViewModel
, you have:
lateinit var binding: ActivityMainBinding
That will not work, for two reasons:
- You are not assigning a value to it before trying to use it
- You can't assign a value to it, as view binding is a UI thing for activities and fragments, not viewmodels
Rewrite it as:
import android.util.Log
import androidx.lifecycle.ViewModel
private const val TAG = "inc, wt, ft"
class bmiViewModel() : ViewModel() {
private fun formula(inc: Int, ft: Int, wt: Int): Float {
val htTotal = (ft.toFloat() / 12) inc.toFloat()
val BMI = (wt.toFloat()*703) / (htTotal * htTotal)
Log.d(TAG,"BMI", Exception())
return BMI
}
}
Move the updatebmi()
code into your activity, as it is updating widgets.