I am working in app with two languages
in autocomplatetextview i want to change values according to the language of device
i try this code
var EGP = getString(R.string.egyptian_pound_egp)
var USD = getString(R.string.american_dollar_usd)
var SAR = getString(R.string.Saudia_Ryal)
var KWD = getString(R.string.Kuwaiti_Dinar)
and full code of MainActivity
package com.example.currency
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Adapter
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Button
import androidx.annotation.StringRes
import androidx.core.widget.addTextChangedListener
import com.google.android.material.internal.ContextUtils.getActivity
import com.google.android.material.textfield.TextInputEditText
class MainActivity : AppCompatActivity() {
var EGP = getString(R.string.egyptian_pound_egp)
var USD = getString(R.string.american_dollar_usd)
var SAR = getString(R.string.Saudia_Ryal)
var KWD = getString(R.string.Kuwaiti_Dinar)
lateinit var convertButton: Button
lateinit var amount: TextInputEditText
lateinit var result: TextInputEditText
lateinit var from: AutoCompleteTextView
lateinit var to: AutoCompleteTextView
val listValue = mapOf(
USD to 0.052356,
EGP to 1.0,
SAR to 0.197040,
KWD to 0.0166838
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initalizeViews()
populateMenu()
convertButton.setOnClickListener {
calcResault()
}
amount.addTextChangedListener {
calcResault()
}
}
private fun initalizeViews() {
convertButton = findViewById(R.id.button)
amount = findViewById(R.id.AmountTIET)
result = findViewById(R.id.ResultTIET)
from = findViewById(R.id.FromACTV)
to = findViewById(R.id.ToACTV)
}
private fun populateMenu() {
val currencyList = listOf(EGP, USD, SAR, KWD)
val adapter = ArrayAdapter(this, R.layout.list_currency, currencyList)
from.setAdapter(adapter)
to.setAdapter(adapter)
}
private fun calcResault(){
if (amount.text.toString().isNotEmpty()) {
result.setText(
String.format(
"%.2f", listValue.get(to.text.toString())!!.times(
amount.text.toString().toDouble()
.div(listValue.get(from.text.toString())!!)
)
)
)
} else {
amount.setError(getString(R.string.amount_required))
}
}
}
after testing some codes , i found that getString(R.string.xxx) the reason of the crashing
when change getString(R.string.xxx) with string value the app opening with no problem
but i want to change values according to the language of device
CodePudding user response:
Make those be either lateinit var or use by lazy {} to defer initialization. You cannot call getString() until after super.onCreate() has been called in your onCreate() function.
CodePudding user response:
Try this
(CAUTION: You values will be re-initialized every time onCreate() method called)
lateinit var EGP: String
lateinit var USD: String
lateinit var listValues: Map<String, Double>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EGP = getString(R.string.egyptian_pound_egp)
USD = getString(R.string.american_dollar_usd)
listValues = mapOf(
USD to 0.052356,
EGP to 1.0
)
//Rest of your code
}
CodePudding user response:
getString requires your Activity to have a Context, and at construction time it doesn't have one. So when you define those top-level variables that are initialised at construction time, your getString calls fail. The error log will tell you this, that you're trying to do something with a null Context or similar.
The context shows up somewhere around onCreate, so if you can guarantee those values won't be used until the Activity is CREATED (i.e. you won't be reading them until onCreate or later) then you could use a lazy delegate. That only initialises them when they're first read - so if you're reading them when the Activity has its Context, the getString call works fine!
val EGP = by lazy { getString(R.string.egyptian_pound_egp) }
val USD = by lazy { getString(R.string.american_dollar_usd) }
val SAR = by lazy { getString(R.string.Saudia_Ryal) }
val KWD = by lazy { getString(R.string.Kuwaiti_Dinar) }
But the problem here is you're not first reading these in onCreate or later - it happens in the next line where you build a Map using those values, which is another top-level variable that's initialised at construction. So you don't get the benefit of the lazy because it's called too early.
You can fix this by making that map lazy too:
val listValue by lazy { mapOf(
USD to 0.052356,
EGP to 1.0,
SAR to 0.197040,
KWD to 0.0166838
)}
Now listValue won't be initialised until it's read either! So it won't try to read those other values until it's actually accessed - so same deal, as long as listValue isn't read by something before onCreate, it should be fine.
This is the kind of thing you have to watch out for with lazy delegates in Android lifecycle components like Activity and Fragment, or anywhere you need lazy initialisation really. Make sure it's not being read too early by something else, and make those lazy too if appropriate.
Using lazy delegates requires your variables to be vals though - if you need to be able to change them, make them lateinit instead and initialise them manually as soon as you can. You could keep listValue as a lazy if you want, just make sure the lateinit vars it initialises from are assigned before it's accessed
