美文网首页
android的PopupWindow无法收到KeyEvent.

android的PopupWindow无法收到KeyEvent.

作者: HWilliamgo | 来源:发表于2019-11-26 20:17 被阅读0次

    PopupWindow无法收到KeyEvent.KEYCODE_BACK的原因:

    在创建的顶层PopupDecorViewdispatchKeyEvent()回调中,把该事件拦截了。

    1. PopupDecorView创建PopupDecorView

    代码:

    //PopupWindow#showAtLocation()
    public void showAtLocation(IBinder token, int gravity, int x, int y) {
        if (isShowing() || mContentView == null) {
            return;
        }
        TransitionManager.endTransitions(mDecorView);
        detachFromAnchor();
        mIsShowing = true;
        mIsDropdown = false;
        mGravity = gravity;
        final WindowManager.LayoutParams p = createPopupLayoutParams(token);
        //创建顶层PopupDecorView
        preparePopup(p);
        p.x = x;
        p.y = y;
        //加入到WindowManger显示出来
        invokePopup(p);
    }
    
    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 (p.accessibilityTitle == null) {
            p.accessibilityTitle = mContext.getString(R.string.popup_window_default_title);
        }
        // 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) {
            //如果background不为空,把ContentView外封一层View,并给外封的View设置backgroud。
            mBackgroundView = createBackgroundView(mContentView);
            mBackgroundView.setBackground(mBackground);
        } else {
            mBackgroundView = mContentView;
        }
        //创建PopupDecorView
        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);
    }
    
    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);
        //加入我们设置的ContentView
        decorView.addView(contentView, MATCH_PARENT, height);
        decorView.setClipChildren(false);
        decorView.setClipToPadding(false);
        return decorView;
    }
    

    2. PopupDecorView#dispatchKeyEvent

    看下PopupDecorView:

    private class PopupDecorView extends FrameLayout{
        @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);
                }
                //返回true
                return true;
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                final KeyEvent.DispatcherState state = getKeyDispatcherState();
                if (state != null && state.isTracking(event) && !event.isCanceled()) {
                    dismiss();
                    //返回true
                    return true;
                }
            }
            return super.dispatchKeyEvent(event);
        } else {
            return super.dispatchKeyEvent(event);
        }
        }
      //...
    }
    

    可以看到,PopupDecorView本身并没有预留接口来给开发者去接管KEYCODE_BACK事件,遇到这个事件他一定会自己处理掉,所以你只能通过OnDismissListener接口和额外的flag变量来实现监控用户在PopupWindow上按下返回按钮。

    相关文章

      网友评论

          本文标题:android的PopupWindow无法收到KeyEvent.

          本文链接:https://www.haomeiwen.com/subject/spiywctx.html