问题
在以前,我们一直使用showAsDropDown 来让PopupWindow 显示在某个anchor的下边,哪怕这个PopupWindow高度是match_parent的,可24以后,你会发现,它不再是显示在anchor下边,而是铺满整个屏幕的,把anchor都盖住了.
简单的使用
val popup=PopupWindow(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT).apply {
contentView=LayoutInflater.from(baseContext).inflate(R.layout.popup_test,null)
this.isTouchable=true
this.isFocusable=true;
}
popup.showAsDropDown(tg)
我们分析下7.0和6.0的源码有啥区别
6.0的源码
public void showAtLocation(IBinder token, int gravity, int x, int y) {
if (isShowing() || mContentView == null) {
return;
}
TransitionManager.endTransitions(mDecorView);
unregisterForScrollChanged();
mIsShowing = true;
mIsDropdown = false;
final LayoutParams p = createPopupLayoutParams(token);
preparePopup(p);
// Only override the default if some gravity was specified.
if (gravity != Gravity.NO_GRAVITY) {
p.gravity = gravity;
}
p.x = x;
p.y = y;
invokePopup(p);
}
7.0的源码
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
if (isShowing() || !hasContentView()) {
return;
}
TransitionManager.endTransitions(mDecorView);
attachToAnchor(anchor, xoff, yoff, gravity);
mIsShowing = true;
mIsDropdown = true;
final WindowManager.LayoutParams p =
createPopupLayoutParams(anchor.getApplicationWindowToken());
preparePopup(p);
final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
p.width, p.height, gravity, mAllowScrollingAnchorParent);
updateAboveAnchor(aboveAnchor);
p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
invokePopup(p);
}
可以看到7.0的多了两行代码,看名字aboveAnchor,它会判断是否显示在anchor上
final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
p.width, p.height, gravity, mAllowScrollingAnchorParent);
updateAboveAnchor(aboveAnchor);
在分析这两行代码之前,先看下默认生成个的params里的高度是啥
进入createPopupLayoutParams方法
mHeightMode :默认是0,如果你没设置的话,我们一般都没设置这个,所以会走else
而mHeight,我们在构造方法里设置的是match_parent也就是-1
protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
if (mHeightMode < 0) {
p.height = mLastHeight = mHeightMode;
} else {
p.height = mLastHeight = mHeight;
}
}
然后继续看findDropDownPosition
protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
//...
final Rect displayFrame = new Rect();
appRootView.getWindowVisibleDisplayFrame(displayFrame);
//...可以看到,高度就是整个容器的高度,差不多就是屏幕的高度了
if (height == MATCH_PARENT) {
height = displayFrame.bottom - displayFrame.top;
}
//...下边会判断我们这个高度是否会超出屏幕,如果超出屏幕的话,会返回false
// 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);
}
//...下边会处理false的情况的,会都params的y进行处理的,处理结果就是,y的位置不再是anchor下边了,因为 Popup高度是屏幕高度了,所以y差不多就是0了.具体就不分析了
// If the popup still doesn't fit, attempt to scroll the parent.
if (!fitsVertical || !fitsHorizontal) {
}
总结
看完源码就发现了,24以上,match_parent的话,Popup的高度就是全屏高度了,所以,如果还想显示在anchor下边,那就只能用wrap_content,或者 自己计算下anchor底部到屏幕底部的距离,也就是Popup需要展示的高度,自己手动setHeight
计算anchor或者容器的位置大小也很简单,下边这种就行
final Rect displayFrame = new Rect();
appRootView.getWindowVisibleDisplayFrame(displayFrame);