PopupWindow点击外部区域消失(二)

很早之前有遇到过一个问题, 点击打开链接就是 PopupWindow 在使用的时候必须要如下设置才可以在点击外部让它消失
<pre name="code" class="html"> pop.setBackgroundDrawable(new BitmapDrawable());
 pop.setOutsideTouchable(true);
 pop.showAtLocation();

 

就是说在设置位置与是否点击消失之前必须要先setBackgroundDrawable();

之前也没有深究过此间缘由,正好今天有时间就追了一下PopupWindow的源码看了下,原来事情是这样的……

我们来看这段代码:

    public void showAtLocation(IBinder token, int gravity, int x, int y) {
        if (isShowing() || mContentView == null) {
            return;
        }

        unregisterForScrollChanged();

        mIsShowing = true;
        mIsDropdown = false;

        WindowManager.LayoutParams p = createPopupLayout(token);
        p.windowAnimations = computeAnimationResource();
       
        <span style="color:#cc0000;">preparePopup(p);</span>
        if (gravity == Gravity.NO_GRAVITY) {
            gravity = Gravity.TOP | Gravity.START;
        }
        p.gravity = gravity;
        p.x = x;
        p.y = y;
        if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
        if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
        invokePopup(p);
    }
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        if (isShowing() || mContentView == null) {
            return;
        }

        registerForScrollChanged(anchor, xoff, yoff, gravity);

        mIsShowing = true;
        mIsDropdown = true;

        WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
        <span style="color:#cc0000;">preparePopup(p);</span>

        updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));

        if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
        if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;

        p.windowAnimations = computeAnimationResource();

        invokePopup(p);
    }
可以看到无乱你调用哪个显示方法都会进入一个 perparePopup(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.");
        }

        <span style="color:#cc0000;">if (mBackground != null)</span> {
            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
            <span style="color:#cc0000;">PopupViewContainer</span> popupViewContainer = new PopupViewContainer(mContext);
            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, height
            );
            popupViewContainer.setBackground(mBackground);
            popupViewContainer.addView(mContentView, listParams);

            mPopupView = popupViewContainer;
        } <span style="color:#cc0000;">else</span> {
            mPopupView = mContentView;
        }

        mPopupView.setElevation(mElevation);
        mPopupViewInitialLayoutDirectionInherited =
                (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
        mPopupWidth = p.width;
        mPopupHeight = p.height;
    }
相信看到这里大家应该能看出点端倪来了吧,是的,它有个内部类 PopupViewContainer ,当 mBackground != null 的时候
mPopupView = popupViewContainer;
而这个类继承于 FrameLayout ,那么下面再接着看这个类又做了什么事情使得必须要设置 mBackground !

 @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) {
                    KeyEvent.DispatcherState state = getKeyDispatcherState();
                    if (state != null) {
                        state.startTracking(event, this);
                    }
                    return true;
                } else if (event.getAction() == KeyEvent.ACTION_UP) {
                    KeyEvent.DispatcherState state = getKeyDispatcherState();
                    if (state != null && state.isTracking(event) && !event.isCanceled()) {
                        <span style="color:#cc0000;">dismiss();</span>
                        return true;
                    }
                }
                return super.dispatchKeyEvent(event);
            } else {
                return super.dispatchKeyEvent(event);
            }
        }

@Override
        public boolean onTouchEvent(MotionEvent event) {
            final int x = (int) event.getX();
            final int y = (int) event.getY();
            
            if ((event.getAction() == MotionEvent.ACTION_DOWN)
                    && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                <span style="color:#cc0000;">dismiss();</span>
                return true;
            } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                <span style="color:#cc0000;">dismiss();</span>
                return true;
            } else {
                return super.onTouchEvent(event);
            }
        }
原因找到了!!它是处理了触摸事件,当点击在view外部的时候都调用了 dismiss() 方法。
而当 mBackgroud == null 的时候  
mPopupView = mContentView;
而 mContentView 只是一个 View 的实例

private View mContentView;


既然原因找到了,那么我们当然能把他给破解了哈。

由于篇幅原因,接下来会再续前言,而如果只想知道原理 的话那么就可以到此结束了。。
代码下载地址

PopupWindow点击外部区域消失(三)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值