Issue
I cannot figure out why this is throwing NPE at the when (colorState)
line. If I remove enum class and replace values with integers, everything works fine.
class ColorChangerButton(context: Context, attrs: AttributeSet) : AppCompatImageButton(context, attrs) {
enum class ColorState {ACCENT, STRONG, WEAK}
private val stateAccent = intArrayOf(R.attr.state_accent)
private val stateStrong = intArrayOf(R.attr.state_strong)
private val stateWeak = intArrayOf(R.attr.state_weak)
var colorState = ColorState.ACCENT
set(value) {
if (field != value) {
field = value
refreshDrawableState()
}
}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
val state = super.onCreateDrawableState(extraSpace + 1)
when (colorState) {
ColorState.ACCENT -> View.mergeDrawableStates(state, stateAccent)
ColorState.STRONG -> View.mergeDrawableStates(state, stateStrong)
ColorState.WEAK -> View.mergeDrawableStates(state, stateWeak)
}
return state
}
}
Stack trace:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.test.uiplayground.buttons.ColorChangerButton$ColorState.ordinal()' on a null object reference
at com.test.uiplayground.buttons.ColorChangerButton.onCreateDrawableState(ColorChangerButton.kt:28)
at android.view.View.getDrawableState(View.java:15953)
at android.view.View.setBackgroundDrawable(View.java:16188)
at androidx.appcompat.widget.AppCompatImageButton.setBackgroundDrawable(AppCompatImageButton.java:122)
at android.view.View.setBackground(View.java:16125)
at android.view.View.<init>(View.java:4090)
at android.widget.ImageView.<init>(ImageView.java:139)
at android.widget.ImageButton.<init>(ImageButton.java:86)
at android.widget.ImageButton.<init>(ImageButton.java:82)
at androidx.appcompat.widget.AppCompatImageButton.<init>(AppCompatImageButton.java:73)
at androidx.appcompat.widget.AppCompatImageButton.<init>(AppCompatImageButton.java:69)
at com.test.uiplayground.buttons.ColorChangerButton.<init>(ColorChangerButton.kt:9)
As I said above, if I replace enum class with integer, then it works perfectly fine:
class ColorChangerButton(context: Context, attrs: AttributeSet) : AppCompatImageButton(context, attrs) {
private val stateAccent = intArrayOf(R.attr.state_accent)
private val stateStrong = intArrayOf(R.attr.state_strong)
private val stateWeak = intArrayOf(R.attr.state_weak)
var colorState = 1
set(value) {
if (field != value) {
field = value
refreshDrawableState()
}
}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
val state = super.onCreateDrawableState(extraSpace + 1)
when (colorState) {
1 -> View.mergeDrawableStates(state, stateAccent)
2 -> View.mergeDrawableStates(state, stateStrong)
3 -> View.mergeDrawableStates(state, stateWeak)
}
return state
}
}
Solved, additional explanation:
- For the reason why NPE is thrown see the accepted answer.
- The integer part was quite misleading. Java sets uninitialized variables to default value (unless local or block variables then you get error saying it's not initialized). For integer that value is 0. Thus it passed by all 3 conditions at the first call. If I add
0 -> View.mergeDrawableStates(state, stateAccent)
case, then I get another NPE, this time becausestateAccent
is not initialized yet - which by now makes perfect sense.
Solution
This is generated Java code for Kotlin source file:
public ColorChangerButton(@NotNull Context context, @NotNull AttributeSet attrs) {
Intrinsics.checkParameterIsNotNull(context, "context");
Intrinsics.checkParameterIsNotNull(attrs, "attrs");
super(context, attrs); <---------- super will be called first
this.stateAccent = new int[0];
this.stateStrong = new int[0];
this.stateWeak = new int[0];
this.colorState = ColorChangerButton.ColorState.ACCENT;
}
Instance variables aren't even initialized until a super()
call has been made. An NPE occurs when onCreateDrawableState
is called by the super constructor.
Answered By - Akaki Kapanadze
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.