Android(安卓)自定义弹窗框架
16lz
2021-01-26
Android 自定义弹窗框架
- 前言
- 样式图
- 写一个Base类 BaseDialog
- 建立基本弹窗布局 dialog_def_loading_view.xml
- 写一个按钮样式选择 ButtonStyle
- 写一个按钮点击监听 OnBtnClick
- 自定义弹窗 ButtonDialog
- 自定义弹窗 EditDialog
- 布局
- 代码
- 自定义弹窗 SingleChooseDialog
- 布局
- 代码
- 使用方法
- 完事
前言
在项目开发中,我们经常需要创建各种各样的自定义弹窗,为了简约代码和统一管理,我初步搭建了这么一个简单的框架,待日后在新项目中使用后再不断维护。
------ 更新 2019/11/27 ------
- 增加单选弹窗
- 修复魅族手机出现编辑框清空崩溃的问题
- 增加弹窗初始化布局宽高方向代码
样式图
这只是初始的想法搭建的框架,本文只说明思维方式,并不提供其他类型弹窗代码,有需要的朋友根据以下代码,进行自定义开发
另外本文也是提供一种在每个弹窗上都带有进度条的代码方法,供大家参考
写一个Base类 BaseDialog
/** * 基础弹窗 * * @author D10NG * @date on 2019-10-25 14:49 */open class BaseDialog<T> constructor( private val context: Context) { /** 弹窗创建器 */ protected val builder = AlertDialog.Builder(context) /** 最终的弹窗实例 */ protected var alert: AlertDialog? = null /** 基础弹窗布局 */ val binding: DialogDefLoadingViewBinding = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.dialog_def_loading_view, null, false) /** * 创建 */ open fun create() : T { binding.loadIndeterminate = true binding.loadVisible = false builder.setView(binding.root) builder.setCancelable(false) alert = builder.create() return this as T } /** * 移除所有button */ open fun removeAllButtons() { binding.buttonLayout.removeAllViews() } /** * 移除content内容 */ open fun removeContent() { binding.contentLayout.removeAllViews() } /** * 设置标题 */ open fun setTittle(tittle: String) : T { binding.tittle = tittle return this as T } /** * 设置二级文本 */ open fun setMsg(msg: String) : T { binding.message = msg return this as T } /** * 设置图标 */ open fun setIcon(resId: Int) : T { binding.image.setImageResource(resId) return this as T } /** * 设置图标 */ open fun setIcon(bitmap: Bitmap) : T { binding.image.setImageBitmap(bitmap) return this as T } /** * 开始加载中 */ open fun startLoad(indeterminate: Boolean, progress: Int, max: Int) { binding.loadIndeterminate = indeterminate binding.loadProgress = progress binding.loadMax = max binding.loadVisible = true } /** * 停止加载中 */ open fun stopLoad() { binding.loadVisible = false } /** * 添加button事件 */ open fun addAction(text: String, style: Int, onBtnClick: OnBtnClick?) : T { binding.buttonLayout.addView(createButton(text, style, onBtnClick)) return this as T } /** * 显示 */ open fun show() { alert?.show() } /** * 关闭 */ open fun dismiss() { alert?.dismiss() } protected fun createButton(text: String, style: Int, onBtnClick: OnBtnClick?) : Button { val button = Button(context) button.background = ContextCompat.getDrawable(context, R.drawable.button_top_line_bg) button.text = text when(style) { ButtonStyle.THEME -> button.setTextColor(ContextCompat.getColor(context, R.color.colorPrimary)) ButtonStyle.NORMAL -> button.setTextColor(ContextCompat.getColor(context, R.color.text_hint_color)) ButtonStyle.ERROR -> button.setTextColor(ContextCompat.getColor(context, R.color.text_wrong_color)) } button.setOnClickListener { if (null == onBtnClick) { dismiss() } else { onBtnClick.click(this, text) } } return button }}
建立基本弹窗布局 dialog_def_loading_view.xml
此处用了 DataBinding 使用方法参考我的另一篇文章 :
Android Kotlin学习 Jitpack 组件之DataBinding
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <import type="android.view.View"/> <variable name="loadIndeterminate" type="boolean" /> <variable name="loadProgress" type="int" /> <variable name="loadMax" type="int" /> <variable name="loadVisible" type="boolean" /> <variable name="tittle" type="String" /> <variable name="message" type="String" /> data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <TextView android:id="@+id/txt_tittle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:gravity="center" android:singleLine="true" android:text="@{tittle}" android:textColor="#ff333333" android:textSize="18sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:adjustViewBounds="true" android:scaleType="centerInside" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/txt_tittle" /> <TextView android:id="@+id/txt_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:ellipsize="end" android:lineSpacingExtra="6sp" android:maxLines="10" android:text="@{message}" android:textAlignment="center" android:textColor="#ff666666" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/image" /> <LinearLayout android:id="@+id/content_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/txt_message"> LinearLayout> <ProgressBar android:id="@+id/pb_load" style="?android:attr/progressBarStyleHorizontal" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:indeterminate="@{loadIndeterminate}" android:max="@{loadMax}" android:progress="@{loadProgress}" android:visibility="@{loadVisible? View.VISIBLE : View.INVISIBLE}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/content_layout" /> <LinearLayout android:id="@+id/button_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/pb_load"> LinearLayout> androidx.constraintlayout.widget.ConstraintLayout>layout>
写一个按钮样式选择 ButtonStyle
object ButtonStyle { const val THEME = 1 const val ERROR = 2 const val NORMAL = 3}
写一个按钮点击监听 OnBtnClick
interface OnBtnClick { fun click(d0: BaseDialog<*>, text: String)}
自定义弹窗 ButtonDialog
class ButtonDialog constructor( context: Context) : BaseDialog<ButtonDialog>(context)
自定义弹窗 EditDialog
布局
dialog_edit_view.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <data> data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/ti_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:errorEnabled="true"> <androidx.appcompat.widget.AppCompatEditText android:id="@+id/edt_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="hint" /> com.google.android.material.textfield.TextInputLayout> LinearLayout>layout>
代码
/** * 带编辑框的弹窗 * * @author D10NG * @date on 2019-11-23 10:05 */class EditDialog constructor( private val context: Context) : BaseDialog<EditDialog>(context) { init { // 改变宽度 val params = binding.contentLayout.layoutParams params.width = LinearLayout.LayoutParams.MATCH_PARENT binding.contentLayout.layoutParams = params } /** 编辑框列表 */ private val edtMap: MutableMap<String, DialogEditViewBinding> = mutableMapOf() /** * 添加一个编辑框 * @param tag 标签 * @param text 初始文本 * @param hint 提示文本 */ fun addEdit(tag: String, text: String, hint: String) : EditDialog { val viewBinding: DialogEditViewBinding = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.dialog_edit_view, null, false ) viewBinding.edtText.setText(text) viewBinding.tiLayout.hint = hint binding.contentLayout.addView(viewBinding.root) edtMap[tag] = viewBinding return this } /** * 获取输入文本 * @param tag 标签 */ fun getInputText(tag: String) : String { return edtMap[tag]?.edtText?.text.toString().trim() } /** * 显示错误信息 * @param tag 标签 * @param value 信息 */ fun setError(tag: String, value: String) : EditDialog { edtMap[tag]?.tiLayout?.error = value return this }}
自定义弹窗 SingleChooseDialog
布局
dialog_recycle_view.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="startText" type="String" /> <variable name="endText" type="String" /> data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/txt_start" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="@{startText}" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rcv" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> androidx.recyclerview.widget.RecyclerView> <TextView android:id="@+id/txt_end" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="@{endText}" /> LinearLayout>layout>
dialog_normal_item_view.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="itemText" type="String" /> <variable name="isSelected" type="boolean" /> data> <LinearLayout android:layout_width="match_parent" android:layout_height="35dp" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="match_parent" android:ellipsize="middle" android:gravity="center" android:minWidth="100dp" android:text="@{itemText}" android:textColor="@{isSelected? @color/colorPrimary : @color/text_hint_color}" android:textSize="@{isSelected? 80 : 50}" /> LinearLayout>layout>
代码
/** * 单选弹窗 * * @author D10NG * @date on 2019-11-25 16:36 */class SingleChooseDialog constructor( private val context: Context) : BaseDialog<SingleChooseDialog>(context) { init { // 改变内容排列方向 binding.contentLayout.orientation = LinearLayout.HORIZONTAL // 改变固定高度 val params = binding.contentLayout.layoutParams params.height = 600 binding.contentLayout.layoutParams = params } /** 选择项列表 */ private val recyclerMap: MutableMap<String, DialogRecycleViewBinding> = mutableMapOf() /** * 添加选择列表 * @param tag 标签 * @param selectItem 选择项 * @param list 全部选项 * @param start 开始文本 * @param end 结束文本 */ fun addSelectionList(tag: String, selectItem: String, list: List<String>, start: String, end: String) : SingleChooseDialog { val viewBinding: DialogRecycleViewBinding = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.dialog_recycle_view, null, false ) viewBinding.startText = start viewBinding.endText = end viewBinding.rcv.layoutManager = LinearLayoutManager(context) val adapter = NormalAdapter(selectItem, list) viewBinding.rcv.adapter = adapter viewBinding.rcv.post { viewBinding.rcv.smoothScrollToPosition(list.indexOf(selectItem) + 3) } binding.contentLayout.addView(viewBinding.root) recyclerMap[tag] = viewBinding return this } /** * 获取选中项文本内容 */ fun getSelectOnTag(tag: String) : String { val viewBinding = recyclerMap[tag]?: return "" val adapter = viewBinding.rcv.adapter as NormalAdapter return adapter.selectStr }}
使用方法
显示普通按键弹窗
fun test(v: View) { val buttonDialog = ButtonDialog(this) .setTittle("标题") .setMsg("文本,段落,清晰,强大觉得还u会丢啊就是不对劲啊混合双打u看见我还大手大脚卡号!「大三大四的」") .setIcon(R.mipmap.icon_test) .addAction("确定", ButtonStyle.THEME, null) .addAction("取消", ButtonStyle.NORMAL, null) .addAction("乱来", ButtonStyle.ERROR, object : OnBtnClick{ override fun click(d0: BaseDialog<*>, text: String) { // 进度条 d0.startLoad(true, 50, 100) } }) .create() buttonDialog.show() // 取消进度条 buttonDialog.stopLoad() }
显示输入框弹窗
fun test(v: View) { val editDialog = EditDialog(this) .setTittle("标题") .setMsg("设置文本") .addEdit("tag1", "12345678901234567890", "请输入密码") .addAction("确定", ButtonStyle.THEME, object : OnBtnClick{ override fun click(d0: BaseDialog<*>, text: String) { val dialog = d0 as EditDialog if (dialog.getInputText("tag1") != "666666") { dialog.setError("tag1", "密码错误") } else { dialog.dismiss() } } }) .create() editDialog.show() }
显示单选弹窗
val list = mutableListOf<String>() for (i in 0 .. 100) { list.add("$i") } SingleChooseDialog(this) .setTittle("提示") .setMsg("设定温度") .addSelectionList("temp", "50", list, "", "℃") .addAction(resources.getString(R.string.sure), ButtonStyle.THEME, object : OnBtnClick{ override fun click(d0: BaseDialog<*>, text: String) { val d = d0 as SingleChooseDialog // 拿到温度 val temp = d.getSelectOnTag("temp").toInt() viewModel.update(temp) d.dismiss() } }) .addAction(resources.getString(R.string.cancel), ButtonStyle.NORMAL, null) .create() .show()
完事
更多相关文章
- Flutter 项目代码打包进 android App的快速集成方案
- Android培训班(84)Dalvik虚拟机的JNI测试函数
- Android(安卓)Fragment 真正的完全解析(上)
- Android(安卓)Material Design系列之RecyclerView和CardView
- Android中TextView的富文本显示
- Android内存优化(二)--布局优化
- Eclipse+CDT+GDB 调试android NDK程序
- android 使用butterknife简化加载布局控件
- Android(安卓)自定义dialog,实现右上角显示一个控件按钮