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);