什么是PopupWindow.showAtLocation的确切行为?

时间:2014-08-04 04:20:36

标签: android popupwindow

我有一个安卓4.0手机说 A ,4.4平板电脑说 B ,都有一个软件导航栏。 我用这个:

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

显示特定位置的窗口。实际结果是,它在 A 上看起来没问题,但在 B 上有y偏移量。我发现偏移量似乎与 B 的导航栏的高度相同。所以我使用以下代码来获取高度并进行减法:

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

新结果是:现在 B 中的窗口正常,但是当在 A 中显示时,它有一个y偏移。

问题是,如何让我的窗口在两台设备上都显示正常

1 个答案:

答案 0 :(得分:1)

今天我和你有完全相同的问题。在我的模拟器上,由于PopupWindow的Gravity.NO_GRAVITY,PopupWindow在Window的边界内被正确绘制。但是,在我的Nexus 7平板电脑上,PopupWindow显示在设备的状态栏下方,该栏显示在底部。

在我点击屏幕上的ImageButton后,我的PopupWindow会出现在ImageButtons的y位置(并且我的宽度为match_parent)。这个ImageButton的位置可能位于Window的边界内,或者只是/完全位于Tablet的Bottom Status Bar之下。

这就是我想出的:

我们拥有什么:

  • 我们给PopupWindow的showAtLocation方法的[x,y]位置(我们只需要这里的y位置,我将其命名为oldY

我们的计算方法:

  • 弹出高度
  • 状态栏高度
  • 窗口边界(screenHeight - statusBarHeight - popupHeight
  • 内的最大可能高度

然后我们检查:

  • 我们会检查oldY是否大于maxY
  • 如果是这种情况,newY将是maxY,我们会重新绘制PopupWindow。如果不是这种情况,则意味着我们什么都不做,只需使用oldY作为正确的Y-postition。

注意1:我为此制作了代码,但在调试过程中,我的模拟器和Nexus平板电脑上的状态栏的高度为0,所以只需使用{ {1}}对我来说已经足够了。尽管如此,如果应用程序将来安装在另一台平板电脑上,我还是会在我的Config文件中包含用于计算Bottom Status Bar Height底部状态栏高度的代码来启用/禁用此功能。

这是在代码中,我刚刚添加了上面的描述,以明确我用来解决这个问题的方法:

screenHeight - popupHeight

注2:我已在此行中将// 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); } }); } 本身设置为tag_popup

width = match_parent; height = wrap_content

此弹出窗口的主要布局为myPopupWindow.setWindowLayoutMode(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

width = match_parent; height = match_parent

注意3: 我的应用程序被强制保持在纵向模式。我还没有在横向模式下对此进行测试,但我认为应该进行一些修改(但不确定)。编辑:经过测试,它也可以在我的两个设备上以横向模式工作。我不知道这是否也适用于横向模式并且启用了底栏高度。

希望这可以帮助您和其他一些有类似问题的人。希望他们将来修复PopupWindow的 <?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> ,所以它永远不会低于状态栏,除非程序员自己想要并更改PopupWindow的设置。