概述
CheckBox原本只有两种状态,选中和不选中,使用系统默认的state_checked就可以满足。但是多了一种状态,就得自定义state了
1. 在attrs.xml文件中添加state
<declare-styleable name="GpState">
<attr name="gp_state_middle" format="boolean" />
</declare-styleable>
2. 在selector文件中使用state
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:gp_state_middle="true" android:drawable="@drawable/middle"/>
<item android:state_checked="true" android:drawable="@drawable/checked"/>
<item android:drawable="@drawable/unchecked"/>
</selector>
注意这里需要添加命名空间xmlns:app="http://schemas.android.com/apk/res-auto"
如果需要在代码中获取drawable,比如使用iconfont,就得在代码中设置StateListDrawable
val stateListDrawable = StateListDrawable()
stateListDrawable.addState(states, middleDrawable)
stateListDrawable.addState(intArrayOf(android.R.attr.state_checked), checkDrawable)
stateListDrawable.addState(intArrayOf(), unCheckDrawable)
stateListDrawable.setBounds(0, 0, stateListDrawable.minimumWidth, stateListDrawable.minimumHeight)
setCompoundDrawables(stateListDrawable, null, null, null)
3.在代码中将自定义state合并到系统中,重写onCreateDrawableState()
override fun onCreateDrawableState(extraSpace: Int): IntArray {
val drawableState = super.onCreateDrawableState(extraSpace + 1)
if (getState() == null) { //null表示中间状态
View.mergeDrawableStates(drawableState, states)
}
return drawableState
}
4.当Checkbox状态改变时,需要刷新状态,调用refreshDrawableState()
5.重新梳理一遍调用流程
来自https://www.jianshu.com/p/b676be395df1
1. 自定义的setType方法实现了类似setEnabled方法的功能。当调动改方法时,通知自定义view更新drawable状态,即调用refreshDrawableState方法。
2. refreshDrawableState方法内部调用drawableStateChanged方法。
3. drawableStateChanged方法通过getDrawableState方法获取最新的drawable状态值。
4. getDrawableState方法调用自定义类内的onCreateDrawableState方法来实现drawable状态的变更(通过mergeDrawableStates方法)。
5. 通过新的drawable状态来更新view的背景。
6.完整代码
class ThreeStateCheckbox : AppCompatCheckBox {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val states = intArrayOf(R.attr.gp_state_middle)
private var isMiddle: Boolean = false
private var mBroadcasting: Boolean = false
private var onStateChangeListener: OnStateChangeListener? = null
private val checkDrawable = ...
private val unCheckDrawable = ...
private val middleDrawable = ...
init {
val stateListDrawable = StateListDrawable()
stateListDrawable.addState(states, middleDrawable)
stateListDrawable.addState(intArrayOf(android.R.attr.state_checked), checkDrawable)
stateListDrawable.addState(intArrayOf(), unCheckDrawable)
stateListDrawable.setBounds(0, 0, stateListDrawable.minimumWidth, stateListDrawable.minimumHeight)
setCompoundDrawables(stateListDrawable, null, null, null)
}
override fun onFinishInflate() {
super.onFinishInflate()
// buttonDrawable = null
buttonDrawable = StateListDrawable() //有些低版本手机还是会显示默认的框,用这个方式去掉
}
override fun toggle() {
if (isMiddle) {
isChecked = true
} else {
super.toggle()
}
}
override fun setChecked(checked: Boolean) {
val checkedChanged = isChecked != checked
super.setChecked(checked)
val wasMiddle = isMiddle
setMiddleState(false, false)
if (wasMiddle || checkedChanged) {
notifyStateListener()
}
}
/**
* 设置中间状态
*/
fun setMiddleState(indeterminate: Boolean) {
setMiddleState(indeterminate, true)
}
private fun getState(): Boolean? {
return if (isMiddle) null else isChecked
}
/**
* 设置状态,null表示中间状态
*/
fun setState(state: Boolean?) {
if (state != null) {
isChecked = state
} else {
setMiddleState(true)
}
}
private fun setMiddleState(isMiddle: Boolean, notify: Boolean) {
if (this.isMiddle != isMiddle) {
this.isMiddle = isMiddle
refreshDrawableState()
if (notify) {
notifyStateListener()
}
}
}
private fun notifyStateListener() {
if (mBroadcasting) {
return
}
mBroadcasting = true
if (onStateChangeListener != null) {
onStateChangeListener!!.onStateChanged(this, getState())
}
mBroadcasting = false
}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
val drawableState = super.onCreateDrawableState(extraSpace + 1)
if (getState() == null) {
View.mergeDrawableStates(drawableState, states)
}
return drawableState
}
fun setOnStateChangeListener(listener: OnStateChangeListener) {
this.onStateChangeListener = listener
}
interface OnStateChangeListener {
fun onStateChanged(checkbox: GpThreeStateCheckbox, newState: Boolean?)
}
}
7.遇到的问题
在自定义state时,如果项目里是多个module,定义state的attrs.xml文件和使用state的selector文件不在同一个module,运行./gradlew build
命名会报错:
java.util.concurrent.ExecutionException:com.android.builder.internal.aapt.v2.Aapt2Exception:Android resource linking failed
这是因为上面导入命名空间,默认是当前包名,而attrs.xml和selector文件不在同一个module中,包名不同,会导致文件找不到问题。解决办法就是将这两个文件放到一起,或者命名空间强制设置包名,但是这好像已经不推荐了
参考:
- Android selector选择器自定义属性
- indeterminate-checkbox
最后
以上就是贪玩豆芽为你收集整理的Android 三种状态的Checkbox的全部内容,希望文章能够帮你解决Android 三种状态的Checkbox所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复