基于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显示到界面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容