如何在Kotlin中通过代码为自定义按钮设置可样式化属性?
代码创建自定义按钮并设置样式属性的问题
我定义了一个带可样式化属性的自定义按钮,属性声明如下:
<declare-styleable name="MBButton"> <attr name="button_type_accessible" format="enum"> <enum name="primary" value="0" /> <enum name="secondary" value="1" /> <enum name="secondary_alternative" value="2" /> <enum name="ghost" value="3" /> <enum name="ghost_alternative" value="4" /> <enum name="action" value="5" /> </attr> <attr name="is_medium_size_accessible" format="boolean" /> <attr name="is_small_size_accessible" format="boolean" /> <attr name="make_accessible_button" format="string" /> </declare-styleable>
我需要通过代码创建该按钮并设置部分属性,原本计划把属性信息存入AttributeSet后在创建按钮时使用,但在Kotlin里无法实现。我的自定义按钮构造函数如下:
class CustomButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.mbButtonSecondaryStyle ) : AppCompatButton(context, attrs, defStyleAttr)
可行的解决方案
1. 暴露公共Setter方法直接设置
在CustomButton类中添加对应属性的公共方法,直接在代码中调用设置:
class CustomButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.mbButtonSecondaryStyle ) : AppCompatButton(context, attrs, defStyleAttr) { // 设置按钮类型 fun setButtonType(type: Int) { when(type) { 0 -> setBackgroundResource(R.drawable.bg_primary_button) 1 -> setBackgroundResource(R.drawable.bg_secondary_button) 2 -> setBackgroundResource(R.drawable.bg_secondary_alt_button) 3 -> setBackgroundResource(R.drawable.bg_ghost_button) 4 -> setBackgroundResource(R.drawable.bg_ghost_alt_button) 5 -> setBackgroundResource(R.drawable.bg_action_button) } } // 设置中等尺寸 fun setMediumSize(isMedium: Boolean) { val heightRes = if(isMedium) R.dimen.button_medium_height else R.dimen.button_default_height layoutParams.height = resources.getDimensionPixelSize(heightRes) } // 设置小尺寸 fun setSmallSize(isSmall: Boolean) { val heightRes = if(isSmall) R.dimen.button_small_height else R.dimen.button_default_height layoutParams.height = resources.getDimensionPixelSize(heightRes) } // 设置辅助文本 fun setAccessibleButtonText(text: String) { contentDescription = text } }
创建按钮时直接调用方法:
val button = CustomButton(context) button.setButtonType(0) // 设置为primary类型 button.setMediumSize(true) button.setAccessibleButtonText("主要操作按钮")
2. 复用TypedArray解析逻辑,添加批量更新方法
如果构造函数中已经通过TypedArray解析了属性,可以提取逻辑到公共方法中,支持批量更新:
class CustomButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.mbButtonSecondaryStyle ) : AppCompatButton(context, attrs, defStyleAttr) { private var currentButtonType = 1 // 默认secondary类型 private var isCurrentMedium = false private var isCurrentSmall = false init { attrs?.let { val typedArray = context.obtainStyledAttributes(it, R.styleable.MBButton, defStyleAttr, 0) currentButtonType = typedArray.getInt(R.styleable.MBButton_button_type_accessible, 1) isCurrentMedium = typedArray.getBoolean(R.styleable.MBButton_is_medium_size_accessible, false) isCurrentSmall = typedArray.getBoolean(R.styleable.MBButton_is_small_size_accessible, false) val accessibleText = typedArray.getString(R.styleable.MBButton_make_accessible_button) applyAttributeChanges(currentButtonType, isCurrentMedium, isCurrentSmall, accessibleText) typedArray.recycle() } } // 批量更新属性的方法 fun updateAttributes( buttonType: Int? = null, isMedium: Boolean? = null, isSmall: Boolean? = null, accessibleText: String? = null ) { val newType = buttonType ?: currentButtonType val newMedium = isMedium ?: isCurrentMedium val newSmall = isSmall ?: isCurrentSmall applyAttributeChanges(newType, newMedium, newSmall, accessibleText) currentButtonType = newType isCurrentMedium = newMedium isCurrentSmall = newSmall } private fun applyAttributeChanges(type: Int, medium: Boolean, small: Boolean, accessibleText: String?) { // 应用按钮类型样式 when(type) { 0 -> setBackgroundResource(R.drawable.bg_primary_button) 1 -> setBackgroundResource(R.drawable.bg_secondary_button) 2 -> setBackgroundResource(R.drawable.bg_secondary_alt_button) 3 -> setBackgroundResource(R.drawable.bg_ghost_button) 4 -> setBackgroundResource(R.drawable.bg_ghost_alt_button) 5 -> setBackgroundResource(R.drawable.bg_action_button) } // 应用尺寸样式 val heightRes = when { small -> R.dimen.button_small_height medium -> R.dimen.button_medium_height else -> R.dimen.button_default_height } layoutParams.height = resources.getDimensionPixelSize(heightRes) // 应用辅助文本 accessibleText?.let { contentDescription = it } } }
使用示例:
val button = CustomButton(context) button.updateAttributes( buttonType = 3, // ghost类型 isMedium = true, accessibleText = "幽灵中等按钮" )
3. 预定义Style样式,通过构造函数传入
在styles.xml中定义包含目标属性的样式:
<style name="GhostMediumButton" parent="@attr/mbButtonSecondaryStyle"> <item name="button_type_accessible">ghost</item> <item name="is_medium_size_accessible">true</item> <item name="make_accessible_button">幽灵中等按钮</item> </style>
创建按钮时传入该样式:
val button = CustomButton(context, null, R.style.GhostMediumButton)
为什么不推荐手动创建AttributeSet
Android的AttributeSet通常是从XML布局解析生成的,手动构建需要依赖XmlPullParser等底层API,步骤繁琐且容易出错,官方也不推荐这种方式。上面的几种方法更简洁可靠。
内容的提问来源于stack exchange,提问作者Iban Arriola
相关产品推荐
相关产品推荐

