开发中可能弹框类使用最多的就是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中需要使用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的外部可以实现窗口不消失。