持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
不知道大家是否有看懂这个标题,就是当前界面中弹出一个
AlertDialog,dismiss掉然后弹出另外一个AlertDialog,这两个弹窗切换期间不允许任何触摸事件传递到下层的当前界面,进行响应。
可能我这样说大家还是不明白,接下来我们通过一个例子举例说明:
class DialogActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityDialogBinding
private var mDialog: Dialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityDialogBinding.inflate(layoutInflater)
setContentView(mBinding.root)
mBinding.root.setOnClickListener {
Toast.makeText(this, "你点击了界面", Toast.LENGTH_SHORT).show()
}
mBinding.root.postDelayed(10 * 1000) {
showDialog()
}
}
private fun showDialog() {
mDialog = AlertDialog.Builder(this)
.setTitle("dialog1")
.setMessage("这里是Dialog1")
.setPositiveButton("confirm") { _, _ ->
showDialog2()
}
.create()
mDialog?.show()
}
private fun showDialog2() {
mDialog = AlertDialog.Builder(this)
.setTitle("dialog2")
.setMessage("这里是Dialog2")
.setPositiveButton("confirm2") { _, _ ->
showDialog()
}
.create()
mDialog?.show()
}
}
先解释下代码逻辑:
-
首先给界面根布局设置一个点击事件,并弹出toast提示"你点击了界面";
-
接着通过
postDelayed()方法延迟10s调用方法showDialog()弹出第一个弹窗,当点击弹窗确认按钮后,调用showDialog2()方法弹出第二个弹窗,同时dimiss掉第一个弹窗; -
第二个弹窗的确认按钮点击后,就会调用
showDialog()方法弹出第一个弹窗,也就是说在通过弹窗的确认按钮,在第一个弹窗和第二个弹窗之间不断的切换;
开头所讲的问题就是,在第一个和第二个弹窗切换之间,不允许点击事件传递到下面的界面并触发点击事件弹出toast,上面的代码这样写是会触发的,截图如下 :
不多比比,下面给出解决方案。
当界面弹出dialog弹窗时,我们可以给界面的
根布局添加一个透明的View,在dialog切换之间拦截触摸事件分发到下面界面中响应点击事件。
具体的代码如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
mBinding.root.postDelayed(10 * 1000) {
mBinding.root.addView(View(this).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
//设置界面背景透明
setBackgroundColor(Color.TRANSPARENT)
//消费点击事件,避免直接传递到界面布局中
setOnClickListener {
}
})
showDialog()
}
}
在通过postDelay()延迟弹出弹窗时,先给根布局添加一个透明的View拦截事件向下分发,请注意,这里有个非常非常关键的点:
主动调用了setOnClickListener {}去消费了点击事件.
如果没有这一行代码,那么你这个拦截将是无效的。为什么呢,因为根据View事件分发机制,它是遍历子View集合去分发触摸事件,如果你这个额外添加的透明
View没有消费点击事件,那么事件分发会将触摸事件分发到另一个子View中(即弹窗下面的界面布局)消费,这就直接导致了继续响应弹出toast的根布局点击事件。
还有一个需要注意的地方,在合适的位置移除透明View: 当界面需要完全关闭弹窗时,记得将这个额外添加的透明View从根布局中移除.
这个地方也有一个小技巧:
我们可以给这个添加的View指定一个id,当从根布局移除这个透明
View时,直接通过这个id去获取这个透明View,如果存在就直接从根布局进行移除。
- 首先在xml中定义一个id:
<resources>
<item name="globe_view" type="id" />
</resources>
- 然后向根布局添加透明
View指定为这个id:
mBinding.root.postDelayed(10 * 1000) {
mBinding.root.addView(View(this).apply {
//...
//消费点击事件,避免直接传递到界面布局中
setOnClickListener {
}
//指明id
id = R.id.globe_view
})
showDialog()
}
- 当需要移除透明
View,直接通过id获取并移除:
private fun removeMaskView() {
mBinding.root.findViewById<View>(R.id.globe_view)?.let {
mBinding.root.removeView(it)
}
}
总结
本篇文章主要是讲解了在界面弹窗切换间 ,拦截触摸事件分发到下层界面响应的解决方法 :
主要是通过添加一个
透明View实现拦截,当需要移除该透明View时,可以考虑通过View Id的方式获取View并从根布局中进行移除。