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

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

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 。然后把每一个拦截器都设置为成员变量,每一个拦截器可以调用不同的接口做校验,当数据返回的时候设置给拦截器暴露的方法,内部再判断数据类型,是否需要放行。

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

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

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

到此完结!

猜你喜欢

转载自juejin.im/post/7108149354342383624