popupwindow的源碼分析

閒來無事把popupwindow的源碼看了一遍,能力有限只看懂了一部分。下面我們來看看源碼:

<span style="font-size:14px;">private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;</span>
分析:從這裏可以看出來PopupWindow(彈出窗口)實際上是一個子窗口,它是一個獨立的類(並不繼承於Window)。
<span style="font-size:14px;">public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        mContext = context;
        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
        final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
        mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
        mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);

        final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
        mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle;

        a.recycle();

        setBackgroundDrawable(bg);
    }
</span>

分析:這個一看就很清楚,這是它的構造函數,裏面通過context對象獲取了windowManager對象和屬性typedArray和一些動畫style,a.recycle是釋放
typedArray。裏面還有很多構造函數,主要是對它進行一些參數的初始化,比如設置contentView、寬、高、焦點等。詳細請看源碼。
接下來主要介紹的是我們經常要用到的顯示popupwindow位置的幾個方法。因爲嚴格來說這個PopuWindow就是用來在指定位置顯示一個View的。主要方法有下面:
	public void showAsDropDown(View anchor) 
      showAsDropDown(View anchor, int xoff, int yoff)
    showAtLocation(View parent, int gravity, int x, int y) 
下面來看看他們的源碼:
public void showAsDropDown(View anchor) {
        showAsDropDown(anchor, 0, 0);
    }
 public void showAsDropDown(View anchor, int xoff, int yoff) {
        showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
    }<pre name="code" class="java">public void showAtLocation(View parent, int gravity, int x, int y) {
        showAtLocation(parent.getWindowToken(), gravity, x, y);
    }


第一個調用的是第二個方法,所以我們直接分析第二個方法:
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());
        preparePopup(p);

        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);
    }
<pre name="code" class="java">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();
       
        preparePopup(p);
        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);
    }

分析:

先解釋下參數:
anchor:就是PopupWindow要顯示位置的參照物
xoff: PopupWindow相對於anchor的左下角x軸方向的偏移大小
yoff: PopupWindow相對於anchor的左下角y軸方向的偏移大小
我覺得這兩個方法的區別就是showAtLocation能夠設置popupwindow顯示在參照物view的哪個方位然後設置偏移值,而showAsDropDown則是默認彈出窗顯示在參照物view的左下角,然後設置偏移值。代碼具體分析:
剛開始isShowing()判斷當前PopupWindow是否顯示,或者mContentView是否爲空,是的話就不用去繼續執行下面的代碼了。然後unregisterForScrollChanged(),爲了滑動改變註冊,就是有可能anchor有滑動,或者ContentView過大,有的地方放不開。
private void unregisterForScrollChanged() {
        WeakReference<View> anchorRef = mAnchor;
        View anchor = null;
        if (anchorRef != null) {
            anchor = anchorRef.get();
        }
        if (anchor != null) {
            ViewTreeObserver vto = anchor.getViewTreeObserver();
            vto.removeOnScrollChangedListener(mOnScrollChangedListener);
        }
        mAnchor = null;
    }
就是註冊一下滑動的全局的監聽,註冊之前先註銷一下之前的註冊,防止ViewTreeObserver失效。在這裏使用了一個弱引用對anchor,防止anchor這個類已經無用了,anchor仍然無法回收內存。接下來爲PopupWindow設置顯示參數,調用了createPopupLayout(anchor.getWindowToken())
看下這個方法:
private WindowManager.LayoutParams createPopupLayout(IBinder token) {
        // generates the layout parameters for the drop down
        // we want a fixed size view located at the bottom left of the anchor
        WindowManager.LayoutParams p = new WindowManager.LayoutParams();<span style="color:#ff0000;">//<span style="font-family: Simsun; line-height: 24px;">創建一個WindowManager.LayoutParams的實例</span></span>
        // 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 = Gravity.START | Gravity.TOP;<span style="color:#ff0000;">//接下來爲實例p設置了一系列的參數</span>
        p.width = mLastWidth = mWidth;
        p.height = mLastHeight = mHeight;
        if (mBackground != null) {
            p.format = mBackground.getOpacity();
        } else {
            p.format = PixelFormat.TRANSLUCENT;
        }
        p.flags = computeFlags(p.flags);
        p.type = mWindowLayoutType;
        p.token = token;
        p.softInputMode = mSoftInputMode;
        p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));

        return p;
    }


接下來分析方法:invokePopup(p),它纔是真正的把PopupWindow顯示在特定的位置上。其實就使用Activity中的WindowManager對象對mPopupView進行了添加顯示。
<span style="font-size:14px;">private void invokePopup(WindowManager.LayoutParams p) {
        if (mContext != null) {
            p.packageName = mContext.getPackageName();
        }
        mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
        setLayoutDirectionFromAnchor();
        mWindowManager.addView(mPopupView, p);//<span style="color: rgb(255, 0, 0); font-family: Arial; line-height: 26px;">調用addView完成添加</span>
    }</span>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章