Android Dialog 层级处理方案之ViewDialog

问题梳理

在之前公司做直播项目的时候,在首页会弹出很多dialog,当时有2种方案。
一种是一次只弹出一个,每次回到首页再次弹出一个。
第二种层叠堆放。
因为众所周知原因,政策弹窗永远要在最上层,其次是升级弹窗等。
第一种我们暂不讨论,各家有各家解决方案。
第二种是大家常用方案,一般解决方案就是我延迟几秒弹出,则最上层的必然是XX弹窗。但是当涉及网络接口的时候,不可抗拒力就变大了,毕竟存在网络延迟和重试问题。
我们针对第二种情况封装了基于View的dialog,在内部进行层级处理,基于FrameLayout的堆叠特性。实现了ViewDialog。经过线上的考验。
使用方式简单粗暴。

解决方案

依赖开源库

 implementation 'io.github.nuonuoOkami:ViewDialog:1.0.0'

使用方法

无需业务处理
//直接传入布局
    ViewDialog(MainActivity@ this, layout = R.layout.activity_main).show()
    // 支持传入View
    ViewDialog(
        MainActivity@ this,
        rootView = LayoutDialogBinding.inflate(layoutInflater).root
    ).show()

内部进行业务处理 继承ViewDialog
 //自定义Demo
 class DemoDialog2(content: Activity) :
ViewDialog(content, rootView = LayoutDialogBinding.inflate(content.layoutInflater).root) {


//控件处理
override fun bindUI(rootView: View) {
    super.bindUI(rootView)
}

//level 越大 显示的时候就在上面
override fun level() = 3

//默认warp
override fun params(): LayoutParams {
    val layoutParams = LayoutParams(
        LayoutParams.WRAP_CONTENT,
        LayoutParams.WRAP_CONTENT
    )
    layoutParams.gravity = gravity()
    return layoutParams
}

//同类型只能存在一个 避免多次弹窗
override fun single()=true
//是否弹窗变黑
override fun isDark()=true
//支持响应事件
override  fun   canceledAble()=true

//是否可以点击外部取消
override  fun canceledOnTouchOutside() = true
//业务处理
    override fun business() {
        super.business()
    }

//直接调用show()和dismiss()就行,无需其他操作
     val dialog2=   DemoDialog2(MainActivity@this)
        dialog2 .show()
        dialog2.dismiss()

原理解析

找到当前Activity的rootView,添加进一个容器 XViewDialogContainer,设置tag。
当ViewDialog 调用show()的时候,会检查当前页面有没有对应容器,没有就进行容器添加,有则直接调用容器将自己添加进去。
在 容器内部,根据所谓的level 值 进行添加,其实这里就是z值,容器的Z值被设置的特别高,为65535,一般没有正常View会超过这个值。
在内部添加ViewDialog的时候,会对传入的ViewDialog 进行排序。如果需要响应返回事件和点击外部消失,也做了对应的处理。
因为实现了DialogInterface ,所以使用方法和普通dialog一致。
现将核心代码奉上,大家可以参考,欢迎提出建议!

源码

open class ViewDialog(context: Context, rootView: View? = null, layout: Int? = null) : IViewDialog,ViewDialogBusiness,
    FrameLayout(context) {
    override fun cancel() {
        dismiss()
    }

    override fun dismiss() {
        if (parent != null) {
            (parent as ViewGroup).removeView(this)
        }
    }
    open fun level(): Int = 0
    open fun params(): LayoutParams {
        val layoutParams = LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT
        )
        layoutParams.gravity = gravity()
        return layoutParams
    }

    open fun gravity() = Gravity.CENTER
    init {
        if (rootView == null && layout == null) {
            throw RuntimeException("必须选择传入布局或者view")
        }


        if (rootView != null) {
            addView(rootView, params())
            bindUI(rootView)

        } else {
            val root = LayoutInflater.from(context)
                .inflate(layout!!, null)
            addView(
                root, params()
            )

            bindUI(root)


        }

        this.z = this.level().toFloat()

        if (this.isDark()) {
            this.setBackgroundColor("#4d000000".toColor())
        }
        safeClickListener {
            if (canceledOnTouchOutside()) {
                dismiss()
            }
        }


    }


    open fun bindUI(rootView: View) {

    }


    open fun show() {
        val activity = context.findActivity()
        if (activity != null) {
            val root = activity.window.decorView.rootView
            var viewDialogContainer =
                root.findViewWithTag<XViewDialogContainer>(XViewDialogContainer::class.java.name)
            //还没容器就添加容器
            if (viewDialogContainer == null) {
                viewDialogContainer = XViewDialogContainer(context)
                (root as ViewGroup).addView(
                    viewDialogContainer,
                    ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                )

            }
            //显示添加
            viewDialogContainer.showDialog(this)

        }
    }

    override fun business() {

    }


}
class XViewDialogContainer : FrameLayout {


    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    fun showDialog(child: ViewDialog) {


        val params =
            LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        if (child.single()) {
            children
            val has = children.firstOrNull {
                it::class.java.name.equals(child::class.java.name)
            }
            if (has != null) {
                return
            } else {
                addView(child, params)
            }
        } else {
            addView(child, params)
        }


    }

    init {
        isFocusable = true
        isFocusableInTouchMode = true;
        requestFocus()
        z = 65535f

        setOnKeyListener { _, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                if (childCount > 0) {
                    //遍历第一个
                    val childZ = sortByZ()

                    val first = childZ.firstOrNull {
                        (it as IViewDialog).canceledAble()
                    }
                    if (first != null) {
                        (first as IViewDialog).dismiss()
                    }
                }

            }
            false

        }
    }

    override fun getChildDrawingOrder(childCount: Int, drawingPosition: Int): Int {
        val orderedViews: MutableList<View> = ArrayList()
        for (j in 0 until childCount) {
            val child = getChildAt(j)
            orderedViews.add(child)
        }

        orderedViews.sortWith { view1, view2 ->
            val zIndex1 = view1.z.toInt()
            val zIndex2 = view2.z.toInt()
            zIndex1.compareTo(zIndex2)
        }

        return indexOfChild(orderedViews[drawingPosition])
    }

    /**
     * z轴排序响应返回事件
     * @return MutableList<View>
     */

    private fun sortByZ(): MutableList<View> {
        val list = children.toMutableList()
        list.toMutableList().sortWith { a, b ->
            a.z.toInt().compareTo(b.z.toInt())
        }
        return list

    }

    init {
        tag=this::class.java.name
    }

}

总结

该方案并非百分百解决所有问题,但是基于我们的业务线是没有问题的。
但是该方案因为是基于View,则不可能比默认系统Dialog 弹出层级更高,所以在使用的时候,需要结合自身业务需求进行处理。
代码比较简单,侵入性极低。基本不存在泄漏风险。大家可以参考自己业务线需求处理。

基友网地址

https://github.com/nuonuoOkami/XViewDialog/blob/master/README.md

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

推荐阅读更多精彩内容