Android PopuWindow实现物理返回键监听

【存在问题】

正常情况下,PopuWindow是无法监听到物理返回键事件的,因为PopuWindow的最顶层的PopupDecorView mDecorViewdispatchKeyEvent中将返回键给拦截掉了,我们瞅一瞅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();
    }
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值