问题描述:
最近用popupwindow实现了一个筛选的弹窗,项目在调试阶段,在我的一加5上调试是可以正常打开关闭popupwindow的,但是有用户反馈,其vivo手机点击打开弹窗之后,点击其他区域或者是手机返回键,均无效果
问题出现的原因:
根据用户的表述,应该就是popupwindow dismiss出错了,所以,我们就从dismiss这个方法开始追溯。
从这里我们可以看到,阻止dismiss往下调用的就是开头的两个标志位,通过全文搜索发现,这两个标志位,在popupwindow弹出的情况,这里的条件均是不满足的,所以dismiss方法内部不会造成点击外部区域不消失问题。
既然dismiss函数不会阻断弹框消失的效果,那么我们就接着向上追溯,看看哪些地方调用了这个方法。通过搜索发现有个PopupDecorView的类调用到了dismiss方法。
根据bug表述,我们首先锁定dispatchKeyEvent方法和onTouchEvent方法,popupwindow的外部点击消失,主要靠监听触摸事件并加以判断实现的,阅读者两个方法,发现均处于正常操作,并无什么异常的地方,那么我们就只能接着再往上追溯。
从代码中,我们发现这句mDecorView = createDecorView(mBackgroundView),这一句则是构建了DecorView,也就是说,只要调用了preparePopup这个方法,我们的decorView就会被创建,我们的触摸响应事件也是有效的。这也就是我在一加5 Android 8.0的手机上调试都是正常的原因。但是用户的手机为什么会点击外部,窗口会不消失呢?查看了用户的手机配置之后,发现用户使用的是基于Android 5.1的vivo手机,那就查看一下api 22的源码。
dimiss方法我们就不再看了,大同小异,我们接着向上追溯。搜索全文发现,有个地方有用到dismiss,那就是PopupViewContainer类,诶,和我们之前看到的api27 的类不一样耶,但是仔细看看,该类的监听触摸实现部分的实现几乎是一样的。
我们接着向上追溯。
我们再次回到了prepare方法,阅读代码,PopupViewContainer只有在mBackground不为空的时候才会创建,换句话说,如果mBackground为空,那么我们的PopupViewContainer就不会创建,那么触摸事件就会受到影响,那么mBackground什么时候会被赋值呢,全文搜索发现在setBackgroundDrawable()方法内被赋值,而该方法可被外部调用以及内部的构造方法,查看下构造方法。
通过代码发现,默认情况下,构造方法都会调用setBackgroundDrawable(bg)方法为mBackground赋值,所以说,正常情况下,mBackground是不为空的,除非是用户强制设置进去的。但是回查自己的代码,是并没有设置mBackground为空的,那么为什么触摸事件还是会受到影响呢?结合网上查询的结果得出,在Android的某些版本,在构造方法内,是没有调用setBackgroundDrawable方法的,又或者是传递的参数为空。为了避免这个问题,我们最好是直接给PopupWindow设置一个背景以兼容出问题的Android版本。
问题解决方案:
在创建PopupWindow时,给其设置一个不为空的背景。setBackgroundDrawable(bg)