I have an activity with two spinners. I have made arrays for each spinner containing data from popular foods, but I want the user to be able to add three of their own selections to the lists. The app compiles and installs and runs, BUT when I select the specific activity, the screen closes and either goes to the apps main screen or to the emulator's home screen. Logcat shows:-
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.kotlinsql/com.example.kotlinsql.CarbsInput}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
It's where I call the shared preferences. I have tried different contexts but still get errors that vary slightly according to the context and I have included them in the code as remarks.
I have tried moving everything into onCreate, but this gives me an error in the class definition line, because the function "override fun onItemSelected" seems to have to be stand-alone, so must be outside onCreate.
Please help. I have only been learning this for less than a year, and I apologise for any stupid mistakes. No offence is intended.
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.*
import kotlinx.android.synthetic.main.input_carbs.*
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import android.content.SharedPreferences
import android.content.res.Configuration
import java.security.AccessController.getContext
import kotlin.math.*
class CarbsInput : AppCompatActivity(),AdapterView.OnItemSelectedListener {
var spinner:Spinner? = null
var spinner2:Spinner? = null
val sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
val dataModel: CarbsInputModel by lazy { CarbsInputModel(sharedPreferences) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.input_carbs)
spinner = this.gi_spinner
spinner2 = this.carbs_per_spinner
// Create an ArrayAdapter using a simple spinner layout and gIndices array
val aa = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.gIndices)
// Set layout to use when the list of choices appear
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spinner!!.setAdapter(aa)
//spinner!!.setSelection(9)//optional, better to leave favourites at top
val aa2 = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.carbsPer)
aa2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner2!!.setAdapter(aa2)
input_carbs_btn.setOnClickListener {//set an onclick listener
enterCarbs() }
backbtn.setOnClickListener {
val fourth = Intent(this, MainActivity::class.java)//sets "fourth" to be MainActivity
// start your next activity
startActivity(fourth) }
btn_view_carbs.setOnClickListener { viewCarbs() }
btn_carb_calc.setOnClickListener {
var carbPer = et_carbsper.text.toString().toLong()
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())
}//end of button onClick listener
}//end of on create
fun enterCarbs(){//get inputs from keys and calculate carbLife using GI
var noow = ZonedDateTime.now(Clock.systemUTC())
var noowSecs: Long = noow.toEpochSecond()
var noowMins: Long = (noowSecs) / 60
//var carbLife:Long = 220// this has to be calculated from GI
var nowLocal = LocalDateTime.now()
var carbTime: Long = noowMins-1
var showCarbTime: String = nowLocal.format(DateTimeFormatter.ofPattern("E d MMM kk:mm "))+"local"
var sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences = getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
if (et_carbs.text.toString().trim() != "" && et_carbGI.text.toString().trim() != "") {
val carbs = et_carbs.text.toString().toLong()
val carbGi = (et_carbGI.text.toString().toLong())
//val carbLife = 12_000 /carbGi.toLong()// to be replaced with 1-(X/L)^n calculation in stage2
var carbDecayIndex:Double= sharedPreferences.getFloat("carbDecayIndex_key",0.8F).toDouble()//n
//public fun carbLifeCalc():Double//L = 10^((log120^n-logGI)/n)
var logLtoN = log10(120.00.pow(carbDecayIndex))//log120^n
var logGi = log10(carbGi / 100.00)//logGI
var carbLife = 10.00.pow((logLtoN - logGi) / carbDecayIndex).toLong()//gives L
//end of carbLifeCalculation
val status =
databaseHandler.saveCarbs(CarbsModelClass(carbTime, showCarbTime, carbs, carbGi, carbLife))
if (status > -1) {
Toast.makeText(applicationContext, "Carbohydrate saved", Toast.LENGTH_LONG).show()
//MainActivity.evaluateCarbs //want to call this function from here without writing it again
et_carbs.text.clear()
et_carbGI.text.clear()
}
} else {
Toast.makeText(
applicationContext,
"No field can be blank enter GI as 50 if unknown",
Toast.LENGTH_LONG
).show()
}
}//end of function entercarbs
fun viewCarbs() {
//creating the instance of DatabaseHandler class
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
//calling the viewCarbs method of DatabaseHandler class to read the records
val carbohs: List<CarbsModelClass> = databaseHandler.viewCarbs()
//val carbohsArraycarbTime = Array<String>(carbohs.size) { "null" }//not needed
val carbohsArrayshowCarbTime = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbs = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbGI = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbLife = Array<String>(carbohs.size) { "null" }
var index = 0
for (e in carbohs) {
//carbohsArraycarbTime[index] = e.carbTime.toString()//not needed
carbohsArrayshowCarbTime[index] = e.showCarbTime
carbohsArraycarbs[index] = e.carbs.toString()
carbohsArraycarbGI[index] = e.carbGi.toString()//note small i inGi
carbohsArraycarbLife[index] = e.carbLife.toString()
//index--
index++
}
//creating custom ArrayAdapter
val myCarbListAdapter = CarbListAdapter(
context = this,
//carbTime = carbohsArraycarbTime,//not needed
showCarbTime = carbohsArrayshowCarbTime,
carbs = carbohsArraycarbs,
carbGI = carbohsArraycarbGI,
carbLife = carbohsArraycarbLife
)
lv_carb_view.adapter = myCarbListAdapter
}//end of fun view carbs
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
// see https://stackoverflow.com/questions/9262871/android-two-spinner-onitemselected
if(parent?.getId() == R.id.gi_spinner) {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString : String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just digits
var GIprelim = gIprelimString.toLong()
et_carbGI.setText(gIprelimString)
}//end of first if
else{ if (parent?.getId() == R.id.carbs_per_spinner) {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())}//end of second if
else { Toast.makeText(applicationContext, "parent id "+parent?.getId().toString(), Toast.LENGTH_LONG).show()
}//end of second else
}//end of elseif OR /first else
}//end of on item selected
override fun onNothingSelected(parent: AdapterView<*>?) { }
}//end of class carbs input
New Class CarbsInputModel Below
//start of CarbsInputModel
import android.content.SharedPreferences
class CarbsInputModel(private val sharedPreferences:SharedPreferences) {
// val sharedPrefFile = "greenbandbasicpreference"
//val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
val sharedFav1Value: String? = sharedPreferences.getString("fav1_key", "50 50 defaultone")
val sharedFav2Value: String? = sharedPreferences.getString("fav2_key", "50 50 defaultwo")
val sharedFav3Value: String? = sharedPreferences.getString("fav3_key", "50 50 defaulthree")
val favDescr1:String = sharedFav1Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr2:String = sharedFav2Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr3:String = sharedFav3Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favData1:String = sharedFav1Value?.takeWhile { !it.isLetter() } .toString()
val favData2:String = sharedFav2Value?.takeWhile { !it.isLetter() } .toString()
val favData3:String = sharedFav3Value?.takeWhile { !it.isLetter() } .toString()
val favCarbPerString1 = favData1.take(3).trim()
val favCarbPerString2 = favData2.take(3).trim()
val favCarbPerString3 = favData3.take(3).trim()
val favGiString1 = favData1.takeLast(4).trim()
val favGiString2 = favData2.takeLast(4).trim()
val favGiString3 = favData3.takeLast(4).trim()
val favFullCarbPer1 = favCarbPerString1+" "+favDescr1+" "
val favFullCarbPer2 = favCarbPerString2+" "+favDescr2+" "
val favFullCarbPer3 = favCarbPerString3+" "+favDescr3+" "
val favFullGi1 = favGiString1+" "+favDescr1+" "
val favFullGi2 = favGiString2+" "+favDescr2+" "
val favFullGi3 = favGiString3+" "+favDescr3+" "
}//end of class Carbs Input Model
Attempted tidy code for override function. This still does absolutely Nothing
//trying to tidy up code
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
when {
parent.id == R.id.gi_spinner -> {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString: String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just leading digits
et_carbGI.setText(gIprelimString)
}
parent.id == R.id.carbs_per_spinner -> {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) / 100.0).toLong()
et_carbs.setText(carbs.toString())
}
else -> {
Toast.makeText(applicationContext, "parent id " + parent?.getId().toString(),
Toast.LENGTH_LONG ).show()
}
}//end of when
}//end of override fun onitemselected