summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/widget/PopupWindow.java210
1 files changed, 133 insertions, 77 deletions
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 92631da2da90..04d598e782d1 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1494,7 +1494,7 @@ public class PopupWindow {
*
* @param anchor the view on which the popup window must be anchored
* @param outParams the layout parameters used to display the drop down
- * @param xOffset absolute horizontal offset from the top of the anchor
+ * @param xOffset absolute horizontal offset from the left of the anchor
* @param yOffset absolute vertical offset from the top of the anchor
* @param gravity horizontal gravity specifying popup alignment
* @return true if the popup is translated upwards to fit on screen
@@ -1523,6 +1523,8 @@ public class PopupWindow {
// Let the window manager know to align the top to y.
outParams.gravity = Gravity.LEFT | Gravity.TOP;
+ outParams.width = width;
+ outParams.height = height;
final int[] screenLocation = mTmpScreenLocation;
anchor.getLocationOnScreen(screenLocation);
@@ -1530,100 +1532,154 @@ public class PopupWindow {
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
- boolean onTop = false;
-
- final View root = anchor.getRootView();
- final int screenY = screenLocation[1] + anchorHeight + yOffset;
- final boolean tooFarDown = screenY + height > displayFrame.bottom;
- final boolean tooFarRight = outParams.x + width > root.getWidth();
- if (tooFarDown || tooFarRight) {
- // If the popup extends beyond the visible area, try to scroll the
- // parent so that it is fully visible.
- if (mAllowScrollingAnchorParent) {
- final int scrollX = anchor.getScrollX();
- final int scrollY = anchor.getScrollY();
- final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
- scrollY + height + anchorHeight + yOffset);
- anchor.requestRectangleOnScreen(r, true);
+ // 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);
+
+ // Next, attempt to fit the popup horizontally without resizing.
+ final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
+ anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
+ displayFrame.right, false);
+
+ // If the popup still doesn't fit, attempt to scroll the parent.
+ if (!fitsVertical || !fitsHorizontal) {
+ final int scrollX = anchor.getScrollX();
+ final int scrollY = anchor.getScrollY();
+ final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
+ scrollY + height + anchorHeight + yOffset);
+ if (mAllowScrollingAnchorParent && anchor.requestRectangleOnScreen(r, true)) {
+ // Reset for the new anchor position.
+ anchor.getLocationInWindow(drawingLocation);
+ outParams.x = drawingLocation[0] + xOffset;
+ outParams.y = drawingLocation[1] + anchorHeight + yOffset;
+
+ // Preserve the gravity adjustment.
+ if (hgrav == Gravity.RIGHT) {
+ outParams.x -= width - anchorWidth;
+ }
}
- // Update for the new anchor position.
- anchor.getLocationInWindow(drawingLocation);
- outParams.x = drawingLocation[0] + xOffset;
- outParams.y = drawingLocation[1] + anchorHeight + yOffset;
+ // Try to fit the popup again and allowing resizing.
+ tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
+ screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
+ tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
+ screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
+ }
- // Preserve the gravity adjustment.
- if (hgrav == Gravity.RIGHT) {
- outParams.x -= width - anchorWidth;
- }
+ // Return whether the popup's top edge is above the anchor's top edge.
+ return outParams.y < drawingLocation[1];
+ }
- final int newScreenY = screenLocation[1] + anchorHeight + yOffset;
- final boolean stillTooFarDown = newScreenY + height > displayFrame.bottom;
- if (stillTooFarDown) {
- // If the popup is still too far down, re-evaluate the space
- // available and decide whether the pop-up will go above or
- // below the anchor.
- anchor.getLocationOnScreen(screenLocation);
-
- final int below = displayFrame.bottom - screenLocation[1] - anchorHeight - yOffset;
- final int above = screenLocation[1] - displayFrame.top + yOffset;
- onTop = above > below;
-
- if (onTop) {
- // Move everything up.
- if (mOverlapAnchor) {
- yOffset += anchorHeight;
- }
- outParams.y = drawingLocation[1] - height + yOffset;
- }
+ private boolean tryFitVertical(@NonNull LayoutParams outParams, int yOffset, int height,
+ int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
+ int displayFrameBottom, boolean allowResize) {
+ final int anchorTopInScreen = screenLocationY + anchorHeight + yOffset;
+ final int spaceBelow = displayFrameBottom - anchorTopInScreen;
+ if (height <= spaceBelow) {
+ return true;
+ }
+
+ final int spaceAbove = displayFrameTop + anchorTopInScreen - anchorHeight;
+ if (height <= spaceAbove) {
+ // Move everything up.
+ if (mOverlapAnchor) {
+ yOffset += anchorHeight;
}
+ outParams.y = drawingLocationY - height + yOffset;
+
+ return true;
}
- if (mClipToScreen) {
- // Use screen coordinates for comparison against display frame.
- final int winOffsetX = screenLocation[0] - drawingLocation[0];
- final int winOffsetY = screenLocation[1] - drawingLocation[1];
- outParams.x += winOffsetX;
- outParams.y += winOffsetY;
+ if (positionInDisplayVertical(outParams, height, drawingLocationY, screenLocationY,
+ displayFrameTop, displayFrameBottom, allowResize)) {
+ return true;
+ }
- final int right = outParams.x + width;
- if (right > displayFrame.right) {
- // The popup is too far right, move it back in.
- outParams.x -= right - displayFrame.right;
- }
+ return false;
+ }
- if (outParams.x < displayFrame.left) {
- // The popup is too far left, move it back in and clip if it's
- // still too large.
- outParams.x = displayFrame.left;
+ private boolean positionInDisplayVertical(@NonNull LayoutParams outParams, int height,
+ int drawingLocationY, int screenLocationY, int displayFrameTop, int displayFrameBottom,
+ boolean canResize) {
+ boolean fitsInDisplay = true;
- final int displayFrameWidth = displayFrame.width();
- width = Math.min(width, displayFrameWidth);
- }
+ final int winOffsetY = screenLocationY - drawingLocationY;
+ outParams.y += winOffsetY;
+ outParams.height = height;
- final int bottom = outParams.y + height;
- if (bottom > displayFrame.bottom) {
- // The popup is too far down, move it back in.
- outParams.y -= bottom - displayFrame.bottom;
- }
+ final int bottom = outParams.y + height;
+ if (bottom > displayFrameBottom) {
+ // The popup is too far down, move it back in.
+ outParams.y -= bottom - displayFrameBottom;
+ }
- if (outParams.y < displayFrame.top) {
- // The popup is too far up, move it back in and clip if
- // it's still too large.
- outParams.y = displayFrame.top;
+ if (outParams.y < displayFrameTop) {
+ // The popup is too far up, move it back in and clip if
+ // it's still too large.
+ outParams.y = displayFrameTop;
- final int displayFrameHeight = displayFrame.height();
- height = Math.min(height, displayFrameHeight);
+ final int displayFrameHeight = displayFrameBottom - displayFrameTop;
+ if (canResize && height > displayFrameHeight) {
+ outParams.height = displayFrameHeight;
+ } else {
+ fitsInDisplay = false;
}
+ }
+
+ outParams.y -= winOffsetY;
- outParams.x -= winOffsetX;
- outParams.y -= winOffsetY;
+ return fitsInDisplay;
+ }
+
+ private boolean tryFitHorizontal(@NonNull LayoutParams outParams, int xOffset, int width,
+ int anchorWidth, int drawingLocationX, int screenLocationX, int displayFrameLeft,
+ int displayFrameRight, boolean allowResize) {
+ final int anchorLeftInScreen = screenLocationX + xOffset;
+ final int spaceRight = displayFrameRight - anchorLeftInScreen;
+ if (width <= spaceRight) {
+ return true;
}
- outParams.width = width;
- outParams.height = height;
+ if (positionInDisplayHorizontal(outParams, width, drawingLocationX, screenLocationX,
+ displayFrameLeft, displayFrameRight, allowResize)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean positionInDisplayHorizontal(@NonNull LayoutParams outParams, int width,
+ int drawingLocationX, int screenLocationX, int displayFrameLeft, int displayFrameRight,
+ boolean canResize) {
+ boolean fitsInDisplay = true;
+
+ // Use screen coordinates for comparison against display frame.
+ final int winOffsetX = screenLocationX - drawingLocationX;
+ outParams.x += winOffsetX;
+
+ final int right = outParams.x + width;
+ if (right > displayFrameRight) {
+ // The popup is too far right, move it back in.
+ outParams.x -= right - displayFrameRight;
+ }
+
+ if (outParams.x < displayFrameLeft) {
+ // The popup is too far left, move it back in and clip if it's
+ // still too large.
+ outParams.x = displayFrameLeft;
+
+ final int displayFrameWidth = displayFrameRight - displayFrameLeft;
+ if (canResize && width > displayFrameWidth) {
+ outParams.width = displayFrameWidth;
+ } else {
+ fitsInDisplay = false;
+ }
+ }
+
+ outParams.x -= winOffsetX;
- return onTop;
+ return fitsInDisplay;
}
/**