public PopupWindow (Context context) public PopupWindow(View contentView) public PopupWindow(int width, int height) public PopupWindow(View contentView, int width, int height) public PopupWindow(View contentView, int width, int height, boolean focusable)
showAsDropDown(View anchor):相對某個控件的位置(正左下方),無偏移 showAsDropDown(View anchor, int xoff, int yoff):相對某個控件的位置,有偏移 showAtLocation(View parent, int gravity, int x, int y):相對於父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),能夠設置偏移或無偏移
具體以下所示php
//建立對象 PopupWindow popupWindow = new PopupWindow(this); View inflate = LayoutInflater.from(this).inflate(R.layout.view_pop_custom, null); //設置view佈局 popupWindow.setContentView(inflate); popupWindow.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); popupWindow.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); //設置動畫的方法 popupWindow.setAnimationStyle(R.style.BottomDialog); //設置PopUpWindow的焦點,設置爲true以後,PopupWindow內容區域,才能夠響應點擊事件 popupWindow.setTouchable(true); //設置背景透明 popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000)); //點擊空白處的時候讓PopupWindow消失 popupWindow.setOutsideTouchable(true); // true時,點擊返回鍵先消失 PopupWindow // 可是設置爲true時setOutsideTouchable,setTouchable方法就失效了(點擊外部不消失,內容區域也不響應事件) // false時PopupWindow不處理返回鍵,默認是false popupWindow.setFocusable(false); //設置dismiss事件 popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { } }); boolean showing = popupWindow.isShowing(); if (!showing){ //show,而且能夠設置位置 popupWindow.showAsDropDown(mTv1); }
先看問題代碼,下面這個不會出現彈窗,思考:爲何?java
PopupWindow popupWindow = new PopupWindow(this); View inflate = LayoutInflater.from(this).inflate(R.layout.view_pop_custom, null); popupWindow.setContentView(inflate); popupWindow.setAnimationStyle(R.style.BottomDialog); popupWindow.showAsDropDown(mTv1);
首先先來看看源碼android
能夠看出,先判斷是否show,若是沒有showing的話,則進行contentView賦值,若是mWindowManager爲null,則取獲取mWindowManager,這個很重要。最後即是根據SDK版本而不是在構造函數中設置附加InDecor的默認設置,由於構造函數中可能沒有上下文對象。咱們只想在這裏設置默認,若是應用程序還沒有設置附加InDecor。git
public void setContentView(View contentView) { //判斷是否show,若是已經show,則返回 if (isShowing()) { return; } //賦值 mContentView = contentView; if (mContext == null && mContentView != null) { mContext = mContentView.getContext(); } if (mWindowManager == null && mContentView != null) { mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); } //在這裏根據SDK版本而不是在構造函數中設置附加InDecor的默認設置,由於構造函數中可能沒有上下文對象。咱們只想在這裏設置默認,若是應用程序還沒有設置附加InDecor。 if (mContext != null && !mAttachedInDecorSet) { setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP_MR1); }
}github
public void setAttachedInDecor(boolean enabled) { mAttachedInDecor = enabled; mAttachedInDecorSet = true; }
先來看一下showAsDropDown(View anchor)部分代碼面試
public void showAsDropDown(View anchor) { showAsDropDown(anchor, 0, 0); }
//主要看這個方法
//注意啦:關於更多內容,能夠參考個人博客大彙總:https://github.com/yangchong211/YCBlogs
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
if (isShowing() || mContentView == null) {
return;
}編程
TransitionManager.endTransitions(mDecorView); //下面單獨講 //https://github.com/yangchong211/YCBlogs attachToAnchor(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; //經過createPopupLayoutParams方法建立和初始化LayoutParams final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken()); preparePopup(p); final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, p.width, p.height, gravity); updateAboveAnchor(aboveAnchor); p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1; invokePopup(p);
}segmentfault
接着來看看attachToAnchor(anchor, xoff, yoff, gravity)源碼markdown
關於四種引用的深刻介紹能夠參考個人這邊文章:01.四種引用比較與源碼分析app
private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) { detachFromAnchor(); final ViewTreeObserver vto = anchor.getViewTreeObserver(); if (vto != null) { vto.addOnScrollChangedListener(mOnScrollChangedListener); } final View anchorRoot = anchor.getRootView(); anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); mAnchor = new WeakReference<>(anchor); mAnchorRoot = new WeakReference<>(anchorRoot); mIsAnchorRootAttached = anchorRoot.isAttachedToWindow(); mAnchorXoff = xoff; mAnchorYoff = yoff; mAnchoredGravity = gravity; }
接着看看dismissImmediate(View decorView, ViewGroup contentHolder, View contentView)源碼
第三步,講mDecorView,mBackgroundView置爲null
private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) { // If this method gets called and the decor view doesn't have a parent, // then it was either never added or was already removed. That should // never happen, but it's worth checking to avoid potential crashes. if (decorView.getParent() != null) { mWindowManager.removeViewImmediate(decorView); } if (contentHolder != null) { contentHolder.removeView(contentView); } // This needs to stay until after all transitions have ended since we // need the reference to cancel transitions in preparePopup(). mDecorView = null; mBackgroundView = null; mIsTransitioningToDismiss = false; }
經過createDecorView(View contentView)方法能夠知道,是PopupDecorView直接new出來的佈局對象decorView,外面包裹了一層PopupDecorView,這裏的PopupDecorView也是咱們自定義的FrameLayout的子類,而後看一下里面的代碼:
private class PopupDecorView extends FrameLayout { private TransitionListenerAdapter mPendingExitListener; public PopupDecorView(Context context) { super(context); } @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()))) { dismiss(); return true; } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { dismiss(); return true; } else { return super.onTouchEvent(event); } } }
new CustomPopupWindow.PopupWindowBuilder(this) //.setView(R.layout.pop_layout) .setView(contentView) .setFocusable(true) //彈出popWindow時,背景是否變暗 .enableBackgroundDark(true) //控制亮度 .setBgDarkAlpha(0.7f) .setOutsideTouchable(true) .setAnimationStyle(R.style.popWindowStyle) .setOnDissmissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { //對話框銷燬時 } }) .create() .showAsDropDown(tv6,0,10);