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()

完事

更多相关文章

  1. Flutter 项目代码打包进 android App的快速集成方案
  2. Android培训班(84)Dalvik虚拟机的JNI测试函数
  3. Android(安卓)Fragment 真正的完全解析(上)
  4. Android(安卓)Material Design系列之RecyclerView和CardView
  5. Android中TextView的富文本显示
  6. Android内存优化(二)--布局优化
  7. Eclipse+CDT+GDB 调试android NDK程序
  8. android 使用butterknife简化加载布局控件
  9. Android(安卓)自定义dialog,实现右上角显示一个控件按钮

随机推荐

  1. Android平台各类恶意软件及病毒概览
  2. Android中的通知—Notification
  3. Android(安卓)TextView设置自动识别的超
  4. Android打包AAR及与unity通信方法
  5. 如何让android 支持多种屏幕尺寸
  6. Android学习总结
  7. 搞明白android
  8. Android(安卓)动态加载布局文件
  9. Android中添加自定义按键 ---- 非标准做
  10. Android(安卓)应用程式的基本档案结构