【存在问题】
正常情况下,PopuWindow是无法监听到物理返回键事件的,因为PopuWindow的最顶层的PopupDecorView mDecorView
在dispatchKeyEvent
中将返回键给拦截掉了,我们瞅一瞅PupoWindow的这段源码:
@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);
}
}
正因为如此,即使我们像网上所说的这样设置,也是无效的:
mPopupWindow.getContentView().setFocusable(true);
mPopupWindow.getContentView().setFocusableInTouchMode(true);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopupWindow.getContentView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.i("min77","监听到返回键");
return true;
}
});
【问题分析】
从PopuWindow的源码可以知道,以下三种情况,会调用到dismiss
方法:
1、当设置setFocusable(true)
、setOutsideTouchable(false)
,点击PopuWindow外部会调用dismiss方法
2、当用户按下物理返回键的时候,也会调用到dismiss方法
3、代码自己调用了dismiss方法
去关闭PupoWindow
【问题解决】
基于上述分析,我们可以自定义一个CustomPopupWindow
继承 PopupWindow,并重写dismiss()
方法,在里面使用new Exception().getStackTrace()
或者StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
方法获取到调用栈
1)假如存在dispatchKeyEvent
,那么就是用户按了物理返回键
2)假如存在onTouchEvent
,那么就是用户点击了PopupWindow外部
3)剩下的就是代码自身调用的dismiss
下面这段代码就是只处理物理返回键事件而已,其他事件都是默认关闭PopupWindow
public class CustomPopupWindow extends PopupWindow {
private OnBackPressListener mOnBackPressListener;
public CustomPopupWindow(View contentView, int width, int height, boolean focusable) {
super(contentView, width, height, focusable);
}
public CustomPopupWindow(Context context) {
super(context);
}
public CustomPopupWindow() {
}
public CustomPopupWindow(View contentView) {
super(contentView);
}
public CustomPopupWindow(int width, int height) {
super(width, height);
}
public CustomPopupWindow(View contentView, int width, int height) {
super(contentView, width, height);
}
@Override
public void dismiss() {
StackTraceElement[] stackTrace = new Exception().getStackTrace();
// for (int i = 0; i < stackTrace.length; i++) {
// Logger.i("key = " + stackTrace[i]);
// }
if (stackTrace.length >= 2 && "dispatchKeyEvent".equals(stackTrace[1].getMethodName())) {
//按了返回键
if(mOnBackPressListener != null){
mOnBackPressListener.onBack();
}else {
dismiss2();
}
} else {
//点击外部或者点击关闭调用了dismiss,直接让弹窗消失
dismiss2();
}
}
//让弹窗消失
private void dismiss2() {
super.dismiss();
}
public OnBackPressListener getOnBackPressListener() {
return mOnBackPressListener;
}
public void setOnBackPressListener(OnBackPressListener onBackPressListener) {
this.mOnBackPressListener = onBackPressListener;
}
public interface OnBackPressListener{
void onBack();
}
}