背景
由于活动业务越来越多,积累的弹窗越来越多和杂乱,出现如下弹窗交互问题:
-
弹窗无限重叠,影响操作
-
弹出顺序无优先级,重要弹窗被隐藏
原因相信大家都一样,产品是一次次迭代的,也可能是不同人开发的,两个毫不相关的业务,弹窗时机也没有任何关联,重不重叠我怎么控制呀
首先来分析一下我们的预期
经过总结后,我想要的弹窗效果如下:
- 短时间内弹出多个弹窗情况下,一次只能显示一个,其他的都加到队列里等待;
- 下一个弹窗弹出的时机,是在当前弹窗关闭之后;
- 队列中的弹窗不是无限等待的,超时后不能显示(出队列的时间-入队列的时间,超时时间可设置)
- 队列中的弹窗显示按照优先级排列(可设置优先级)
当然,除了满足上面的业务条件外,对当前代码的侵入性,代码可靠性也同样重要,不然改了一顿之后上线都是bug,那这个就得不偿失了;
设计阶段
为了后续开发清晰,我总结了一个流程图
设计DialogManager类:
定义需要的结构
object DialogShowManager {
//弹窗等待队列
private val mQueueList by lazy { LinkedList<ConfigDialogFragment>() }
//优先级计算
private var mComparator = Comparator<ConfigDialogFragment> { e1, e2 -> e2.config.priority - e1.config.priority}
//当前正在显示的dialog
private var mCurrentDialog:ConfigDialogFragment? = null
//....
}
定义显示dialog的入口方法:
fun addDialog(dialog: DialogFragment, fragmentManager: FragmentManager?, flags: Int, config: DialogShowConfig) {
log("addDialog::${dialog.javaClass.simpleName}")
val configDialog = ConfigDialogFragment(dialog, fragmentManager,flags, config.apply { joinTime = System.currentTimeMillis()})
if (mCurrentDialog == null) {
showDialogInternal(configDialog)
return
}
log("addDialog add to queue::${dialog.javaClass.simpleName}")
mQueueList.add(configDialog)
Collections.sort(mQueueList, mComparator)
}
显示dialog,如果当前没有正在显示的弹窗,则直接进入显示弹窗;如果有,则直接加入队列;
显示弹窗内部逻辑:
private fun showDialogInternal(dialogFragment: ConfigDialogFragment) {
log("showDialogInternal::${dialogFragment.dialog.javaClass.simpleName}")
mCurrentDialog = dialogFragment
mCurrentDialog?.dialog?.lifecycle?.addObserver(mDialogObserver)
Navigator.showDialog(dialogFragment.dialog, dialogFragment.fragmentManager, dialogFragment.flags)
}
注意,显示之前,为当前弹窗添加了lifecycleObserver,用来监听弹窗的生命周期;
Navigator.showDialog为内部封装显示dialogFragment的方法;
当前弹窗的生命周期监听:
private val mDialogObserver = object: LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(owner: LifecycleOwner) {
log("mDialogObserver::onCreate")
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy(owner: LifecycleOwner) {
log("mDialogObserver::onDestroy")
mCurrentDialog?.dialog?.lifecycle?.removeObserver(this)
mCurrentDialog = null
showNextDialog()
}
}
因为注册了lifecycleObserver,所以在这里可以监听到弹窗的开启和关闭,在关闭时清空当前弹窗并执行显示下一个弹窗的逻辑
显示下一个弹窗:
private fun showNextDialog() {
log("showNextDialog")
while (true) {
val dialog = mQueueList.poll() ?: return
log("showNextDialog::isTimeout::${dialog.isTimeout()},::${dialog.dialog.javaClass.simpleName}")
if (!dialog.isTimeout()) {
showDialogInternal(dialog)
return
}
continue
}
}
找到队列里下一个未超时的弹窗,并进入执行显示逻辑
这样,业务里弹窗时,只需要调用DialogShowManager.addDialog即可,传入需要显示的dialog和优先级配置等;
此方案的优点是侵入性小,没有复杂的判断逻辑,健壮性好,只使用少量的代码即实现了预想的方案;