What's the exact behavior of PopupWindow.showAtLocation?

I have an android android 4.0 phone and a 4.4 tablet, say B, both have a software navigation bar. I am using this:

showAtLocation(myView, Gravity.NO_GRAVITY, x, y);

      

to show a window at a specific location. Actual result: It looks ok on A, but has an offset y of B. I find that the offset seems to have the same height as the navigation bar B. So I use the following code to get the height and do the subtraction:

private int getNavigationBarHeight(Resources res, Context context) {
        final int apiLevel = Build.VERSION.SDK_INT;
        if((apiLevel >= Build.VERSION_CODES.HONEYCOMB && apiLevel <= Build.VERSION_CODES.HONEYCOMB_MR2)
                ||
                (apiLevel >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !ViewConfiguration.get(context).hasPermanentMenuKey())
                ) {
            int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
            if (resourceId > 0) {
                return res.getDimensionPixelSize(resourceId);
            }
        }
        return 0;
    }

      

New result: The window is now ok, and when shown in A, it has an offset of y.

The question is, how can I get my window to look ok on both devices

+3


source to share


1 answer


I had the same problem today as you did. On my emulator, the PopupWindow was drawn correctly within the window bounds, thanks to PopupWindow Gravity.NO_GRAVITY

. However, on my Nexus 7 tablet, the PopupWindow was displayed below the device status bar which is displayed at the bottom.

My PopupWindow will appear after I clicked on the ImageButton on my screen, at that y-position of the ImageButtons (and how width I had match_parent

). This ImageButton position can be within the bounds of the window or just / completely below the status bar of the bottom tablet panel.

This is what I came up with:

What we have:

  • The [x, y] -position we provided to the PopupWindow showAtLocation-method (we only need the y position in this which I named oldY

    )

What we calculate:

  • Popup height
  • Status bar height
  • Maximum possible height inside window borders ( screenHeight - statusBarHeight - popupHeight

    )

What we then check:

  • We check if more oldY

    maxY

  • If so, it newY

    will maxY

    , and we re-draw the PopupWindow. If this is not the case, it means that we are not doing anything and just using the oldY

    Y-postition as correct.


NOTE 1: I did some code to do this, but during debugging it turned out that the status bar height is 0 on my emulator and my Nexus tablet, so just using screenHeight - popupHeight

was enough for me. However, I have included code to calculate the bottom status bar with boolean in my Config file to enable / disable this in case the app is installed on another tablet in the future.

Here in the code, I just added the description above to understand which approach I used to solve 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 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 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 height
            int popupHeight = layout.getHeight();
            // Get the Status Bar 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 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 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 myself installed tag_popup

in width = match_parent; height = wrap_content

:

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 Content (EditTexts, Spinner, TextViews, Button, etc.) -->

 </RelativeLayout>

      

NOTE 3: My app is forced to stay in portrait mode. I haven't tested this in landscape mode, but I'm guessing some modifications should be made (not sure though). EDIT: Tested and also works in landscape mode on my two devices. I don't know if this works in landscape mode with bottom bar height enabled.

Hope this helps you and other people with similar problems. Hopefully in the future they will fix the PopupWindow Gravity

so it will never be below the status bar unless the programmer wants to do it himself and changes the PopupWindow settings.

+1


source







All Articles