我們?cè)陂_發(fā)中經(jīng)常使用到PopUpWindow,有時(shí)我們需要在彈出popupwindow之后帝牡,點(diǎn)擊手機(jī)的返回按鍵,在popupwindow dismiss之前做一些其他的事情蒙揣,比如動(dòng)畫效果靶溜,以下為標(biāo)準(zhǔn)寫法
View view= LayoutInflater.from(MainActivity.this).inflate(R.layout.pop_layout,null);
view.setFocusable(true);
view.setFocusableInTouchMode(true);
final PopupWindow popupWindow=new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setFocusable(true);
popupWindow.showAtLocation(view, Gravity.BOTTOM,0,0);
view.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
//do something
popupWindow.dismiss();
return true;
}
return false;
}
});
這代碼在6.0以下系統(tǒng)沒有問題,在6.0以上系統(tǒng)就出現(xiàn)了攔截不到手機(jī)返回按鍵事件的問題了懒震,onKey不會(huì)執(zhí)行罩息。查看源碼,我發(fā)現(xiàn)了問題所在个扰,他們都會(huì)執(zhí)行preparePopup方法瓷炮,而preparePopup有所區(qū)別, 以下為Android 5.0 popwindow的preparePopup方法源碼
private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
}
if (mBackground != null) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int height = ViewGroup.LayoutParams.MATCH_PARENT;
if (layoutParams != null &&
layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
// when a background is available, we embed the content view
// within another view that owns the background drawable
PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height
);
popupViewContainer.setBackground(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
}
mPopupView.setElevation(mElevation);
mPopupViewInitialLayoutDirectionInherited =
(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
我們沒有設(shè)置backgroud所以mBackground為null ,執(zhí)行 mPopupView = mContentView代碼递宅,以下是6.0部分源碼
private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
}
// The old decor view may be transitioning out. Make sure it finishes
// and cleans up before we try to create another one.
if (mDecorView != null) {
mDecorView.cancelTransitions();
}
// 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);
// The background owner should be elevated so that it casts a shadow.
mBackgroundView.setElevation(mElevation);
// We may wrap that in another view, so we'll need to manually specify
// the surface insets.
p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}
我們發(fā)現(xiàn)這里無論有沒有設(shè)置background娘香,都會(huì)執(zhí)行createDecorView方法,我們?cè)倏纯碿reateDecorView方法源碼
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;
}
這個(gè)方法對(duì)contentView進(jìn)行了包裝办龄,我們?cè)倏纯碢opupDecorView源碼
private class PopupDecorView extends FrameLayout {
/** Runnable used to clean up listeners after exit transition. */
private Runnable mCleanupAfterExit;
public PopupDecorView(Context context) {
super(context);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (getKeyDispatcherState() == null) {
return super.dispatchKeyEvent(event);
}
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
dismiss();
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}
}
問題就在這烘绽,這里的PopupDecorView是FrameLayout級(jí)別的,這里設(shè)置了dispatchKeyEvent俐填,并且 return true安接,消費(fèi)了點(diǎn)擊事件,所以不會(huì)分發(fā)給我們?cè)O(shè)置的onkeyListner玷禽,因此我們?cè)O(shè)置監(jiān)聽無效,目前沒有找到好的解決辦法呀打,但好在不影響功能矢赁。