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

来源:互联网 发布:怎样注册中文.手机域名 编辑:程序博客网 时间:2024/06/07 17:51
很早之前有遇到过一个问题,点击打开链接就是 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 0