从源码剖析PopupWindow 兼容Android 6.0以上版本点击外部不消失

前言

PopupWindow可以说是Google坑最多的一个控件,使用PopupWindow的时候没有遇到几个坑你都不好意思说你用过它,说一个可能大多数人都遇到过的一个坑:那就是我们想触摸PopupWindow 以外区域就隐藏PopupWindow,理论上我们只需要调用 setOutsideTouchable(ture)设置为ture就可以了,但是实际上只设置这个属性是不行的,必须设置背景,也就是说要和setBackgroundDrawable(Drawable background)同时使用才有效,不然,点击PopupWindow以外区域是不能隐藏掉的。

当时遇到这个坑的时候也是一脸懵逼,设不设背景跟我点击外面消失有啥关系?看了源码才知道,它是根据mBackground这个值来判断的,如果没设置这个值,那么就不会走到dispatchEvent 方法,就处理不了dismiss事件。在Android 6.0 以上,Google源码进行了更改,去掉了mBackground是否为null 的这个判断条件,并且在构造方法中初始化了mBackground这个值,因此在Android 6.0以上,不用调

setBackgroundDrawable(Drawable background)

这个方法,就可以dismiss 了。那么本篇文章将从源码的角度,分析Android 6.0以上和Android 6.0 以下,如何控制点击外部PopupWindow消失/不消失。

1 . 为何Android 6.0 以下要设置BackgroundDrawable 才能dismiss

这个问题在上面已经描述,在Android 6.0 以前,我们显示出来的PopupWindow,在只设置setOutsideTouchable(ture)的情况下,触摸PopupWindow以外区域是不能dismiss掉的(6.0以后已经可以了)。必须同时设置BackgroundDrawable,才能dismiss掉,以前可能我们找到了解决办法,我们就没有管造成它的原因,那么今天就一起看一下源码为什么会这样。从显示PopupWindow的方法为入口,源码分析如下(源码为API 21 版本):

在showAsDropDown()方法 中调用了一个preparePopup(p)方法,我们看一下这个方法中做了什么,如下:

注意这个方法中,有一个判断条件是mBackground != null,在里面包装了一个PopupViewContainer,我在再去看一下这个PopupViewContainer又干了什么,如下:(部分源码)

PopupViewContainer 其实就处理了PopupWindow的事件分发,在onTouch方法里面,如果点击PopupWindow之外的区域,先dismiss,然后消费掉了事件。

重点就在这儿了,前面在preparePopup方法中,判断了,只有当mBackground不为null,才包装了PopupViewContainer,处理了事件,在点击 popupWindow外部的时候,会dismiss。而mBackground这个值只有在setBackgroundDrawable()这一个地方初始化的,因此必须调用setBackgroundDrawable方法设置了mBackground不为null,才能点击PopupWindow外部关闭PopupWindow。这就解释了为何Android 6.0 以下要设置BackgroundDrawable 才能dismiss

2 . 点击PopupWindow以外区域不让其消失

在我们使用PopupWindow的时候,我们可能有这样一种需求:点击PopupWindow以外的区域,不让其消失(只能通过返回键和PopupWindow中的其他事件来DisMiss),但也不能响应页面的其他事件,也就是模态,像AlertDialog一样,只有当PopupWindow消失之后才能响应其他事件。

开始做这个需求的时候想得很简单:

想到了2种方法:

1,设置setOutsideTouchable(false),测试过后,这种方法无效。

2,既然上面说了mBackground 这个属性为null的时候,点击popupWindow以外区域是取消不了的,那么直接调用setBackgroundDrawable(null)不就行了?这种方式在Android 6.0以下是取消不了,但是,页面的其他事件可以响应,也就是说没有关闭弹出的 PopupWindow的情况下,还可以响应页面其他事件。这当然不是我们想要的效果。如下图:
popupWindow不消失但相应其他事件

上面是我开始想到2种方式,测试过后都不行,那么我们就得找其他方法。

2.1 Android6.0以下 点击PopupWindow以外区域不让其消失

试了一下上面两种方式都不行之后,于是就找其他方法,第一时间进行了Google,嘿,还真找到了一种方法,代码如下:

 LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View contentview = inflater.inflate(R.layout.pop_layout1, null);
        final PopupWindow popupWindow = new PopupWindow(contentview, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        //popupWindow
        popupWindow.setFocusable(true);
        popupWindow.setOutsideTouchable(
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值