基于kt的对话框封装

一、前言

近期突然想坚持的写写博客,以后每周可能都会都会不定时的发上几篇,来记录一下这一周的学习成果以及回顾一下知识。

二、背景

平时开发中或多或少都会使用到[Dialog,每次都需要自定义继承Dialog,写多了不胜其烦,索性直接对Dialog做个简单的封装。以后直接拷贝来用,话不多说上代码。如有大佬发现问题也请多多指教一起讨论!

三、相关源码:

CustomControlsDialogUtils.kt

package com.example.thirdpartyintegrationmodule.util

import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.Window
import kotlin.math.roundToInt

object CustomControlsDialogUtils {
    
    /**
     * 创建对话框
     * layout:弹框布局文件
     * theme:弹框布局样式
     * direction:弹框方向
     * listener: 弹框监听
     */
    fun createCustomDialog(
        context: Context?, layout: Int,
        theme: Int, direction: Int,
        onCreatedView: (Dialog, View) -> Unit?
    ): Dialog {
        val view = LayoutInflater.from(context).inflate(layout, null)
        val dialog: Dialog = CustomDialog(context!!, view, theme)
        return run {
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
            dialog.setCancelable(true)// 是否可取消(按物理返回键)
            onCreatedView(dialog, view)
            setDialogWindow(dialog.window!!, direction)
            dialog
        }
    }

    /**
     * 获取控件的绑定
     */
    fun <T : View> findViewById(mView: View, viewId: Int): T? {
        return mView.findViewById(viewId)
    }

    /**
     * 修改对话框的属性
     */
    private fun setDialogWindow(window: Window, direction: Int) {
        val widthRatio = 0.85f // 默认的宽度比
        val attr = window.attributes
        attr.width =
            ((SoftInputUtils.getScreenWidthPixels() * widthRatio).roundToInt()) // 获取屏幕宽度*设置的默认宽度
        attr.gravity = direction // 设置对话框参数
        window.attributes = attr // 设置对话框属性
    }

    /**
     * 关闭对话框
     */
    fun dismissDialog(dialog: Dialog?) {
        if (dialog != null) {
            val context = (dialog.context as ContextWrapper).baseContext
            if (context is Activity) {
                dialog.dismiss()
            }
        }
    }

    /**
     * 构建自定义的对话框参数
     */
    open class CustomDialog(mContext: Context, var view: View, var mTheme: Int) :
        Dialog(mContext) {
        init {
            setTransparentBackground()
        }

        override fun show() {
            super.show()
            setContentView(view)
        }

        private fun setTransparentBackground() {
            window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        }
    }
    
}

  调用,这里的dialog_privacy_rights,dialog_style是我根据自己的需求场景自定义的xml文件以及样式,可自行更换

       R.id.customControlsDialogBoxBut -> {
                CustomControlsDialogUtils.createCustomDialog(this,
                    R.layout.dialog_privacy_rights,
                    R.style.dialog_style,
                    Gravity.BOTTOM) { dialog: Dialog, mView: View ->

                    CustomControlsDialogUtils.findViewById<TextView>(mView,R.id.dialogPrivacyTitleTv)?.text = "隐私政策"

                    CustomControlsDialogUtils.findViewById<TextView>(mView, R.id.dialogPrivacyDisagreeTv)?.setOnClickListener {
                        CustomControlsDialogUtils.dismissDialog(dialog)
                    }

                    CustomControlsDialogUtils.findViewById<TextView>(mView, R.id.dialogPrivacyAgreeTv)?.setOnClickListener {
                        CustomControlsDialogUtils.dismissDialog(dialog)
                    }
                }.show()
            }

  基本样式参考

 <style name="dialog_style" parent="@android:style/Theme.Dialog">
        <!-- 是否有边框 -->
        <item name="android:windowFrame">@null</item>
        <!-- 是否漂现在activity上 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 是否半透明 -->
        <item name="android:windowIsTranslucent">true</item>
        <!-- 是否隐藏标题 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 设置背景透明 -->
        <item name="android:background">@android:color/transparent</item>
        <!-- 是否允许背景变暗 -->
        <item name="android:backgroundDimEnabled">true</item>
        <!-- 设置蒙层的透明度0-1 -->
        <item name="android:backgroundDimAmount">0.5</item>
        <!-- 设置背景 -->
        <item name="android:windowBackground">@null</item>
        <!-- 设置点击弹窗之外是否关闭 -->
        <item name="android:windowCloseOnTouchOutside">true</item>
        <!-- 设置键盘弹窗模式 -->
        <item name="android:windowSoftInputMode">adjustResize</item>
        <!-- 设置动画样式 -->
        <item name="android:windowAnimationStyle">@style/bottomDialogAnimation</item>
    </style>

四、杂谈:

结合网上整理了一下关于Dialog的面试问题以及回答方式。

1、当Dialog存在于Activity上时按下Home键的生命周期?

答案:
  onPause ⇒ onStop ⇒ 重新显示 ⇒ onRestart ⇒ onStart () ⇒ onResume,开始按下Home键,才会继续执行onPause()和onStop()方法。到这里就说明对话框并没有使Activity进入后台,而是在点击了Home键后Activity才进入后台工作。
  原因就是,其实Dialog是Activity的一个组件,此时Activity并不是不可见,而是被Dialog组件覆盖了其他的组件,此时我们无法对其他组件进行操作而已。

2、Activity、Dialog、PopupWindow、Toast 与Window的关系

答案:

  • Activity
      在Activity创建过程中所创建的PhoneWindow,是层级最小的Window,叫做应用Window,层级范围1-99。(层级范围大的Window可以覆盖层级小的Window)
  • Dialog
      Dialog的显示过程和Activity基本相同,也是创建了PhoneWindow,初始化DecorView,并将Dialog的视图添加到DecorView中,最终通过addView显示出来。
    但是有一点不同的是,Dialog的Window并不是应用窗口,而是子窗口,层级范围1000-1999,子Window的显示必须依附于应用窗口,也会覆盖应用级- Window。这也就是为什么Dialog传入的上下文必须为Activity的Context了。
  • PopupWindow
      PopupWindow的显示就有所不同了,它没有创建PhoneWindow,而是直接创建了一个View(PopupDecorView),然后通过WindowManager的addView方法显示出来了。没有创建PhoneWindow,是不是就跟Window没关系了呢?并不是,其实只要是调用了WindowManager的addView方法,那就是创建了Window,跟你有没有创建PhoneWindow无关。View就是Window的表现形式,只不过PhoneWindow的存在让Window形象更立体了一些。所以PopupWindow也是通过Window展示出来的,而它的Window层级属于子Window,必须依附与应用窗口。
  • Toast
      Toast和PopupWindow比较像,没有新建PhoneWindow,直接通过addView方法显示View即可。不同的是它属于系统级Window,层级范围2000-2999,所以无须依附于Activity。

总结
  四个比较下来,可以发现,只要想显示View,就会涉及到WindowManager的addView方法,也就用到了Window这个概念,然后会根据不同的分层依次显示覆盖到界面上。不同的是,Activity和Dialog涉及到了布局比较复杂,还会有布局主题等元素,所以用到了PhoneWindow进行一个解耦,帮助他们管理View。而PopupWindow和Toast结构比较简单,所以直接新建一个类似DecorView的View,通过addView显示到界面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容