PopupWindow之了解

本文深入探讨了PopupWindow的工作原理,包括其初始化流程、加载绘制机制及实现点击外部消失的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在分析PopupWindow之前,我们将首先说一下什么是PopupWindow?理解一个类最好的方式就是看一下这个类的定义,这里我们摘要了一下Android系统中PopupWindow的类的说明:

A popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current 
activity.

一个PopupWindow能够被用于展示任意的View,PopupWindow是一个悬浮的容易展示在当前Activity的上面。 
简单来说PopupWindow就是一个悬浮在Activity之上的窗口,可以用展示任意布局文件。

PopupWindow 设置可点击可有两种方法~

1、在构造函数中 super(activity)

2、给PopupWindow 设置一个背景:如:

ColorDrawable dw = new ColorDrawable(0x00000000);
setBackgroundDrawable(dw);

在说明PopupWindow的加载绘制机制之前,我们还是先写一个简单的例子用于说明一下PopupWindow的简单用法。

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">public static View showPopupWindowMenu(Activity mContext, View anchorView, int layoutId) {
        LayoutInflater inflater = (LayoutInflater) mContext<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getSystemService</span>(Context<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.LAYOUT</span>_INFLATER_SERVICE)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        View view = inflater<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inflate</span>(layoutId, null)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow = new PopupWindow(view, DisplayUtil<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dip</span>2px(mContext, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">148</span>), WindowManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.LayoutParams</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRAP</span>_CONTENT)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setBackgroundDrawable</span>(mContext<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getResources</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDrawable</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.drawable</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.menu</span>_bg))<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setFocusable</span>(true)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setOutsideTouchable</span>(true)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>

        int[] location = new int[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>]<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        anchorView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getLocationOnScreen</span>(location)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setAnimationStyle</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.style</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.popwin</span>_anim_style)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.showAtLocation</span>(anchorView, Gravity<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.NO</span>_GRAVITY,
                location[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] - popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getWidth</span>() + anchorView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getWidth</span>() - DisplayUtil<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dip</span>2px(mContext, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">12</span>),
                location[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] + anchorView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getHeight</span>() - DisplayUtil<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dip</span>2px(mContext, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>))<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>

        popupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setOnDismissListener</span>(new PopupWindow<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.OnDismissListener</span>() {
            @Override
            public void onDismiss() {
                popupWindow = null<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            }
        })<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        return view<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>

可以看到我们首先通过LayoutInflater对象将布局文件解析到内存中View对象,然后创建了一个PopupWindow对象,可以看到传递了三个参数,一个是View对象,一个是PopupWindow的宽度和高度。

这里就是PopupWindow的初始化流程的开始了,好吧,我们来看一下PopupWindow的构造方法的实现:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">PopupWindow</span>(View contentView, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> width, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>(contentView, width, height, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

可以看到这里调用了PopupWindow的重载构造方法,好吧,继续看一下这个重载构造方法的实现逻辑:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">PopupWindow</span>(View contentView, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> width, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> focusable) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (contentView != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            mContext = contentView.getContext();
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }

        setContentView(contentView);
        setWidth(width);
        setHeight(height);
        setFocusable(focusable);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

这里首先根据传入的View是否为空做了一下判断,若不为空,则初始化成员变量,Context和mWindowManager,可以发现这里的mContext对象就是传入的View组件中保留的Context对象,这里的mWindowManager是应用进程创建的时候注册的服务本地接口。然后调用了setContentView方法,这里就是为PopupWindow的contentView赋值。然后后面调用的setWidth、setHeight、setFocusable方法都是为PopupWindow的成员变量,width,height,focusable等赋值,这样PopupWindow的构造方法就执行完成了。

我们继续回到我们的例子代码中,在后续的代码中我们调用了:popupWindow.setBackgroundDrawable、popupWindow.setFocusable、PopupWindow.setOutsideTouchable、 
PopupWindow.setAnimationStyle等方法,初始化了PopupWindow中的相关成员变量,最后我们调用了popupWindow.showAtLocation方法用于展示PopupWindow,这里我们具体看一下showAtLocation的实现逻辑:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">showAtLocation</span>(View parent, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> gravity, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> x, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> y) {
        showAtLocation(parent.getWindowToken(), gravity, x, y);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

可以发现,这里调用了showAtLocation的重载函数,这样我们继续看一下这个重载函数的实现方式:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">showAtLocation</span>(IBinder token, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> gravity, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> x, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> y) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (isShowing() || mContentView == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
        }

        TransitionManager.endTransitions(mDecorView);

        unregisterForScrollChanged();

        mIsShowing = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
        mIsDropdown = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> WindowManager.LayoutParams p = createPopupLayoutParams(token);
        preparePopup(p);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Only override the default if some gravity was specified.</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (gravity != Gravity.NO_GRAVITY) {
            p.gravity = gravity;
        }

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

        invokePopup(p);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>

可以看到通过调用createPopupLayoutParams方法创造了WindowManager.LayoutParams对象,然后又调用了preparePopup方法,可以看一下preparePopup方法的具体实现:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">preparePopup</span>(WindowManager.LayoutParams p) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mContentView == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> || mContext == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> || mWindowManager == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalStateException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"You must specify a valid content view by "</span>
                    + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"calling setContentView() before attempting to show the popup."</span>);
        }

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// The old decor view may be transitioning out. Make sure it finishes</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// and cleans up before we try to create another one.</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mDecorView != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            mDecorView.cancelTransitions();
        }

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// When a background is available, we embed the content view within</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// another view that owns the background drawable.</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mBackground != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            mBackgroundView = createBackgroundView(mContentView);
            mBackgroundView.setBackground(mBackground);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            mBackgroundView = mContentView;
        }

        mDecorView = createDecorView(mBackgroundView);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// The background owner should be elevated so that it casts a shadow.</span>
        mBackgroundView.setElevation(mElevation);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// We may wrap that in another view, so we'll need to manually specify</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// the surface insets.</span>
        final <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> surfaceInset = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) Math.ceil(mBackgroundView.getZ() * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>);
        p.surfaceInsets.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">set</span>(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
        p.hasManualSurfaceInsets = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;

        mPopupViewInitialLayoutDirectionInherited =
                (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
        mPopupWidth = p.width;
        mPopupHeight = p.height;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li></ul>

preparePopup方法的参数是WindowManager.LayoutParams,然后设置了PopupWindow中的几个比较重要的成员变量,首先看一下mBackgroundView的初始化过程:

<code class="hljs matlab has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mBackground != null) <span class="hljs-cell" style="box-sizing: border-box;">{
            mBackgroundView = createBackgroundView(mContentView);
            mBackgroundView.setBackground(mBackground);
        }</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-cell" style="box-sizing: border-box;">{
            mBackgroundView = mContentView;
        }</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

可以发现如果我们设置了mBackground变量也就是我们在初始化的时候执行了popupWindow的setBackgound方法,那么我们这里执行的就是if分之,这里看一下createBackgourndView的具体执行逻辑:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> PopupBackgroundView <span class="hljs-title" style="box-sizing: border-box;">createBackgroundView</span>(View contentView) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (layoutParams != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            height = ViewGroup.LayoutParams.WRAP_CONTENT;
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            height = ViewGroup.LayoutParams.MATCH_PARENT;
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> PopupBackgroundView backgroundView = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> PopupBackgroundView(mContext);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> PopupBackgroundView.LayoutParams listParams = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> PopupBackgroundView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, height);
        backgroundView.addView(contentView, listParams);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> backgroundView;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

可以看到,createBackgroundView的执行逻辑就是在参数contentView的外面一层包裹一层PopupBackgroundView,而这里的PopupBackgroundView值我们自定义的FrameLayout的子类,重写了其onCreateDrawableState方法。

继续回到我们的preparePopup方法,这里我们又调用了createDecorView方法初始化mDectorView变量,我们可以看一下createDecorView的具体实现:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> PopupDecorView <span class="hljs-title" style="box-sizing: border-box;">createDecorView</span>(View contentView) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (layoutParams != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            height = ViewGroup.LayoutParams.WRAP_CONTENT;
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            height = ViewGroup.LayoutParams.MATCH_PARENT;
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> PopupDecorView decorView = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> PopupDecorView(mContext);
        decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
        decorView.setClipChildren(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>);
        decorView.setClipToPadding(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> decorView;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

可以发现这里也是给参数contentView外面包裹了一层PopupDecorView,这里的PopupDecorView也是我们自定义的FrameLayout的子类,PopupDecorView的源码比较多,这里就不都贴出来了,这里具体看一下其onTouchEvent方法的实现:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">@Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box;">onTouchEvent</span>(MotionEvent <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>) {
            final <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> x = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>.getX();
            final <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> y = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>.getY();

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>.getAction() == MotionEvent.ACTION_DOWN)
                    && ((x < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) || (x >= getWidth()) || (y < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) || (y >= getHeight()))) {
                dismiss();
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>.getAction() == MotionEvent.ACTION_OUTSIDE) {
                dismiss();
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> super.onTouchEvent(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">event</span>);
            }
        }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

可以发现其重写了onTouchEvent时间,这样我们在点击popupWindow外面的时候就会执行pupopWindow的dismiss方法,取消PopupWindow。

好吧,继续回到我们的showAsDropDown方法,在执行完成preparePopup方法之后又调用了invokePopup方法,这里的方法应该就是具体执行PopupWindow的加载与显示逻辑了。这里我们具体看一下其实现逻辑:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">invokePopup</span>(WindowManager.LayoutParams p) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mContext != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            p.packageName = mContext.getPackageName();
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> PopupDecorView decorView = mDecorView;
        decorView.setFitsSystemWindows(mLayoutInsetDecor);

        setLayoutDirectionFromAnchor();

        mWindowManager.addView(decorView, p);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mEnterTransition != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            decorView.requestEnterTransition(mEnterTransition);
        }
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

我们看到这里我们调用了mWindowManager.addView方法,看过我们前面几篇关于Dialog和Activity的加载与现实流程的同学应该知道这里的addView其实是我们布局绘制的流程,这里的mWindowManager是我们在调用PopupWIndow的构造函数的时候初始化的,其调用的是:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mWindowManager == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && mContentView != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

而这里的mContext.getSystemService是一个接口其具体的实现是在ContextImpl中实现的,所以这里我们看一下ContextImpl的getSystemService的实现:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Object <span class="hljs-title" style="box-sizing: border-box;">getSystemService</span>(String name) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> SystemServiceRegistry.getSystemService(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, name);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

好吧,在ContextImpl中的getSystemService方法又调用了SystemServiceRegister中的静态方法getSystemService,这样我们再看看一下在SystemServiceRegister是如何实现的。

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Object <span class="hljs-title" style="box-sizing: border-box;">getSystemService</span>(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(name);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> fetcher != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> ? fetcher.getService(ctx) : <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

这里发现服务对象的获取就是通过一个SYSTEM_SERVICE_FETCHERS的map数据结构获取的,那么这个map对象的数据是何时填充的呢?通过查看源码我们发下在SystemServiceRegister中有一段静态代码主要用于注册本地服务接口,其中关于windowManagerService本地服务的代码如下:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">registerService(Context.WINDOW_SERVICE, WindowManager.class,
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> CachedServiceFetcher<WindowManager>() {
            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> WindowManager <span class="hljs-title" style="box-sizing: border-box;">createService</span>(ContextImpl ctx) {
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> WindowManagerImpl(ctx.getDisplay());
            }});</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

好吧,原来我们通过mContext.getSystemService获取的WindowManager其实际上是一个WindowManagerImpl对象,而我们调用的addView就是WindowManagerImpl的addView方法。

这样就回到了我们前几篇讲解的内容上了,通过调用WindowManagerImpl实现了布局文件的绘制流程。。。。

好了,经过上面的一系列的操作我们分析完了PopupWindow的加载绘制流程,其和Dialog,Activity的加载绘制流程类似,都是通过Window对象控制布局文件的加载与绘制流程。

总结:

  • PopupWindow的界面加载绘制流程也是通过Window对象实现的;

  • PopupWindow内部保存的mWindowManager对象通过ContextImpl中获取,并且取得的是WindowManagerImpl对象;

  • PopupWindow通过为传入的View添加一层包裹的布局,并重写该布局的点击事件,实现点击PopupWindow之外的区域PopupWindow消失的效果;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值