Let me start off by saying my Kotlin experience is limited. I am trying to get a nullable var
from a class, but get a null pointer whenever I execute it.
Class code:
class MultiSpinner : Spinner, OnMultiChoiceClickListener, OnCancelListener {
private var items: List<String>? = null
private var selected: BooleanArray? = null
private var selectedNew: BooleanArray? = null
private var defaultText: String? = null
private var listener: MultiSpinnerListener? = null
var category: String = "010"
constructor(context: Context) : super(context) {}
constructor(arg0: Context, arg1: AttributeSet) : super(arg0, arg1) {}
constructor(arg0: Context, arg1: AttributeSet, arg2: Int) : super(arg0, arg1, arg2) {}
override fun onClick(dialog: DialogInterface, which: Int, isChecked: Boolean) {
selectedNew!![which] = isChecked
}
override fun onCancel(dialog: DialogInterface) {
// refresh text on spinner
val spinnerBuffer = StringBuffer()
var allUnselected = true
for (i in items!!.indices) {
if (selected!![i]) {
spinnerBuffer.append(items!![i])
spinnerBuffer.append(", ")
allUnselected = false
}
selectedNew!![i] = selected!![i]
}
var spinnerText: String?
if (allUnselected) {
spinnerText = defaultText
} else {
spinnerText = spinnerBuffer.toString()
/** Remove trailing comma*/
spinnerText = spinnerText.substring(0, spinnerText.length - 2)
}
val adapter = ArrayAdapter(
context,
R.layout.simple_spinner_item,
arrayOf(spinnerText)
)
setAdapter(adapter)
category = selected!!.joinToString(limit = selected!!.size, separator = "") {it.toInt().toString()}
listener!!.onItemsSelected(selected)
}
override fun performClick(): Boolean {
val builder = AlertDialog.Builder(context)
builder.setMultiChoiceItems(
items!!.toTypedArray(), selectedNew, this
)
builder.setPositiveButton(R.string.ok
) { dialog, _ -> selected = selectedNew!!.copyOf(); dialog.cancel() }
builder.setNegativeButton(R.string.cancel
) { dialog, _ -> dialog.cancel() }
builder.setOnCancelListener(this)
builder.show()
return true
}
fun setItems(
items: List<String>, allText: String,
listener: MultiSpinnerListener
) {
this.items = items
this.defaultText = allText
this.listener = listener
// one selected by default
selected = BooleanArray(items.size) {false}
selectedNew = BooleanArray(items.size) {false}
for (i in selected!!.indices) {
selected!![i] = false
selectedNew!![i] = false
}
selected!![0] = true
selectedNew!![0] = true
category = selected!!.joinToString(limit = selected!!.size, separator = "") {it.toInt().toString()}
// all text on the spinner
val adapter = ArrayAdapter(
context,
R.layout.simple_spinner_item, arrayOf(allText)
)
setAdapter(adapter)
}
fun getSelected(): BooleanArray? {
return selected
}
interface MultiSpinnerListener {
fun onItemsSelected(selected: BooleanArray?)
}
private fun Boolean.toInt() = if (this) 1 else 0
}
Initializing
override fun onCreate(savedInstanceState: Bundle?) {
...
val spinnerCategory: MultiSpinner = findViewById(R.id.category_spinner)
val categoryList: List<String> = resources.getStringArray(R.array.category).toList()
spinnerCategory.setItems(categoryList, getString(R.string.default_category), this)
...
}
Accessing selected
from MultiSpinner
val selected: BooleanArray? = MultiSpinner(this).getSelected()
println(selected)
The println reads null
.
I have tried returning BooleanArray
in stead of BooleanArray?
for getSelected
, but that just gave me a null pointer exception (as expected from the println reading).
I circumvented the problem for now by using category
in the class and performing the manipulation inside the class. However, I would like to get the selected
array in my main activity, and use its data to create my desired String outside of the class.
Why do I not get my selected
data but instead a null pointer?
You have to access selected
using the instance obtained from findViewById
(the one already existing in your view hierarchy) instead of creating a completely new object by yourself:
val selected: BooleanArray? = spinnerCategory.getSelected()
Edit:
If you need to access selected
outside the onCreate
method simply store spinnerCategory
as a property instead of a local variable:
private lateinit var spinnerCategory: MultiSpinner
override fun onCreate(savedInstanceState: Bundle?) {
spinnerCategory = findViewById(R.id.category_spinner)
...
}
Also Kotlin has a nice feature for Android called View Binding which allows you to omit calling findViewById
and reference your views using their ids directly:
override fun onCreate(savedInstanceState: Bundle?) {
...
category_spinner.setItems(categoryList, getString(R.string.default_category), this)
...
}
private fun doSomethingWithSelected() {
val selected: BooleanArray? = category_spinner.getSelected()
}
However in that case I'd recommend changing the view's id so it agrees with the Kotlin variable naming guidelines , eg to spinnerCategory
as it's already being used.
You initialized selected
with null
meaning it can be nullable, so the compiler think it might possible that you will get null in this parameter. If you don't want it to be null you can define the variable like so:
private var selected: BooleanArray = false
Now selected
parameter won't be nullable at any time.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.