I have a PopupWindow which opens after I click an ImageButton:
// Get the [x, y]-location of the ImageButton
int[] loc = new int[2];
myImageButton.getLocationOnScreen(loc);
// Inflate the tag_popup.xml
LinearLayout viewGroup = (LinearLayout)findViewById(R.id.tagPopupLayout);
LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View layout = layoutInflater.inflate(R.layout.tag_popup, viewGroup);
// Create the PopupWindow
myPopupWindow = new PopupWindow(ChecklistActivity.this);
myPopupWindow.setContentView(layout);
myPopupWindow.setWindowLayoutMode(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
myPopupWindow.setFocusable(true);
myPopupWindow.setOutsideTouchable(false);
// Clear the default translucent background and use a white background instead
myPopupWindow.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.WHITE));
// Set the content of the TextViews, EditTexts and Buttons of the PopupWindow
setPopupContent(...);
// Displaying the Pop-up at the specified location
myPopupWindow.showAtLocation(layout, Gravity.NO_GRAVITY, 0, loc[1]);
because of the Gravity.NO_GRAVITY
, the PopupWindow will be displayed within the borders of the Window. Everything works as intended on my Emulator, but when I run it on my Nexus 7 Tablet, it is partly covered by the Device's bottom status bar.
How can I fix this? Should I somehow get the current PopupWindow's location after the Gravity.NO_GRAVITY
took place, then change the y-location to add the Device's Statusbar's height, and then re-draw it? (Will try this, but I think that having the right location to start with instead of re-drawing it is a better solution..)
This is what I came up with:
What we have:
oldY
)What we calculate:
screenHeight - statusBarHeight - popupHeight
)What we then check:
oldY
is larger than the maxY
newY
will be the maxY
and we re-draw the PopupWindow. If this isn't the case it means we do nothing and just use the oldY
as the correct Y-postition.NOTE 1: I made the code for this, but during debugging it turned out the Status Bar's Height is 0 on both my Emulator and my Nexus Tablet, so just using the screenHeight - popupHeight
was enough for me. Still, I included the code to calculate the Bottom Status Bar Height with a boolean in my Config-file to enable/disable this, in case the app is installed on another tablet in the future.
Here it is in code, I just added the description above to make it clear which approach I used to tackle this problem:
// Get the [x, y]-location of the ImageButton
int[] loc = new int[2];
myImageButton.getLocationOnScreen(loc);
// Inflate the popup.xml
LinearLayout viewGroup = (LinearLayout)findViewById(R.id.popup_layout);
LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View layout = layoutInflater.inflate(R.layout.popup, viewGroup);
// Create the PopupWindow
myPopupWindow = new PopupWindow(ChecklistActivity.this);
myPopupWindow.setContentView(layout);
myPopupWindow.setWindowLayoutMode(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
... // Some more stuff with the PopupWindow's content
// Clear the default translucent background and use a white background instead
myPopupWindow.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.WHITE));
// Displaying the Pop-up at the specified location
myPopupWindow.showAtLocation(layout, Gravity.NO_GRAVITY, 0, loc[1]);
// Because the PopupWindow is displayed below the Status Bar on some Device's,
// we recalculate it's height:
// Wait until the PopupWindow is done loading by using an OnGlobalLayoutListener:
final int[] finalLoc = loc;
if(layout.getViewTreeObserver().isAlive()){
layout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// This will be called once the layout is finished, prior to displaying it
// So we can change the y-position of the PopupWindow just before that
@Override
public void onGlobalLayout() {
// Get the PopupWindow's height
int popupHeight = layout.getHeight();
// Get the Status Bar's height
int statusBarHeight = 0;
// Enable/Disable this in the Config-file
// This isn't needed for the Emulator, nor the Nexus 7 tablet
// Since the calculated Status Bar Height is 0 with both of them
// and the PopupWindow is displayed at its correct position
if(D.WITH_STATUS_BAR_CHECK){
// Check whether the Status bar is at the top or bottom
Rect r = new Rect();
Window w = ChecklistActivity.this.getWindow();
w.getDecorView().getWindowVisibleDisplayFrame(r);
int barHeightCheck = r.top;
// If the barHeightCheck is 0, it means our Status Bar is
// displayed at the bottom and we need to get it's height
// (If the Status Bar is displayed at the top, we use 0 as Status Bar Height)
if(barHeightCheck == 0){
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
}
// Get the Screen's height:
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenHeight = dm.heightPixels;
// Get the old Y-position
int oldY = finalLoc[1];
// Get the max Y-position to be within Window boundaries
int maxY = screenHeight - statusBarHeight - popupHeight;
// Check if the old Y-position is outside the Window boundary
if(oldY > maxY){
// If it is, use the max Y-position as new Y-position,
// and re-draw the PopupWindow
myPopupWindow.dismiss();
myPopupWindow.showAtLocation(layout, Gravity.NO_GRAVITY, 0, maxY);
}
// Since we don't want onGlobalLayout to continue forever, we remove the Listener here again
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
NOTE 2: I've set the tag_popup
itself to width = match_parent; height = wrap_content
on this line:
myPopupWindow.setWindowLayoutMode(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
and the main layout of this Popup to width = match_parent; height = match_parent
:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xml>
<!-- The DOCTYPE above is added to get rid of the following warning:
"No grammar constraints (DTD or XML schema) detected for the document." -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/popup_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@layout/tag_shape"
android:padding="@dimen/default_margin">
... <!-- Popup's Content (EditTexts, Spinner, TextViews, Button, etc.) -->
</RelativeLayout>
NOTE 3: My app is forced to stay in Portrait mode. I haven't test this in Landscape mode, but I assume some modifications should be made (not sure though). EDIT: Tested and it also works in Landscape mode on my two devices. I don't know if this also works in Landscape Mode with the Bottom Bar Height enabled.
Took me some time, but it works now. Hopefully they will fix PopupWindow's Gravity
in the future, so it will never be below a Status bar, unless the programmer wants this themselves and change the PopupWindow's settings. Makes things a lot easier..