Android Dialog 層級(jí)處理方案之ViewDialog

問(wèn)題梳理

在之前公司做直播項(xiàng)目的時(shí)候床三,在首頁(yè)會(huì)彈出很多dialog,當(dāng)時(shí)有2種方案月趟。
一種是一次只彈出一個(gè)灯蝴,每次回到首頁(yè)再次彈出一個(gè)。
第二種層疊堆放孝宗。
因?yàn)楸娝苤蚯钤辏邚棿坝肋h(yuǎn)要在最上層,其次是升級(jí)彈窗等因妇。
第一種我們暫不討論问潭,各家有各家解決方案。
第二種是大家常用方案沙峻,一般解決方案就是我延遲幾秒彈出睦授,則最上層的必然是XX彈窗。但是當(dāng)涉及網(wǎng)絡(luò)接口的時(shí)候摔寨,不可抗拒力就變大了,畢竟存在網(wǎng)絡(luò)延遲和重試問(wèn)題怖辆。
我們針對(duì)第二種情況封裝了基于View的dialog,在內(nèi)部進(jìn)行層級(jí)處理是复,基于FrameLayout的堆疊特性。實(shí)現(xiàn)了ViewDialog竖螃。經(jīng)過(guò)線上的考驗(yàn)淑廊。
使用方式簡(jiǎn)單粗暴。

解決方案

依賴開(kāi)源庫(kù)

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

使用方法

無(wú)需業(yè)務(wù)處理
//直接傳入布局
    ViewDialog(MainActivity@ this, layout = R.layout.activity_main).show()
    // 支持傳入View
    ViewDialog(
        MainActivity@ this,
        rootView = LayoutDialogBinding.inflate(layoutInflater).root
    ).show()

內(nèi)部進(jìn)行業(yè)務(wù)處理 繼承ViewDialog
 //自定義Demo
 class DemoDialog2(content: Activity) :
ViewDialog(content, rootView = LayoutDialogBinding.inflate(content.layoutInflater).root) {


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

//level 越大 顯示的時(shí)候就在上面
override fun level() = 3

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

//同類型只能存在一個(gè) 避免多次彈窗
override fun single()=true
//是否彈窗變黑
override fun isDark()=true
//支持響應(yīng)事件
override  fun   canceledAble()=true

//是否可以點(diǎn)擊外部取消
override  fun canceledOnTouchOutside() = true
//業(yè)務(wù)處理
    override fun business() {
        super.business()
    }

//直接調(diào)用show()和dismiss()就行特咆,無(wú)需其他操作
     val dialog2=   DemoDialog2(MainActivity@this)
        dialog2 .show()
        dialog2.dismiss()

原理解析

找到當(dāng)前Activity的rootView,添加進(jìn)一個(gè)容器 XViewDialogContainer季惩,設(shè)置tag。
當(dāng)ViewDialog 調(diào)用show()的時(shí)候腻格,會(huì)檢查當(dāng)前頁(yè)面有沒(méi)有對(duì)應(yīng)容器画拾,沒(méi)有就進(jìn)行容器添加,有則直接調(diào)用容器將自己添加進(jìn)去菜职。
在 容器內(nèi)部青抛,根據(jù)所謂的level 值 進(jìn)行添加,其實(shí)這里就是z值酬核,容器的Z值被設(shè)置的特別高蜜另,為65535,一般沒(méi)有正常View會(huì)超過(guò)這個(gè)值嫡意。
在內(nèi)部添加ViewDialog的時(shí)候举瑰,會(huì)對(duì)傳入的ViewDialog 進(jìn)行排序。如果需要響應(yīng)返回事件和點(diǎn)擊外部消失蔬螟,也做了對(duì)應(yīng)的處理此迅。
因?yàn)閷?shí)現(xiàn)了DialogInterface ,所以使用方法和普通dialog一致。
現(xiàn)將核心代碼奉上邮屁,大家可以參考整袁,歡迎提出建議!

源碼

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)
            //還沒(méi)容器就添加容器
            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) {
                    //遍歷第一個(gè)
                    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軸排序響應(yīng)返回事件
     * @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
    }

}

總結(jié)

該方案并非百分百解決所有問(wèn)題佑吝,但是基于我們的業(yè)務(wù)線是沒(méi)有問(wèn)題的坐昙。
但是該方案因?yàn)槭腔赩iew,則不可能比默認(rèn)系統(tǒng)Dialog 彈出層級(jí)更高,所以在使用的時(shí)候芋忿,需要結(jié)合自身業(yè)務(wù)需求進(jìn)行處理炸客。
代碼比較簡(jiǎn)單,侵入性極低戈钢”韵桑基本不存在泄漏風(fēng)險(xiǎn)。大家可以參考自己業(yè)務(wù)線需求處理殉了。

基友網(wǎng)地址

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末开仰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子薪铜,更是在濱河造成了極大的恐慌众弓,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隔箍,死亡現(xiàn)場(chǎng)離奇詭異谓娃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蜒滩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)滨达,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人俯艰,你說(shuō)我怎么就攤上這事捡遍。” “怎么了蟆炊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵稽莉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我涩搓,道長(zhǎng)污秆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任昧甘,我火速辦了婚禮良拼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘充边。我一直安慰自己庸推,他們只是感情好常侦,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贬媒,像睡著了一般聋亡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上际乘,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天坡倔,我揣著相機(jī)與錄音,去河邊找鬼脖含。 笑死罪塔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的养葵。 我是一名探鬼主播征堪,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼关拒!你這毒婦竟也來(lái)了佃蚜?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夏醉,失蹤者是張志新(化名)和其女友劉穎爽锥,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體畔柔,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年臣樱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了靶擦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雇毫,死狀恐怖玄捕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棚放,我是刑警寧澤枚粘,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站飘蚯,受9級(jí)特大地震影響馍迄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜局骤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一攀圈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峦甩,春花似錦赘来、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嗦篱。三九已至,卻和暖如春幌缝,著一層夾襖步出監(jiān)牢的瞬間灸促,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工狮腿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腿宰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓缘厢,卻偏偏與公主長(zhǎng)得像吃度,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贴硫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容