setOutsideTouchable
setOutsideTouchable(true),点击非PopupWindow视图区域,直接隐藏PopupWindow。调用setOutsideTouchable(true)会使得点击非PopupWindow视图区域,PopupWindow能够接收到ACTION_OUTSIDE事件,接收到这个事件后执行dismiss。但是在5.0以下需要调用setBackground给PopupWindow设置一个非空的背景才会起作用。
小于5.0分析
5.0以下的PopupWindow,当mBackground为非null时,才会创建PopupViewContainer来封装contentView,而ACTION_OUTSIDE的处理正是由其来处理。点击查看4.4源代码
private void preparePopup(WindowManager.LayoutParams p) {
......
if (mBackground != null) {
......
PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
......
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
}
......
}
private class PopupViewContainer extends FrameLayout {
......
@Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
}
......
}
大于等于5.0分析
和小于5.0分析类似,大于等于5.0的PopupWindow,不管mBackground是否为null,都会创建PopupDecorView来封装contentView。PopupDecorView会处理ACTION_OUTSIDE事件。点击查看5.0源代码
......
private void preparePopup(WindowManager.LayoutParams p) {
......
// When a background is available, we embed the content view within
// another view that owns the background drawable.
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
}
mDecorView = createDecorView(mBackgroundView);
......
}
......
private PopupDecorView createDecorView(View contentView) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
final int height;
if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
height = WRAP_CONTENT;
} else {
height = MATCH_PARENT;
}
final PopupDecorView decorView = new PopupDecorView(mContext);
decorView.addView(contentView, MATCH_PARENT, height);
decorView.setClipChildren(false);
decorView.setClipToPadding(false);
return decorView;
}
......
private class PopupDecorView extends FrameLayout {
......
@Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
}
......
}
setFocusable
调用setFocusable(true),按back键先隐藏PopupWindow而不是整个Activity。setFoucusable(true)将PopupWindow的mFoucusable设置为true。在PopupWindow显示时如果mFoucusable为false,则会将FLAG_NOT_FOCUSABLE设置到Window上,导致系统按键事件不会派发给PopupWindow。mFoucusable默认为false,所以这就是必须调用setFocusable(true)的原因。