Preface:

As for the drop-down selection box, you probably have a lot to choose from. I wrote about the drop-down selection box in the previous article: Discussion of Project Requirements -HyBrid Model Requirements Modification, which happens to use a Spinner.

This time there is a need for a drop-down box, so THIS time I use PopupWindow to implement it. Then I realized that PopupWindow can be used in many places, but it has not been properly summarized, so I thought of writing this article, and this article is very basic and simple, everyone is easy to understand.

It is mainly divided into three parts:

  1. The use of PopupWindow
  2. PopupWindow encapsulation of the utility class
  3. PopupWindow source code analysis

The body of the

We know that very few people will continue to read the source code directly, so we will write a drop-down selection box demo to demonstrate.

So we can first look at the drop-down box style we need: (ugly for an example) :

We can take a step-by-step look at how to do this:

1. Basic usage tutorial

Since you want to pop out of the following box, and this article said to use PopupWindow, so just implement a PopupWindow, very simple.

1.1 Instantiating the PopupWindow object

Now that the PopupWindow object is instantiated, let’s look at its constructor:

public PopupWindow() {
    this(null, 0, 0);
}

public PopupWindow(View contentView) {
    this(contentView, 0, 0);
}

public PopupWindow(int width, int height) {
    this(null, width, height);
}

public PopupWindow(View contentView, int width, int height) {
    this(contentView, width, height, false);
}



/**
    @param contentView the popup content
    @param width the popup's width @param height the popup's height
    @param focusable true if the popup can be focused, false otherwise
*/

public PopupWindow(View contentView, int width, int height, boolean focusable) {
    if(contentView ! = null) { mContext = contentView.getContext(); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); }setContentView(contentView);
    setWidth(width);
    setHeight(height);
    setFocusable(focusable);
}

Copy the code

(PopupWindow(View contentView, int width, int height, Boolean focusable))

That is, we tell PopupWindow something like this:

  1. According to the contentView
  2. PopupWindow to display the width and height,
  3. Whether PopupWindow has the ability to get focus (default false).

Suppose we use the fourth constructor

View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout, null);
PopupWindow popupWindow = new PopupWindow(contentView,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,true);
Copy the code

1.2 PopupWindow setting methods

Of course, we can also use the first constructor to generate the object and then set various parameters with the corresponding SetXXXX method.

Let’s look at some common Set methods:

Set contentView, width and height, get focus ability:

popupWindow.setContentView(contentView);
popupWindow.setHeight(height);
popupWindow.setWidth(width);
popupWindow.setFocusable(true);
Copy the code

Click out of form to disappear:

/ / need to set up the PopupWindow background, click away works outside PopupWindow. SetBackgroundDrawable (new BitmapDrawable (getResources (), (Bitmap) null)); Popupwindow.settouchable (popupwindow.settouchable)true);
popupWindow.setOutsideTouchable(true);
Copy the code

Windows will be blocked by software disk:

/ / set the pop keyboard top up, rather than keep out popupWindow. SetSoftInputMode (popupWindow. INPUT_METHOD_NEEDED); popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);Copy the code

Popupwindow adds various animation effects (panning, scaling, transparency, etc.):

popupWindow.setAnimationStyle(R.style.popwindow_anim_style);
Copy the code

Animation style:

<style name="AnimDown" parent="@android:style/Animation">
    <item name="android:windowEnterAnimation">@anim/push_scale_in</item>
    <item name="android:windowExitAnimation">@anim/push_scale_out</item>
</style>
Copy the code

Specific animation:

<! -- Show animation --> <? xml version="1.0" encoding="utf-8"? > <! -- top left enlarged --> <set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="0.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toXScale="1.0"
        android:toYScale="1.0" />
</set>
Copy the code
<! -- Hide animation --> <? xml version="1.0" encoding="utf-8"? > <! -- top left enlarged --> <set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toXScale="1.0"
        android:toYScale="0.001" />
</set>
Copy the code

1.3 PopupWindow is displayed

The showXXXX method is used to implement this, and there are several methods:

ShowAsDropDown and showAtLocation ShowAsDropDown is relative to a control, and PopupWindow is displayed below the control. The showAtLocation is relative to the screen, and you can set Gravity to specify where the PopupWindow should be displayed on the screen.

For example, showAsDropDown:

//PopupWindow will show the bottom of the View we passed in, and the tangent is aligned to the left.Copy the code

ShowAsDropDown (View,int,int); //PopupWindow (View,int,int); //PopupWindow (View,int,int);Copy the code

ShowAsDropDown (View,50,50); Both the X and Y axes are offset by 50.

//PopupWindow can optionally set Gravity. Gravity.Left by default. // Setting Top and Bottom at the same time does not work because it is below the View. showAsDropDown(View,int,int,int);Copy the code

Such as the code we write is: the popupWindow. ShowAsDropDown (v, 0, 0, Gravity. RIGHT); Now the bottom right corner of the View is aligned with the top left corner of the PopupWindow.

ShowAtLocation: Since this method is PopupWindow relative to the screen, the View that is passed in is just that screen, because the View is passed in just to get the Window Token.

Public void showAtLocation(view parent) public void showAtLocation(view parent, view parent) int gravity, int x, int y) { showAtLocation(parent.getWindowToken(), gravity, x, y); } public void showAtLocation(IBinder token, int gravity, int x, int y){ ....... }Copy the code

Such as we write the code is: popupWindow showAtLocation (view, Gravity RIGHT | Gravity. BOTTOM, 0, 0).

If we set to: popupWindow showAtLocation (view, Gravity. TOP, 0, 0).

We notice that PopupWindow is not on top of statusBar. If we want to cover statusbar, can add 1: popupWindow. SetClippingEnabled (false);

So I think you know how to use it. Let’s summarize the code:


1.4 Summary of PopupWindow primary use code

LayoutInflater mLayoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); ViewGroup view = (ViewGroup) mLayOutInflater.inflate (r.layout.window, null,true);
PopupWindow popupWindow = new PopupWindow(view, LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, true); / / whether you need to click the PopupWindow external interface disappeared mPopWindow. When other setBackgroundDrawable (new BitmapDrawable ()); mPopWindow.setOutsideTouchable(true); // Set touchable and focusable mpopwindow.setfocusable (true);
mPopWindow.setTouchable(true); /** Then display PopupWindow in a button click event. Remember, you can't call PopupWindow directly in a button click event like onCreate, */ btn.setOnclickListener(v -> {if (popupWindow != null) {
        popupWindow.showAsDropDown(v);
    }
})
Copy the code

2.PopupWindow tool class encapsulation

I’ve written about Dialog encapsulation before:

Project requirements discussion -Android custom Dialog implementation steps and packaging

We’ll wrap PopupWindow this time, using Builder mode as we did in the previous article.

Let’s start by looking at what factors we should consider:

  1. ContentView, there are two possibilities: the user just passed r.layout. XXX in, or the user passed in a specific View object.
  2. Width and height of PopupWindow. (You may need to pass in a Px value, it may be dp, it may be r.dian.xxx, if not, it will default to Wrap_Content, which will show the width and height of the contentView you passed in.)
  3. Whether animation is required, and if so, the specific style parameter
  4. Focusable, touchable Settings
  5. Whether to make PopupWindow disappear by clicking outside
  6. Set the click event for a View inside

So preliminarily we can write it like this:

public class CustomPopupWindow extends PopupWindow {

    private CustomPopupWindow(Builder builder) {
        super(builder.context);

        builder.view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        setContentView(builder.view);
        setHeight(builder.height == 0? ViewGroup.LayoutParams.WRAP_CONTENT:builder.height);setWidth(builder.width == 0? ViewGroup.LayoutParams.WRAP_CONTENT:builder.width);if (builder.cancelTouchout) {
            setBackgroundDrawable(new ColorDrawable(0x00000000)); // Set the transparent backgroundsetOutsideTouchable(builder.cancelTouchout); // Set outside to click}setFocusable(builder.isFocusable);
        setTouchable(builder.isTouchable);

        if(builder.animStyle ! = 0) {setAnimationStyle(builder.animStyle);
        }
    }

    public static final class Builder {

        private Context context;
        private int height, width;
        private boolean cancelTouchout;
        private boolean isFocusable = true;
        private boolean isTouchable = true;
        private View view;
        private int animStyle;

        public Builder(Context context) {
            this.context = context;
        }

        public Builder view(int resView) {
            view = LayoutInflater.from(context).inflate(resView, null);
            return this;
        }

        public Builder view(View resVew){
            view = resVew;
            return this;
        }

        public Builder heightpx(int val) {
            height = val;
            return this;
        }

        public Builder widthpx(int val) {
            width = val;
            return this;
        }

        public Builder heightdp(int val) {
            height = dip2px(context, val);
            return this;
        }

        public Builder widthdp(int val) {
            width = dip2px(context, val);
            return this;
        }

        public Builder heightDimenRes(int dimenRes) {
            height = context.getResources().getDimensionPixelOffset(dimenRes);
            return this;
        }

        public Builder widthDimenRes(int dimenRes) {
            width = context.getResources().getDimensionPixelOffset(dimenRes);
            return this;
        }

        public Builder cancelTouchout(boolean val) {
            cancelTouchout = val;
            return this;
        }

        public Builder isFocusable(boolean val) {
            isFocusable = val;
            return this;
        }

        public Builder isTouchable(boolean val) {
            isTouchable = val;
            return this;
        }

        public Builder animStyle(int val){
            animStyle = val;
            return this;
        }

        public Builder addViewOnclick(int viewRes, View.OnClickListener listener) {
            view.findViewById(viewRes).setOnClickListener(listener);
            return this;
        }


        public CustomPopupWindow build() {

            return new CustomPopupWindow(this);
        }
    }
    
    @Override
    public int getWidth() {
        return getContentView().getMeasuredWidth();
    }
    
    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return(int) (dipValue * scale + 0.5f); }}Copy the code

So as long as we know what properties we want to set, it’s easy to encapsulate.

Then use:

customPopupWindow = new CustomPopupWindow.Builder(this)
                .cancelTouchout(true)
                .view(popupWindowView)
                .isFocusable(true)
                .animStyle(R.style.AnimDown)
                .build();
Copy the code

Here I would like to mention two additional points from the encapsulated class code above:

1. Know the width and height of popupWindow in advance.

We can see that in our utility class, we have a piece of code:

builder.view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); .

I’m just going to draw the contentView that we passed in ahead of time, So we can call popupwindow. GetContentView () getMeasuredWidth () method to get the contentView wide high (ps: we usually set the popupwindow wide high contentv must be handed in with us Iew is consistent).

Some may wonder why we need to know the width and height of popupWindow in advance, such as the following requirement:

For example, the PopupWindow button above is wider than the PopupWindow button below. We definitely want our PopupWindow to be displayed in the middle, so we call:

showAsDropDown(View anchor, int xoff, int yoff);
Copy the code

The offset of the X value passed in is half the width of the top button minus the width of the bottom PopupWindow. But in normal cases, we simply use popupwindow.getwidth () or contentView.getwidth () to get the values -2 and 0 on the first click and then the correct values on the second click. Since the PopupWindow is not on the screen before the first click, it has not been drawn, and the width is not accurate. After appearing once, the second click will get it correctly. So the first time PopupWindow is in the wrong place, right after that.

So we reload PopupWindow’s getWidth method:

@Override
public int getWidth() {
    return getContentView().getMeasuredWidth();
}
Copy the code

Touchable and Focusable Settings

We usually set the top button like this:

btn.setOnclickListener(v -> {
    if (popupWindow != null) {
        popupWindow.showAsDropDown(v);
    }
})
Copy the code

This will bring up our PopupWindow when you click the button, but when you click the button again, PopupWindow will disappear and then reappear like this:

But what we want is to click on the button, and if PopupWindow is there, it disappears.

Of course you can use popupwindow.isshowing () in the click event; Judge, then make popupwindow.dismiss (); So we set PopupWindow’s touchable and focusable to true by default, so we don’t need to change anything about our click event. You can click appear, click disappear.


3 PopupWindow source simple analysis

Very ashamed, a long time ago will use PopupWindow, but the source code has not been read.

Before explaining the PopupWindow source code, let’s take a look at some other things.

We’ve all done or seen the introduction of floating Windows or Windows Manager in articles such as The Android Art Development Journey, which has a chapter devoted to this. Check it out:

Exploring the art of Android Development — Chapter 8: Understanding Windows and Windows Manager

Suppose we now wanted to add a button somewhere in the application:

Button btn = new Button(this);
btn.setText("I am the window"); WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); WindowManager.LayoutParams layout = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT , WindowManager. LayoutParams. WRAP_CONTENT, 0, 0, PixelFormat. TRANSLUCENT); layout.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; layout.gravity = Gravity.CENTER; layout.type = WindowManager.LayoutParams.TYPE_APPLICATION; layout.x = 300; layout.y = 100; wm.addView(btn, layout);Copy the code

All you have to do is add this button through the addView method in Windows Manager, and I think 80 or 90 percent of android developers have probably seen or know how to add this through Windows Manager.

We can see the following steps:

  1. Create the ContentView to display (Button)
  2. Create WindowMananger. LayoutParams object
  3. Set the corresponding property values for LayoutParams objects, such as x,y
  4. The WindowMananger object calls addView(ContentView,LayoutParams);

PS: here’s an extra layout way. Type = WindowManager. LayoutParams. TYPE_APPLICATION; This property, for example, we’re just adding a button to our app right now, so we don’t need to do anything extra; If we want to add buttons globally, that is, our app is minimized to the background, we can still see a button floating on the mobile desktop (similar to some floating balls such as mobile cleaning assistant), we need to switch the Type attribute here and declare the corresponding permissions, otherwise the app will report an error. Say Permission denied for this window type. The corresponding type, you can reference WindowManager. LayoutParams type attribute

Yes, our PopupWindow is similar.

We start by looking at the constructor:

public PopupWindow(View contentView, int width, int height, boolean focusable) {
    if(contentView ! = null) { mContext = contentView.getContext(); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); }setContentView(contentView);
    setWidth(width);
    setHeight(height);
    setFocusable(focusable);
}
Copy the code

We get the WindowManager object and assign the contentView, width, height, and focusable values inside the PopupWindow.

We look at the last display of the method source:

public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
    if (isShowing() || mContentView == null) {
        return;
    }

    TransitionManager.endTransitions(mDecorView);

    attachToAnchor(anchor, xoff, yoff, gravity);

    mIsShowing = true;
    mIsDropdown = true;
    
    //'we can see here. Sure enough to generate the corresponding WindowManager LayoutParams'
    final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
    
    //'Pass in this LayoutParams, create what PopupWindow really looks like, which is the view.'
    preparePopup(p);

    //'findDropDownPosition method determines where PopupWindow should be displayed. 'final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, p.width, p.height, gravity); updateAboveAnchor(aboveAnchor); p.accessibilityIdOfAnchor = (anchor ! = null) ? anchor.getAccessibilityViewId() : -1; //'Finally call windowManager.addView method to render popupWindow'
    invokePopup(p);
}
Copy the code

Step 1: create a WindowManager. LayoutParams

We can see that create WindowManager. LayoutParams is through code final WindowManager. LayoutParams p = createPopupLayoutParams(anchor.getWindowToken()); Let’s look at this method in detail

private 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 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;
}
Copy the code

Step 2: Create the View

Let’s do preparePopup(p); Methods:

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."); } // 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. /**'Prepare backgroundView, because normally mBackgroundView is null, So I'm going to call the contentView that I set up before the setContentView as an mBackgroundView, or else I'm going to generate a PopupBackgroundView, and I'm going to add the contentView, And then I'm going to set the background for this PopupBackgroundView '* /if(mBackground ! = null) { mBackgroundView = createBackgroundView(mContentView); mBackgroundView.setBackground(mBackground); }else{ mBackgroundView = mContentView; } / * *'generates the corresponding PopupWindow root View. Instantiate a PopupDecorView(inheriting FrameLayout) and add the contentView. DecorView = contentView ();
    */
    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 will need to manually specify
    // the surface insets.
    p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);

    mPopupViewInitialLayoutDirectionInherited =
            (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}
Copy the code

Step 3: WindowManager. LayoutParams according to our View of reference to determine the specific attribute values

Mainly through the following method in the source code:

findDropDownPosition(anchor, p, xoff, yoff,p.width, p.height, gravity);
Copy the code

Because we may make PopupWindow in we click the button below, so we will be introduced to the View button, we know that we let PopupWindow appeared on the button below, certainly need to set the WindowManager. LayoutParams x, y, In order for it to appear in the specified position, so we’re definitely going to take its x and y value based on the View of the button, and then we’re going to add the x and y offset that we passed in later, and then we’re going to display it.

We view the content of the source code in detail:

private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
        int xOffset, int yOffset, int width, int height, int gravity) {
    final int anchorHeight = anchor.getHeight();
    final int anchorWidth = anchor.getWidth();
    if(mOverlapAnchor) { yOffset -= anchorHeight; } // Initially, align to the bottom-left corner of the anchor plus offsets. final int[] drawingLocation = mTmpDrawingLocation; / * *'We can see that the getLocationInWindow method is called to get the absolute coordinates of the current window of our reference View. The resulting values are array: location[0] -----> x coordinates location[1] -----> Y coordinates'
    */
    anchor.getLocationInWindow(drawingLocation);
    //'Our PopupWindow x is the x value of the current reference View plus the extra offset value that we passed in'
    outParams.x = drawingLocation[0] + xOffset;
    //'Our PopupWindow y is the current reference View's Y value plus the height of our reference View and the extra offset passed in'
    outParams.y = drawingLocation[1] + anchorHeight + yOffset;

    final Rect displayFrame = new Rect();
    anchor.getWindowVisibleDisplayFrame(displayFrame);
    if (width == MATCH_PARENT) {
        width = displayFrame.right - displayFrame.left;
    }
    if (height == MATCH_PARENT) {
        height = displayFrame.bottom - displayFrame.top;
    }

    // Let the window manager know to align the top to y.
    outParams.gravity = computeGravity();
    outParams.width = width;
    outParams.height = height;

    // If we need to adjust for gravity RIGHT, align to the bottom-right
    // corner of the anchor (still accounting foroffsets). final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK; / * *'If it's Gravity.RIGHT, our x value will have to be offset again by subtracting (our PopupWindow width minus the reference View width). '* /if (hgrav == Gravity.RIGHT) {
        outParams.x -= width - anchorWidth;
    }

    final int[] screenLocation = mTmpScreenLocation;
    anchor.getLocationOnScreen(screenLocation);

    // First, attempt to fit the popup vertically without resizing.
    final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
            anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
            displayFrame.bottom, false);

    // Next, attempt to fit the popup horizontally without resizing.
    final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
            anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
            displayFrame.right, false);

    // If the popup still doesn not fit, attempt to scroll the parent.
    if(! fitsVertical || ! fitsHorizontal) { final int scrollX = anchor.getScrollX(); final int scrollY = anchor.getScrollY(); final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset, scrollY + height + anchorHeight + yOffset);if (mAllowScrollingAnchorParent && anchor.requestRectangleOnScreen(r, true)) {
            // Reset for the new anchor position.
            anchor.getLocationInWindow(drawingLocation);
            outParams.x = drawingLocation[0] + xOffset;
            outParams.y = drawingLocation[1] + anchorHeight + yOffset;

            // Preserve the gravity adjustment.
            if (hgrav == Gravity.RIGHT) {
                outParams.x -= width - anchorWidth;
            }
        }

        // Try to fit the popup again and allowing resizing.
        tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
                screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
        tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
                screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
    }

    // Return whether the popup top edge is above the anchor top edge.
    return outParams.y < drawingLocation[1];
}
Copy the code

Step 3: WindowManager adds the corresponding View

By the final invokePopup(p);

private void invokePopup(WindowManager.LayoutParams p) {
    if(mContext ! = null) { p.packageName = mContext.getPackageName(); } final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor);setLayoutDirectionFromAnchor();
    
    //'Finally add a decorView through the AddView method of windowManager'
    mWindowManager.addView(decorView, p);

    if (mEnterTransition != null) {
        decorView.requestEnterTransition(mEnterTransition);
    }
}
Copy the code

Complement 1: Windowmanager. removeView or removeViewImmediate And our PopupWindow. Dismiss () method is the same, using the mWindowManager. RemoveViewImmediate (decorView); Remove. I’m not going to go over this. You can see for yourself.

ShowAsDropDown = showAsDropDown = showAsDropDown = showAsDropDown = showAsDropDown = showAsDropDown

Add 3: Our previous mentioned in the onCreate method directly display ShowAsDropDown etc method complains: android. The WindowManager $BadTokenException, because by this time the Activity related to the view is not initialized, The view.token is null.

conclusion

The PopupWindow summary may not be complete enough, or there is something wrong, please point out.