如何通过代码自定义含多按钮、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
相关产品推荐
相关产品推荐

