PopupWindow 系统6.0以上 setOutsideTouchable失效分析

来源:互联网 发布:短信猫用什么网络 编辑:程序博客网 时间:2024/06/06 11:02

一、PopupWindow是什么?

开发中可能弹框类使用最多的就是PopupWindow和Dialog,那么PopupWindow是什么呢?根据PopupWindow源码注释解释为:

* <p> * This class represents a popup window that can be used to display an * arbitrary view. The popup window is a floating container that appears on top * of the current activity. * </p>
 可以理解为弹出窗口,可以用来显示任意视图。弹出窗口是依附于当前Activity的。使用PopupWindow控件可以更加灵活的用于减少Activity使用设计的时候,比如游戏SDK开发的时候就尽量减少Activity的使用,再次封装即可做到不错的效果。

二、PopupWindow使用时问题?

PopupWindow原理不再分析,因为业务需求使用PopupWindow创建窗口,如果PopupWindow中需要使用EditText,必须设置:

popWindow.setOutsideTouchable(false);popWindow.setFocusable(true);//设置焦点生效

这样设置焦点可以获取,那么问题出现了,经测试6.0系统之前setOutsideTouchable设置为false 效果生效,但是在6.0之后的系统,就会出现失效。这样就需要看下6.0系统后的PopupWindow;入点查看setOutsideTouchable的原理:

/**     * <p>Controls whether the pop-up will be informed of touch events outside     * of its window.  This only makes sense for pop-ups that are touchable     * but not focusable, which means touches outside of the window will     * be delivered to the window behind.  The default is false.</p>     *     * <p>If the popup is showing, calling this method will take effect only     * the next time the popup is shown or through a manual call to one of     * the {@link #update()} methods.</p>     *     * @param touchable true if the popup should receive outside     * touch events, false otherwise     *     * @see #isOutsideTouchable()     * @see #isShowing()     * @see #update()     */    public void setOutsideTouchable(boolean touchable) {        mOutsideTouchable = touchable;    }

方法的大概意思:触摸窗口外的事件,默认为false,这样理解,设置setOutsideTouchable后应该是有效果的,但是6.0之后系统为什么没有效果:

private int computeFlags(int curFlags) {        curFlags &= ~(                WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |                WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);        if(mIgnoreCheekPress) {            curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;        }        if (!mFocusable) {            curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;            if (mInputMethodMode == INPUT_METHOD_NEEDED) {                curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;            }        } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {            curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;        }        if (!mTouchable) {            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;        }        if (mOutsideTouchable) {            curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;        }        if (!mClippingEnabled || mClipToScreen) {            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;        }        if (isSplitTouchEnabled()) {            curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;        }        if (mLayoutInScreen) {            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;        }        if (mLayoutInsetDecor) {            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;        }        if (mNotTouchModal) {            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;        }        if (mAttachedInDecor) {          curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;        }        return curFlags;    }

根据PopWindow的创建过程,及createPopupLayoutParams中可以确认有个flags,这个里面的flags作用是什么,看上面computeFlags函数源码:首先会先把这几种状态清除,再根据变量去设置,其中有一个WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL:获取焦点的情况下,设置这个属性可以把Window之外的event发送到window后面其他的window上,因此我们可以设置它为true,把窗口外的event传递,根据:

/**     * Set whether this window is touch modal or if outside touches will be sent to     * other windows behind it.     * @hide     */    public void setTouchModal(boolean touchModal) {        mNotTouchModal = !touchModal;    }

可以看出,直接调用不可使用为hide,mNotTouchModal = !touchModal;所以外部设置false即可,可使用反射的办法去解决:

        try {            Method  method = PopupWindow.class.getDeclaredMethod("setTouchModal", boolean.class);            method.setAccessible(true);            method.invoke(popupWindow, false);        } catch (Exception e) {            e.printStackTrace();        }

经测试,6.0系统之后点击PopWindow的外部可以实现窗口不消失。



0 0