开箱即用-Android设计模式实战-拦截弹窗展示

Android中多个弹窗的排队展示

在实际的开发中,我们会遇到多个弹窗的问题,又不能一次全部弹出,所以我们的代码就是 if else if else 循环嵌套。

例如我们在工作详情中申请一个工作,首先我们就要走一个校验接口,然后接口会告诉需要判断的对象。如下判断逻辑:

  1. 首先需要先判断是否是新用户,如果是新用户需要去参加线上培训(跳转页面)
  2. 判断用户是否完善了个人信息,如果没有需要去完善个人信息 (跳转页面)
  3. 判断个人用工状态是否是Approve,如果个人状态不对需要联系管理员 (拨打电话)
  4. 判断当前用户是否填写了每日新冠报告,如果没有需要填写新冠表单(跳转浏览器)
  5. 判断当前申请的工作是否需要押金,如果要押金弹框提示(弹框)
  6. 如果需要押金,你的钱包又恰好有钱,会提示是否把钱包的钱充值到押金(弹框)
  7. 如果改工作需要人脸信息,需要你采集人脸 (跳转页面)
  8. 如果是非直营的工作,会提示你非自营工作 (弹窗)
  9. 如果该工作需要what's app 会弹窗提示你输入账号 (弹框)
  10. 如果该工作是银行卡结算,你又没有银行卡信息,需要填写银行卡(新页面)

...

真实业务肯定不止10个条件了,这里就先写10个吧。来,键盘给你,你来写if else 。你写完了? 中间第5个后面再加一个判断 如果你没有填写技能 去新页面填写你的技能! 这么多if else 中你真的改的动吗?

什么?你业务中就是这么写的?你改的动?

就喜欢这种朴实无华的程序员,但是呢相信我,真的非常简单。毕竟偷懒才是程序员的第一生产力,所有的设计模式,框架,封装什么的,都是为了偷懒,让我们更轻松一点。来吧,看看我的实现!

一、拦截器模式的定义

真正的实现: 定义拦截器接口

interface Interceptor {
    fun intercept(chain: InterceptChain)
}

定义一个拦截器的基类

abstract class BaseInterceptImpl : Interceptor {

    protected var mChain: InterceptChain? = null

    @CallSuper
    override fun intercept(chain: InterceptChain) {
        mChain = chain
    }

}

具体的逻辑都在拦截链管理类中

class InterceptChain private constructor(
    // 弹窗的时候可能需要Activity/Fragment环境。
    val activity: FragmentActivity? = null,
    val fragment: Fragment? = null,
    private var interceptors: MutableList<Interceptor>?
) {
    companion object {

        @JvmStatic
        fun create(count: Int = 0): Builder {
            return Builder(count)
        }
    }

    private var index: Int = 0

    // 执行拦截器。
    fun process() {
        interceptors ?: return
        when (index) {
            in interceptors!!.indices -> {
                val interceptor = interceptors!![index]
                index++
                interceptor.intercept(this)
            }

            interceptors!!.size -> {
                clearAllInterceptors()
            }
        }
    }

    private fun clearAllInterceptors() {
        interceptors?.clear()
        interceptors = null
    }

    // 构建者模式。
    open class Builder(private val count: Int = 0) {
        private val interceptors by lazy(LazyThreadSafetyMode.NONE) {
            ArrayList<Interceptor>(count)
        }
        private var activity: FragmentActivity? = null
        private var fragment: Fragment? = null

        // 添加一个拦截器。
        fun addInterceptor(interceptor: Interceptor): Builder {
            if (!interceptors.contains(interceptor)) {
                interceptors.add(interceptor)
            }
            return this
        }

        // 关联Fragment。
        fun attach(fragment: Fragment): Builder {
            this.fragment = fragment
            return this
        }

        // 关联Activity。
        fun attach(activity: FragmentActivity): Builder {
            this.activity = activity
            return this
        }


        fun build(): InterceptChain {
            return InterceptChain(activity, fragment, interceptors)
        }
    }
}

二、拦截器模式的使用

三个简单的文件就定义了一个拦截器链,我们看看上面的需求我们如何实现

具体到工作申请判断类

public class JobInterceptBean {

    public boolean isNewMember;
    public boolean isFillInfo;
    public boolean isMemberApprove;
    public boolean isNOCVUpload;
    public boolean isNeedDepost;
    public boolean isNeedFace;
    public boolean isUnderCompany;
    public boolean isNeedWhatApp;
    public boolean isNeedBankInfo;
    public boolean isNeedSkill;

    ...
}

具体的拦截器一,新用户的拦截。这里直接暴力的跳转页面

class InterceptNewMember(private val bean: JobInterceptBean) : BaseInterceptImpl() {

    override fun intercept(chain: InterceptChain) {
        super.intercept(chain)

        if (bean.isNewMember) {
            //拦截
            //可以不弹窗,直接就暴力跳转新页面
            Demo5Activity.startInstance()
        } else {
            //放行- 转交给下一个拦截器
            chain.process()
        }
    }


    //已经完成了培训-放行
    fun resetNewMember() {
        mChain?.process()
    }

}

完善信息的拦截,这里使用弹窗判断

class InterceptFillInfo(private val bean: JobInterceptBean) : BaseInterceptImpl() {

    override fun intercept(chain: InterceptChain) {
        super.intercept(chain)

        if (!bean.isFillInfo) {
            //拦截
            //跳转新页面
            showDialogTips(chain)
        } else {
            //放行- 转交给下一个拦截器
            chain.process()
        }
    }

    private fun showDialogTips(chain: InterceptChain) {
        FangIOSDialog(chain.activity).apply {
            setTitle("完善信息")
            setMessage("你没有完善信息,你要去完善信息")
            setNegativeButton("跳过"){
                dismiss()
                chain.process()
            }
            setPositiveButton("去完善"){
                Demo5Activity.startInstance()
            }
            show()
        }
    }

}

用户状态使用弹窗判断

class InterceptMemberApprove(private val bean: JobInterceptBean) : BaseInterceptImpl() {

    override fun intercept(chain: InterceptChain) {
        super.intercept(chain)

        if (!bean.isMemberApprove) {
            //拦截
            showDialogTips(chain)
        } else {
            //放行- 转交给下一个拦截器
            chain.process()
        }
    }

    private fun showDialogTips(chain: InterceptChain) {
        FangIOSDialog(chain.activity).apply {
            setTitle("状态不对")
            setMessage("你用户状态不对,联系管理员吗?")
            setNegativeButton("跳过") {
                dismiss()
                chain.process()
            }
            setPositiveButton("联系") {
                dismiss()
                toast("去拨打电话,当你状态对了才能继续")
            }
            show()
        }
    }

}

技能的拦截-使用弹窗判断

class InterceptSkill(private val bean: JobInterceptBean) : BaseInterceptImpl() {

    override fun intercept(chain: InterceptChain) {
        super.intercept(chain)

        if (bean.isNeedSkill) {
            //拦截
            //跳转新页面
            showDialogTips(chain)
        } else {
            //放行- 转交给下一个拦截器
            chain.process()
        }
    }

    private fun showDialogTips(chain: InterceptChain) {
        FangIOSDialog(chain.activity).apply {
            setTitle("你没有填写技能")
            setMessage("你要去填写技能吗?")
            setNegativeButton("跳过") {
                dismiss()
                chain.process()
            }
            setPositiveButton("去填写") {
                Demo5Activity.startInstance()
            }
            show()
        }
    }

}

内部可以使用 Dialog 或者 PopupWindow 或者第三方的弹窗库,都是可以的。具体的可以自行实现。下面看看如何使用

    lateinit var newMemberIntercept: InterceptNewMember

        fun navIntercept() {

            //模拟网络请求
            LoadingDialogManager.get().showLoading(this@Demo4Activity)
            CommUtils.getHandler().postDelayed({
                LoadingDialogManager.get().dismissLoading()

                val bean = JobInterceptBean(true, false, false, false, true, true, true, true, true, true)

                createIntercept(bean)
            }, 1500)


        }

        //创建拦截弹窗
        private fun createIntercept(bean: JobInterceptBean) {
            newMemberIntercept = InterceptNewMember(bean)

            val chain = InterceptChain.create(4)
                .attach(this@Demo4Activity)
                .addInterceptor(newMemberIntercept)
                .addInterceptor(InterceptFillInfo(bean))
                .addInterceptor(InterceptMemberApprove(bean))
                .addInterceptor(InterceptSkill(bean))
                .build()

            //开始执行
            chain.process()

        }

我们把 NewMemberIntercept 单独提取出来作为成员变量是为了接受到完成培训的通知,对它做放行操作。


    override fun startObserve() {
        LiveEventBus.get("newMember",Boolean::class.java).observe(this){
            //调用内部放行的方法
            newMemberIntercept.resetNewMember()
        }
    }


看看效果,NewMemberIntercept 放行与不放行的区别

可以看到我们的第二个拦截器 InterceptFillInfo 它内部就没有定义放行的方法,如果一旦跳转到新页面,但是没有接收通知去放行的话就会卡在这里。

效果:

总结

这样就完成了一个拦截器链,我们可以自由的定义弹窗,打电话,跳转页面,跳转浏览器等自定义的功能。

我们的业务场景是调用接口获取到全部的校验数据,然后再创建 Chain 。当然我们也可以先创建 Chain 。然后把每一个拦截器都设置为成员变量,每一个拦截器可以调用不同的接口做校验,当数据返回的时候设置给拦截器暴露的方法,内部再判断数据类型,是否需要放行。

可以说这是一种非常灵活的拦截器链,内部使用了责任链模式,单例模式,建造者模式,工厂模式等。如果大家有需要可以使用起来。

具体的源码在此。如有错漏还请指出,如果有更好的方法也欢迎讨论。

如果觉得不错还请点赞支持哦!

到此完结!

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