You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义MyButton:如何获取XML中声明的layout_width布局属性值?

自定义MaterialButton根据XML布局宽度适配样式问题

问题需求

  • 自定义MyButton继承自MaterialButton,实现两种尺寸样式:
    • 大按钮:宽度为match_parent,对应特定padding和文字大小
    • 小按钮:最小宽度100dp,对应另一组padding和文字大小
  • 需要获取XML中声明的layout_width属性值,以此自动切换样式;此前尝试直接获取宽高返回0、doOnLayout未触发、自定义属性无效。

解决方案

1. 直接从AttributeSet解析XML中声明的layout_width属性

布局参数(如layout_width)可以通过AttributeSet直接读取XML中的原始声明值,无需等待布局完成。通过TypedValue解析属性值,判断是否为match_parent:

open class MyButton @JvmOverloads constructor(
    context: Context,
    private val attributeSet: AttributeSet? = null,
    private val styleAttribute: Int = com.google.android.material.R.attr.materialButtonStyle
) : MaterialButton(context, attributeSet, styleAttribute) {

    init {
        attributeSet?.let { parseAttr(it) }
    }

    private fun parseAttr(attr: AttributeSet) {
        // 获取layout_width的属性索引(使用Android官方命名空间)
        val widthIndex = attr.getAttributeIndex("http://schemas.android.com/apk/res/android", "layout_width")
        if (widthIndex != -1) {
            val typedValue = TypedValue()
            attr.getValue(widthIndex, typedValue)
            // 判断是否为match_parent
            val isMatchParent = typedValue.data == ViewGroup.LayoutParams.MATCH_PARENT
            if (isMatchParent) {
                // 大按钮样式:全屏宽度,设置对应padding和文字大小
                setPadding(dpToPx(16), dpToPx(12), dpToPx(16), dpToPx(12))
                textSize = 16f
                // 强制宽度撑满父布局(避免样式覆盖)
                layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            } else {
                // 小按钮样式:最小宽度100dp,设置对应padding和文字大小
                minWidth = dpToPx(100)
                setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8))
                textSize = 14f
            }
        }
    }

    // dp转px工具方法
    private fun dpToPx(dp: Int): Int {
        return (dp * resources.displayMetrics.density).toInt()
    }
}

2. 修正doOnLayout未触发的问题(备选方案)

如果需要在布局完成后获取实际渲染宽高,可以通过addOnLayoutChangeListener替代doOnLayout(确保在UI线程执行):

init {
    addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
        val parent = parent as? ViewGroup ?: return@addOnLayoutChangeListener
        val actualWidth = measuredWidth
        val parentWidth = parent.measuredWidth
        val isMatchParent = actualWidth == parentWidth
        // 根据实际宽度设置样式,逻辑同parseAttr方法
    }
}

注意事项

  • 解析系统属性时必须使用Android官方命名空间http://schemas.android.com/apk/res/android
  • 如果XML中layout_width设置为固定dp值(如150dp),会被归类到小按钮样式;若需区分固定值和wrap_content,可进一步解析typedValue的类型和数值
  • 若使用主题默认样式,需确保构造函数正确传入styleAttribute参数

内容的提问来源于stack exchange,提问作者Shadman Wakil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:43:12