Null object reference when using string resources for ListView - android

Why is it that whenever I try to use a simple array for a ListView, I get this error?:
Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
I really don't get what I'm doing wrong here as I've tried using different contexts (base and application) but still no change in solving this issue.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var array = arrayOf(resources.getString(R.string.item_a),
resources.getString(R.string.item_b),
resources.getString(R.string.item_c),
resources.getString(R.string.item_d))
val adapter = ArrayAdapter(this,
R.layout.listview_item, array)
val listView:ListView = findViewById(R.id.listview_1)
listView.setAdapter(adapter)
}
override fun onItemClick(view: View, position: Int) {
if (array[position] == resources.getString(R.string.item_a)){
Toast.makeText(this, "Hi there! This is a Toast.", Toast.LENGTH_SHORT).show()
}
}
}

Two things going on here.
First, if you declare and initialize an array of string resources at the class level of your activity, you will get an error. Your activity isn't "ready" immediately when it is constructed; it needs to have super.onCreate() called before you'll be able to access resources.
Second, if you move the entire array inside onCreate() then you won't be able to access it from onItemClick() (since now it is local to onCreate).
The solution is to use a lateinit var so that you can declare your array at the class level but then initialize it inside onCreate():
class MainActivity : AppCompatActivity() {
private lateinit var array: Array<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.array = arrayOf(...)
// ...
}
override fun onItemClick(view: View, position: Int) {
if (array[position] == resources.getString(R.string.item_a)){
// ...
}
}
}

Related

Spinner Issue in MVVM architecture android

When I rotate the screen the spinner reset though I am using MVVM architecture.
While setting value I set value in view model, but still spinner reset to its orignal state.
In Main Activity I have done this,
GetBusinessPartners.setOnItemSelectedListener(object:OnItemSelectedListener{
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
dealMealPreApproval.initsetSpinnerIndex(position)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
})
dealMealPreApproval.getSpinnerValue().observe(this#DealMealPreApproval, Observer {
GetBusinessPartners.setSelection(it)
})
in view model i have done this
class MealPolicyViewModel : ViewModel() {
var businessPartners=MutableLiveData<ArrayList<BusinessPartnersModel>>()
var spinnerString=MutableLiveData<Int>()
fun initsetSpinnerIndex(valueOfSpinner:Int){
spinnerString.value=valueOfSpinner
Log.d("valueOfValueOFSPinner",valueOfSpinner.toString())
}
fun getSpinnerValue() : LiveData<Int>{
return spinnerString
}
}
For A small data like double, boolean, string, int you should use onSavedInstance like this, for large amount of data view model will be used.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("MySPinner", GetBusinessPartners.getSelectedItemPosition());
}
Then getValue Like this in OnCreate Method
var counter=0
if (savedInstanceState != null) {
counter = savedInstanceState.getInt("MySPinner", 0)
}
After Spinner Adapter call SetSelection and pass counter in it like,
ArrayAdapter<BusinessPartnersModel>(context, android.R.layout.simple_list_item_1, list)
GetBusinessPartners.setSelection(counter)
I still recommend you to use viewmodel with livedata in this case. Please check my solution.
In the viewmodel, you create the livedata that you want to store the data to display on the view. I still recommend using MutableLiveData to set data for live data, and LiveData for view to get data.
class MealPolicyViewModel : ViewModel() {
private val _businessPartners = MutableLiveData<ArrayList<BusinessPartnersModel>>()
val businessPartners: LiveData<ArrayList<BusinessPartnersModel>> = _businessPartners
private val _spinnerString = MutableLiveData<Int>()
val spinnerString: LiveData<Int> = _spinnerString
fun initsetSpinnerIndex(valueOfSpinner: Int){
_spinnerString.value = valueOfSpinner
Log.d("valueOfValueOFSPinner", valueOfSpinner.toString())
}
}
In the view, specifically MainActivity, you initialize the viewModel through the lazy variable associated with the built-in extension of the activity-ktx library by viewModels(). Then you observe your livedata in onCreate().
class MainActivity : AppCompatActivity() {
private val viewModel: MealPolicyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.spinnerString.observe(this) {
// TODO do something.
}
}
}
As you know, livedata will always observe the lifecycle of the view. In case you rotate the screen, the livedata will observe again when you finish rotating the screen.
Try my implementation and let me know if you still get the error.
save the position in viewmodel,did u try that?but do not call it in spinner adapter after item selected, save it in onPasue and the save button(or ending to network button).
this is how u get position:
binding.spinnerLessonModelName.selectedItemPosition

Android Kotlin does spinner_listener run through during initialize?

I write a kotlin app and wonder, why the code runs through the listener during initializing it. I try to explain the question with my code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
findpath()
//check, if Dateien existieren, sonst create in function
checkOrCreateFiles()
binding = ActivityMainBinding.inflate(layoutInflater)
var view = binding.root
setContentView(view) //R.layout.activity_main)
val cadapterk: ArrayAdapter<String> = ArrayAdapter<String>(
this,
android.R.layout.simple_spinner_item, myvokdirs
)
binding.spinnerKasten.adapter = cadapterk
//binding.spinnerKasten.setSelection(0)
binding.spinnerKasten.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
kastenselected = myvokdirs[position].toString()
setnewpath(kastenselected)
}
override fun onNothingSelected(parent: AdapterView<*>) {
kastenselected = myvokdirs[0]
}
}
binding.AuswahlContainer.isEnabled = false
fileAktuell = boxes[0] //dateiAkt
checkAktuell = gut[0] // gutAkt
readDatei(fileAktuell)
binding.spinnerKasten.isEnabled = false
// some addional code
}
The situation / problem
In principle, the code works. Binding is o.k. The spinner "binding.spinnerkasten" is o.k. The associated adapter "cadapterk" is ok and shows data of my list "myvokdirs". BUT:
during initializing the spinner the code runs through "setnewpath". But "setnewpath" should be used only after selecting an item in the spinner.
How can I avoid, that "setnewpath" is fired during init? It seems, that the app runs through the listener during onCreate-function.
What is wrong or what is my misunderstanding, that the code fires "setnewpath" already in init instead of only after selecting an item?
do I habe to combine it with an onCLickListener?
(All other things are correct. The spinner appears on the right place, the spinner shows the correct data
onItemSelected is always called upon loading if you dont want something to run on initial load then surround with a boolean
var firstLoad = true
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
if(!firstLoad){
kastenselected = myvokdirs[position].toString()
setnewpath(kastenselected)
}else{
firstLoad = false
}
}

NullPointerException on this.getPreferences()

class PlayerDetails : AppCompatActivity(), View.OnClickListener, TextWatcher {
val sharedPref = this.getPreferences(Context.MODE_PRIVATE) // NPE
var applicationID = sharedPref.getString("applicationID", null)
private lateinit var binding: ActivityPlayerDetailsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_player_details)
...
}
...
I'm attempting to create a reference to the default Shared Preferences file of my activity by called this.getPreferences().
The results is giving me a NullPointerException - why is this? It's not associated with the UI so it doesn't need to be called after setContentView(). Why would the activity be null at this point?
It's not associated with the UI so it doesn't need to be called after
setContentView()
Right, but it must be called after:
super.onCreate(savedInstanceState)
because this refers to the activity's Context and this Context is only valid after this call.
So what you can do is change the declaration to:
val sharedPref: SharedPreferences? by lazy { this.getPreferences(Context.MODE_PRIVATE) }
this way sharedPref will be initialized the first time it will be used, after the call to super.onCreate(savedInstanceState).
Read more about lazy.
The safest place to call this is after super.onCreate(savedInstnaceState).
Move the line inside onCreate after super.onCreate
val PREFS_NAME = "com.teamtreehouse.colorsarefun.prefs"
var prefs: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prefs = = this.getSharedPreferences(PREFS_FILENAME, 0)
binding = DataBindingUtil.setContentView(this, R.layout.activity_player_details)
...
}
By doing this.getPreferences(Context.MODE_PRIVATE) you called inherit method from AppCompatActivity , Since onCreate is the first callback when activity launched , so things are not set up yet.

push string to array from another activity

my aplication
I have 2 activity, the first is CategoryActivity and the second is QuestionActivity.
in CategoryActivity i have a recyclerview which lists categories, and also in Category Activity, i have an array. When i click eksterior, it will go to questionActivity, and when i click submit, it will back to CategoryActivity and send data to CategotyActivity. the data sent was entered into the array, so the array will be like this: array [] = {“1”}; then i click the second item in recyclerview which is interior, then it will go to QuestionActivity again, when i click submit, it will send data to CategoryActivity and add it into the previous array, so the array will be like this : array [] = {“1”,”1”}; and so on until the items on the recyclerview run out. I have tried like this :
QuestionActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result)
button_save.setOnClickListener(){
val intent = Intent(this#ResultActivity, KategoriActivity::class.java)
intent.putExtra(KategoriActivity.Companion.ARRAY, "1")
startActivity(intent)
}
}
CategoryActivity.kt
var list = arrayOf(intent.getStringExtra(ARRAY))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_category)
Toast.makeText(this#KategoriActivity, ""+ list, Toast.LENGTH_SHORT).show()
}
companion object {
val ARRAY = "array"
}
override fun onBackPressed() {
Toast.makeText(this#KategoriActivity, ""+ list, Toast.LENGTH_SHORT).show()
if (list.size == kategoriDatas!!.size){
onBackPressed()
} else {
val builder = AlertDialog.Builder(this)
builder.setMessage("You Must Finish All The Category")
// add a button
builder.setPositiveButton("OK", null)
// create and show the alert dialog
val dialog = builder.create()
dialog.show()
}
}
and the problem is when i open category activity the app is stopped, and the log is say Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getStringExtra(java.lang.String)' on a null object reference
and the data can not push to the array, please help me
You can only get the intent after the activity has been created. In order to make sure of it it's best if you place the code that's gonna get the extras from Intents inside your onCreate.
Like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_category)
Toast.makeText(this#KategoriActivity, ""+ list, Toast.LENGTH_SHORT).show()
var list = arrayOf(intent.getStringExtra(ARRAY))
}
If you want to have an Application class that holds information:
class MyApplication : Application(){
var list = emptyArray<String>()
override fun onCreate() {
super.onCreate()
}
}
To use it in your activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_category)
Toast.makeText(this#KategoriActivity, ""+ list, Toast.LENGTH_SHORT).show()
var list = (application as MyApplication).list
// you can than use it to update or retrieve values from it
}

unfortunately the app has stopped Android studio and there is no Error

In android monitor it's make this error like in the picture and I can't understand why is the error in the XML ?
the code no problem with it
can the picture that i added it make this error?
class MainActivity : AppCompatActivity() {
val ListOffood = ArrayList<Food>()
var adpter:foodAdapter=null!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
ListOffood.add(Food("sandwis","m3 t7eneh",R.drawable.ic_stat_name))
ListOffood.add(Food("sandwis","m3 t7eneh",R.drawable.ic_stat_name))
ListOffood.add(Food("sandwis","m3 t7eneh",R.drawable.ic_stat_name))
ListOffood.add(Food("sandwis","m3 t7eneh",R.drawable.ic_stat_name))
ListOffood.add(Food("sandwis","m3 t7eneh",R.drawable.ic_stat_name))
adpter=foodAdapter(this,ListOffood)
graidV.adapter=adpter
}
class foodAdapter: BaseAdapter {
var ListOffood=ArrayList<Food>()
var context: Context?=null
constructor(context: Context, ListOffood:ArrayList<Food>):super(){
this.context=context
this.ListOffood=ListOffood
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View? {
val food = ListOffood[p0]
var view = LayoutInflater.from(context).inflate(R.layout.foodlist,p2,false)
view.textV.text=food.name
view.imageV.setImageResource(food.image!!)
return view
}
override fun getItem(p0: Int): Any {
return ListOffood[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return ListOffood.size
}
}
}
The property adapter should be use a late-initialized property or a nullable property. Otherwise, you will get a KotlinNullPointerException by the NPE-lovers operator !!, for example:
// v---cast `null` to any type will throws NullPointerException
var adpter:foodAdapter=null!!
Should be a late-initialized property:
lateinit var adpter:foodAdapter
OR a nullable property:
var adapter:foodAdapter? = null
To expand on holi-java answer, Kotlin unlike Java tries to be a null safe language.
Unless explicitly defined all variables in Kotlin are not nullable. Therefore if you wish to have a variable that can hold a null value you will need to add ? after the type name to indicate this is the case.
var canBeNull: String? = null
var canNotBeNull: String = null // Won't compile
var canNotBeNull2: String = null!! // Will crash and burn at runtime
In your example you were forcing the compiler to accept the null value into your adapter variable which led to the runtime NPE.
The !! syntax should only be used when you are sure that the value will not be null but the compiler is unable to make that distinction. This can be the case when using 3rd party libraries that are written in Java.

Categories

Resources