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

Android Studio复杂按钮设计:如何实现含1个ImageView和2个TextView的设备选择按钮

嘿,这个需求我刚好在做智能设备相关项目时实现过,给你分享几个实用的方案,都能轻松搞定你要的这种带图标+双文本的设备选择按钮~

方案一:自定义布局容器(最灵活,推荐)

这种方式完全自定义按钮的内部结构,想怎么摆就怎么摆,还能轻松添加点击/选中的视觉反馈。

步骤1:创建按钮内部布局

新建一个XML布局文件,比如layout_device_button.xml,用ConstraintLayout或者LinearLayout排版ImageView和两个TextView:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp"
    android:background="@drawable/bg_device_button_selector"
    android:clickable="true"
    android:focusable="true">

    <!-- 设备图标 -->
    <ImageView
        android:id="@+id/iv_device_icon"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/ic_smart_lamp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- 设备名称 -->
    <TextView
        android:id="@+id/tv_device_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="智能吸顶灯"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintStart_toEndOf="@id/iv_device_icon"
        app:layout_constraintTop_toTopOf="@id/iv_device_icon"
        app:layout_constraintMarginStart="12dp" />

    <!-- 设备描述 -->
    <TextView
        android:id="@+id/tv_device_desc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="客厅 · 已连接"
        android:textSize="12sp"
        android:textColor="@color/gray_600"
        app:layout_constraintStart_toStartOf="@id/tv_device_name"
        app:layout_constraintTop_toBottomOf="@id/tv_device_name"
        app:layout_constraintMarginTop="4dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

步骤2:添加按钮状态背景

新建一个Drawable文件bg_device_button_selector.xml,给按钮添加按下、选中、默认状态的视觉反馈:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 按下状态 -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/gray_100" />
            <corners android:radius="8dp" />
        </shape>
    </item>
    <!-- 选中状态(比如用户点击后标记已选) -->
    <item android:state_selected="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/blue_50" />
            <stroke android:width="2dp" android:color="@color/blue_primary" />
            <corners android:radius="8dp" />
        </shape>
    </item>
    <!-- 默认状态 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/white" />
            <stroke android:width="1dp" android:color="@color/gray_300" />
            <corners android:radius="8dp" />
        </shape>
    </item>
</selector>

步骤3:使用按钮

在你的页面布局里直接include这个自定义布局,然后在代码中设置点击事件:

<include
    layout="@layout/layout_device_button"
    android:id="@+id/btn_living_room_lamp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
findViewById<ConstraintLayout>(R.id.btn_living_room_lamp).setOnClickListener {
    // 处理设备选择逻辑
    it.isSelected = !it.isSelected // 切换选中状态
}
方案二:自定义View继承自布局(高复用场景)

如果这个按钮要在很多页面复用,建议封装成自定义View,方便统一管理样式和逻辑。

步骤1:创建自定义View类

class DeviceButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    private val ivIcon: ImageView
    private val tvName: TextView
    private val tvDesc: TextView

    init {
        // 加载自定义布局
        LayoutInflater.from(context).inflate(R.layout.layout_device_button, this, true)
        ivIcon = findViewById(R.id.iv_device_icon)
        tvName = findViewById(R.id.tv_device_name)
        tvDesc = findViewById(R.id.tv_device_desc)

        // 读取自定义属性(可选)
        attrs?.let {
            val typedArray = context.obtainStyledAttributes(it, R.styleable.DeviceButton)
            typedArray.getResourceId(R.styleable.DeviceButton_deviceIcon, 0)?.let { resId ->
                ivIcon.setImageResource(resId)
            }
            typedArray.getString(R.styleable.DeviceButton_deviceName)?.let { name ->
                tvName.text = name
            }
            typedArray.getString(R.styleable.DeviceButton_deviceDesc)?.let { desc ->
                tvDesc.text = desc
            }
            typedArray.recycle()
        }
    }

    // 对外提供设置设备信息的方法
    fun setDeviceInfo(iconRes: Int, name: String, desc: String) {
        ivIcon.setImageResource(iconRes)
        tvName.text = name
        tvDesc.text = desc
    }
}

步骤2:定义自定义属性

在res/values/attrs.xml中添加自定义属性:

<declare-styleable name="DeviceButton">
    <attr name="deviceIcon" format="reference" />
    <attr name="deviceName" format="string" />
    <attr name="deviceDesc" format="string" />
</declare-styleable>

步骤3:使用自定义View

在布局里直接引用:

<com.yourpackage.DeviceButton
    android:id="@+id/btn_bedroom_lamp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:deviceIcon="@drawable/ic_bedroom_lamp"
    app:deviceName="卧室台灯"
    app:deviceDesc="卧室 · 未连接" />

代码中设置点击事件和更新信息:

val bedroomBtn = findViewById<DeviceButton>(R.id.btn_bedroom_lamp)
bedroomBtn.setOnClickListener {
    // 处理选择逻辑
    it.isSelected = !it.isSelected
}
// 动态更新设备信息
bedroomBtn.setDeviceInfo(R.drawable.ic_bedroom_lamp_on, "卧室台灯", "卧室 · 已连接")
方案三:MaterialButton结合Spannable文本(快速实现)

如果想直接用官方MaterialButton控件,可以把双文本合并成SpannableString,配合图标实现:

val materialBtn = findViewById<MaterialButton>(R.id.btn_kitchen_lamp)
// 设置图标
materialBtn.icon = ContextCompat.getDrawable(this, R.drawable.ic_kitchen_lamp)
materialBtn.iconGravity = MaterialButton.ICON_GRAVITY_START
materialBtn.iconPadding = 12.dpToPx() // 自己实现dp转px的扩展方法

// 拼接双文本并设置样式
val deviceName = "厨房吊灯"
val deviceDesc = "\n厨房 · 已连接"
val spannable = SpannableString(deviceName + deviceDesc)
// 设置描述文本的字号
spannable.setSpan(
    AbsoluteSizeSpan(12, true),
    deviceName.length,
    spannable.length,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
// 设置描述文本的颜色
spannable.setSpan(
    ForegroundColorSpan(ContextCompat.getColor(this, R.color.gray_600)),
    deviceName.length,
    spannable.length,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
materialBtn.text = spannable

这种方式优点是不用自定义布局,但双文本的排版灵活性不如前两种方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 18:12:58