'Why don't custom color states defined in onCreateDrawableState work?
I have a custom class McEditText that merges a constraint layout with a background xml drawable colored stroke.
(There's more to McEditText than Im showing here. The ellipses are the excluded code. So the problem is not the merge)
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/edit_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/mc_edit_text_border"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
app:layout_constraintTop_toBottomOf="@id/edit_label"
app:layout_constraintStart_toStartOf="parent">
...
</androidx.constraintlayout.widget.ConstraintLayout>
...
</merge>
mc_edit_text_border.xml in my drawables folder
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
<stroke
android:width="2dp"
android:color="@drawable/mc_edit_text_bordercolor" />
</shape>
mc_edit_text_bordercolor.xml in my drawables folder
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:color="@color/InputDisabledBorderColor" android:state_enabled="false"/>
<item android:color="@color/InputReadonlyBorderColor" app:state_readonly="true"/>
<item android:color="@color/InputErrorBorderColor" app:state_error="true" android:state_selected="false"/>
<item android:color="@color/InputErrorActiveBorderColor" app:state_error="true" android:state_selected="true"/>
<item android:color="@color/InputSuccessBorderColor" app:state_success="true" android:state_selected="false"/>
<item android:color="@color/InputSuccessActiveBorderColor" app:state_success="true" android:state_selected="true"/>
<item android:color="@color/InputActiveBorderColor" android:state_selected="true"/>
<item android:color="@color/InputBorderColor" android:state_selected="false"/>
</selector>
So far its all pretty standard stuff. The complexity is that I've added a few extra states, which you can see in the above selector xml: state_error, state_readonly and state_success. I read about how to do that here.
So in my attrs.xml...
<resources>
<attr name="state_error" format="boolean"/>
<attr name="state_success" format="boolean"/>
<attr name="state_readonly" format="boolean"/>
<declare-styleable name="McEditText">
<attr name="state_error"/>
<attr name="state_success"/>
<attr name="state_readonly"/>
<attr name="editTextKind" format="enum">
<enum name="normal" value="0" />
<enum name="error" value="1" />
<enum name="success" value="2" />
<enum name="readonly" value="3" />
<enum name="disabled" value="4" />
</attr>
</declare-styleable>
</resources>
Then in my McEditText.kt I do this...
class McEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet
) : ConstraintLayout(context, attrs) {
private val stateError = intArrayOf(R.attr.state_error)
private val stateSuccess = intArrayOf(R.attr.state_success)
private val stateReadOnly = intArrayOf(R.attr.state_readonly)
enum class Kind {
NORMAL, ERROR, SUCCESS, READONLY, DISABLED
}
init {
context.theme.obtainStyledAttributes(attrs, R.styleable.McEditText, 0, 0).apply {
try {
kind = Kind.values()[getInteger(R.styleable.McEditText_editTextKind, Kind.NORMAL.ordinal)]
} finally {
recycle()
}
}
_layout = inflate(context, R.layout.layout_mcedittext, this@McEditText) as ConstraintLayout
_editBorder = _layout.findViewById(R.id.edit_border)
if (kind == Kind.DISABLED) {
_editBorder.isEnabled = false
} else {
_editBorder.isEnabled = true
}
redraw()
}
private fun redraw() {
refreshDrawableState()
invalidate()
requestLayout()
}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
var drawableState = super.onCreateDrawableState(extraSpace + 2)
when (kind) {
Kind.ERROR -> View.mergeDrawableStates(drawableState, stateError)
Kind.SUCCESS -> View.mergeDrawableStates(drawableState, stateSuccess)
Kind.READONLY -> View.mergeDrawableStates(drawableState, stateReadOnly)
else -> {drawableState = super.onCreateDrawableState(extraSpace)}
}
return drawableState
}
}
Then finally I create a couple instances of the control in my fragment.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.mcafee.vision.ui.components.McEditText
android:id="@+id/as_disabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:label="Disabled"
custom:editTextKind="disabled"
/>
<View
android:layout_width="match_parent"
android:layout_height="10dp"/>
<com.mcafee.vision.ui.components.McEditText
android:id="@+id/as_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:label="Error"
custom:editTextKind="error"
/>
</LinearLayout>
The problem is that it doesnt work. When I make an instance of this control the border color never changes for my custom states (though it does work for the built in states like enabled=false). Im not sure how to set the custom states beyond merging them in the onCreateDrawableState() override. This article details how to do this but it doesn't appear to work. Am I missing something?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
