I'm trying to present a PopupWindow
from a FloatingActionButton
Since this is part of an SDK intended for others to use, I can't depend on the location or some attributes of the FloatingActionButton
Instead, I have to find an appropriate location near the FAB to show the PopupWindow
I currently have some code which seems to be working fine on Lollipop and newer. When I run it on KitKat, there is some additional spacing between the FAB
and the Popup
. I've managed to eliminate the space by hardcoding some offsets, but that's a really ugly solution (and depends on the elevation
of the FAB
to be identical to how I've configured it, which also isn't a reliable precondition)
Expected appearance (works on Lollipop):
Incorrect appearance on KitKat:
First the code I'm using to present the popup:
public void onShowPopup(View view) {
View layout = getLayoutInflater().inflate(R.layout.popup_menu, null);
PositionedPopupWindow.withLayout(layout).showRelativeToView(
view,
PositionedPopupWindow.AnchorPoint.values()[spinner.getSelectedItemPosition()],
0, // getResources().getDimensionPixelSize(R.dimen.fab_hmargin),
0 // getResources().getDimensionPixelSize(R.dimen.fab_vmargin)
);
}
I should be able to pass 0 to both of the last two parameters and have the popup show adjacent to the FAB. It works fine on Lollipop and later, but on KitKat I have to pass in some magic numbers that depend on the elevation.
Here's the body of showRelativeToView
It's most of the implementation of PositionedPopupWindow
public void showRelativeToView(View parent, AnchorPoint anchorPoint, int hMargin, int vMargin) {
if(anchorPoint == AnchorPoint.Automatic) {
anchorPoint = computeAutomaticAnchorPoint(parent);
}
// Get the size of the popup
getContentView().measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
Point popupSize = new Point(getContentView().getMeasuredWidth(), getContentView().getMeasuredHeight());
int gravity = 0, x = 0, y = 0;
// Horizontally align the popup edge closest to the screen edge with the button edge
switch(anchorPoint) {
case TopLeft:
case BottomLeft:
gravity |= Gravity.LEFT;
x += hMargin;
break;
case TopRight:
case BottomRight:
x = -popupSize.x;
x -= hMargin;
gravity |= Gravity.RIGHT;
break;
}
// Vertically align the popup edge closest to the screen edge with the button edge
switch(anchorPoint) {
case TopLeft:
case TopRight:
y = -popupSize.y - parent.getMeasuredHeight();
y += vMargin;
gravity |= Gravity.TOP;
break;
case BottomLeft:
case BottomRight:
// align top of popover and bottom of button
gravity |= Gravity.BOTTOM;
y -= vMargin;
break;
}
PopupWindowCompat.showAsDropDown(this, parent, x, y, gravity);
}
I've tried to setting the margins of the FAB to 0 within my onCreate
method, as suggested elsewhere to get rid of the extra margin, but it doesn't seem to change anything.
My question is, how can I get rid of the extra spacing between the FAB
and the PopupWindow
without hardcoding magic numbers (which won't work reliably)
The problem seems to arise because KitKat puts an extra margin around FloatingActionButton
to account for the elevation shadow, since this changes the size of the FAB, I can compare the actual button size to the expect size and compute the necessary offset:
public void onShowPopup(View view) {
View layout = getLayoutInflater().inflate(R.layout.popup_menu, null);
int hMargin = 0;
int vMargin = 0;
// Pre-Lollipop the FAB shadow is factored into the button size, KitKat and on the
// shadow isn't part of the button size. By comparing the actual width to the expected
// width we can compute an offset necessary to position the popup adjacent to the FAB
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
hMargin += (int) (view.getWidth() - Util.dpToPixels(this, 56)) / 2;
vMargin += (int) (view.getHeight() - Util.dpToPixels(this, 56)) / 2;
}
PositionedPopupWindow.withLayout(layout).showRelativeToView(
view,
PositionedPopupWindow.AnchorPoint.Automatic,
hMargin,
vMargin
);
}