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

如何通过代码自定义含多按钮、EditText的AlertDialog.Builder

动态增减输入组的AlertDialog纯代码实现方案

核心逻辑:setView()只需要传入一个根ViewGroup,所有动态生成的控件都作为子View添加到这个根布局内部的容器中即可,不需要多次调用setView()。

实现步骤

1. 构建层级化的根布局

用垂直线性布局作为最外层根容器,内部拆成两个固定部分:

  • 专门承载动态输入组的垂直线性容器
  • 固定在底部的「+」新增按钮
// 初始化弹窗根容器(垂直方向)
val dialogRoot = LinearLayout(context).apply {
    orientation = LinearLayout.VERTICAL
    // 配置16dp内边距,避免内容贴弹窗边框
    val paddingValue = (16 * resources.displayMetrics.density).toInt()
    setPadding(paddingValue, paddingValue, paddingValue, paddingValue)
}

// 初始化动态输入组专属容器
val inputGroupContainer = LinearLayout(context).apply {
    orientation = LinearLayout.VERTICAL
    layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.MATCH_PARENT,
        LinearLayout.LayoutParams.WRAP_CONTENT
    )
}
dialogRoot.addView(inputGroupContainer)

// 初始化固定的「+」新增按钮
val addButton = Button(context).apply {
    text = "+"
    layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT
    ).apply {
        gravity = Gravity.END
        topMargin = (8 * resources.displayMetrics.density).toInt()
    }
}
dialogRoot.addView(addButton)

2. 封装单组输入行的生成逻辑

每一组输入控件用水平线性布局承载,左侧EditText通过layout_weight占满剩余宽度,右侧放置行操作按钮(默认配置为删除当前行的按钮,可按需修改逻辑)。封装成复用方法,每次调用就生成新行追加到动态容器中:

fun addInputRow() {
    // 单组行布局(水平方向)
    val singleRow = LinearLayout(context).apply {
        orientation = LinearLayout.HORIZONTAL
        layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            bottomMargin = (8 * resources.displayMetrics.density).toInt()
        }
    }

    // 行内输入框
    val inputEt = EditText(context).apply {
        hint = "请输入内容"
        layoutParams = LinearLayout.LayoutParams(
            0,
            LinearLayout.LayoutParams.WRAP_CONTENT,
            1f // 权重设为1,占满除按钮外的剩余宽度
        ).apply {
            marginEnd = (8 * resources.displayMetrics.density).toInt()
        }
    }
    singleRow.addView(inputEt)

    // 行内操作按钮(默认实现为删除当前行)
    val rowActionBtn = Button(context).apply {
        text = "×"
        layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        )
        setOnClickListener {
            inputGroupContainer.removeView(singleRow)
        }
    }
    singleRow.addView(rowActionBtn)

    // 将新行追加到动态容器
    inputGroupContainer.addView(singleRow)
}

3. 绑定事件并构建弹窗

初始化时默认添加1组输入行,给「+」按钮绑定新增行的点击事件,最后将根布局一次性传入setView()即可,不需要重复调用该方法:

// 弹窗初始化时默认展示1组输入框
addInputRow()

// 绑定新增按钮点击事件
addButton.setOnClickListener {
    addInputRow()
}

// 构建AlertDialog
val customDialog = AlertDialog.Builder(context)
    .setTitle("自定义输入弹窗")
    .setView(dialogRoot) // 仅需传入一次根布局
    .setPositiveButton("确定") { _, _ ->
        // 确定按钮点击时,遍历容器获取所有输入内容
        val inputList = mutableListOf<String>()
        repeat(inputGroupContainer.childCount) { index ->
            val currentRow = inputGroupContainer.getChildAt(index) as LinearLayout
            val currentEt = currentRow.getChildAt(0) as EditText
            inputList.add(currentEt.text.toString().trim())
        }
        // 此处可对inputList做后续业务处理
    }
    .setNegativeButton("取消", null)
    .create()

customDialog.show()

优化提示

  • 动态增减控件时不要直接操作根布局,所有输入行统一添加到inputGroupContainer中,避免打乱「+」按钮的固定位置
  • 如果觉得纯代码编写布局参数繁琐,也可以提前将根布局、单组输入行写成XML文件,通过inflate加载后再做动态添加,逻辑完全一致
  • 输入校验、行按钮自定义逻辑(比如选择按钮、跳转按钮)都可以直接在addInputRow()方法内给对应控件设置监听实现
  • 禁止多次调用setView(),该方法每次触发都会替换Dialog的整个内容视图,之前添加的内容会被直接覆盖

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 02:57:09