PopupWindow显示NavigationBar又出现

 

由于业务需要将底部的NavigationBar给隐藏了,期间用不获取焦点的方式处理了dialog弹出navigationBar又会出现的问题,本以为万事ok了,谁曾想,换popupWindow弹出,navigationBar又出现了。

第一想法,按dialog的套路来处理,结果刚下手就发现,两实现方式不同,popupWindow是用windowManager来添加view的,不能用这个套路来玩。

那就换一种,获取到DecorView,去设置它的flags(网上基本上的解决方案都是用这种)

public static void setFullScreenWindowLayout(Window window, boolean withNavigationBar) {

		int flags = withNavigationBar ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE :
				View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
						| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
		window.getDecorView().setSystemUiVisibility(flags);
		window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
			window.setStatusBarColor(Color.TRANSPARENT);
		}
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
			//设置页面全屏显示
			WindowManager.LayoutParams lp = window.getAttributes();
			lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
			//设置页面延伸到刘海区显示
			window.setAttributes(lp);
		}
	}

结果,navigationBar一会出现一会隐藏,不光闪还不按预期出牌,这个方案out。

为什么设置DecorView的flags没有出现预期效果,网上的方案不行,那只能自己动手丰衣足食了。

看下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);
        preparePopup(p);

        p.x = x;
        p.y = y;

        invokePopup(p);
    }

很简单的一个实现,看方法名也差不多猜到与createPopupLayoutParams(token)有关,接着往下跟 

protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
        final WindowManager.LayoutParams p = new WindowManager.LayoutParams();

        // These gravity settings put the view at the top left corner of the
        // screen. The view is then positioned to the appropriate location by
        // setting the x and y offsets to match the anchor's bottom-left
        // corner.
        p.gravity = computeGravity();
        p.flags = computeFlags(p.flags);
        p.type = mWindowLayoutType;
        p.token = token;
        p.softInputMode = mSoftInputMode;
        p.windowAnimations = computeAnimationResource();

        if (mBackground != null) {
            p.format = mBackground.getOpacity();
        } else {
            p.format = PixelFormat.TRANSLUCENT;
        }

        if (mHeightMode < 0) {
            p.height = mLastHeight = mHeightMode;
        } else {
            p.height = mLastHeight = mHeight;
        }

        if (mWidthMode < 0) {
            p.width = mLastWidth = mWidthMode;
        } else {
            p.width = mLastWidth = mWidth;
        }

        p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
                | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;

        // Used for debugging.
        p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));

        return p;
    }

嗯。。看到computeFlags(p.flags),我们应该离结果不远了

 private int computeFlags(int curFlags) {
        curFlags &= ~(
                WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
                WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
        if(mIgnoreCheekPress) {
            curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
        }
        if (!mFocusable) {
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            if (mInputMethodMode == INPUT_METHOD_NEEDED) {
                curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
            }
        } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) {
            curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        }
        if (!mTouchable) {
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        }
        if (mOutsideTouchable) {
            curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        }
        if (!mClippingEnabled || mClipToScreen) {
            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
        }
        if (isSplitTouchEnabled()) {
            curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
        }
        if (mLayoutInScreen) {
            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        }
        if (mLayoutInsetDecor) {
            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
        }
        if (mNotTouchModal) {
            curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        }
        if (mAttachedInDecor) {
            curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
        }
        return curFlags;
    }

果然,看成到了熟悉的flags设置。理论上来说,变更这个flags就能实现我们想要的结果了,实践下理论,

嗯。。涉及到flags的方法,还是private就是final的,这问题先放一放,我们自己用WindowManager设置flags来添加contentView,。

实践结果,完美达到预期。

接下来就是怎么优雅的处理了,按刚测试的自己来add? 那还有popupWindow什么事,这方法太low。重写popupWindow?前边就有提供,涉及到flags的,不是private就是final,该如何去干涉add过程?

popupWindow无从下手,那我们就转眼看看WindowManager,最终的view是通过WindowManager添加上的。WindowManager是个接口,WindowManagerImpl是具体的,我们要干涉addView。

这么一分析,其实解决方案也就呼之欲出了,很典型的,可以使用代理来拦截addView,所以我们的最终解决方案

 try {
            final WindowManager window = (WindowManager) getContentView().getContext().getSystemService(Context.WINDOW_SERVICE);
            WindowManager mWindowManager = (WindowManager) Proxy.newProxyInstance(window.getClass().getClassLoader(),
                    window.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      
                            if ("addView".equalsIgnoreCase(method.getName())) {
                                WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1];
                                layoutParams.flags = (
                                        WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
                                                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                                                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
                                                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                                                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
                                                WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                                );
                                args[1] = layoutParams;
                            }
                            return method.invoke(window, args);
                        }
                    });

            Field field = PopupWindow.class.getDeclaredField("mWindowManager");
            field.setAccessible(true);
            field.set(this, mWindowManager);
        } catch (Exception e) {
            e.printStackTrace();
        }

只要重写popupWindow,在各种show之前添加这串代理设置,就可以完美解决popupWindow弹出又把navigationBar给显示出来的问题了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值