Add gravity settings to PopupWindow/ListPopupWindow/PopupMenu
Allow calling code to specify left/right/start/end gravity when showing a popup attached to an anchor. This allows easy alignment of either the right or left edges of the popup and anchor view. Bug 10728401 Change-Id: Ie0844a04ea0576fa67b0972f5873aaa4c5b823f6
This commit is contained in:
@@ -31576,6 +31576,7 @@ package android.widget {
|
||||
method public void setAnimationStyle(int);
|
||||
method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
|
||||
method public void setContentWidth(int);
|
||||
method public void setDropDownGravity(int);
|
||||
method public void setHeight(int);
|
||||
method public void setHorizontalOffset(int);
|
||||
method public void setInputMethodMode(int);
|
||||
@@ -31757,6 +31758,7 @@ package android.widget {
|
||||
|
||||
public class PopupMenu {
|
||||
ctor public PopupMenu(android.content.Context, android.view.View);
|
||||
ctor public PopupMenu(android.content.Context, android.view.View, int);
|
||||
method public void dismiss();
|
||||
method public android.view.View.OnTouchListener getDragToOpenListener();
|
||||
method public android.view.Menu getMenu();
|
||||
@@ -31820,6 +31822,7 @@ package android.widget {
|
||||
method public void setWindowLayoutMode(int, int);
|
||||
method public void showAsDropDown(android.view.View);
|
||||
method public void showAsDropDown(android.view.View, int, int);
|
||||
method public void showAsDropDown(android.view.View, int, int, int);
|
||||
method public void showAtLocation(android.view.View, int, int, int);
|
||||
method public void update();
|
||||
method public void update(int, int);
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.IntProperty;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -76,6 +77,8 @@ public class ListPopupWindow {
|
||||
private int mDropDownVerticalOffset;
|
||||
private boolean mDropDownVerticalOffsetSet;
|
||||
|
||||
private int mDropDownGravity = Gravity.NO_GRAVITY;
|
||||
|
||||
private boolean mDropDownAlwaysVisible = false;
|
||||
private boolean mForceIgnoreOutsideTouch = false;
|
||||
int mListItemExpandMaximum = Integer.MAX_VALUE;
|
||||
@@ -438,6 +441,16 @@ public class ListPopupWindow {
|
||||
mDropDownVerticalOffsetSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the gravity of the dropdown list. This is commonly used to
|
||||
* set gravity to START or END for alignment with the anchor.
|
||||
*
|
||||
* @param gravity Gravity value to use
|
||||
*/
|
||||
public void setDropDownGravity(int gravity) {
|
||||
mDropDownGravity = gravity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the popup window in pixels.
|
||||
*/
|
||||
@@ -610,7 +623,7 @@ public class ListPopupWindow {
|
||||
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
|
||||
mPopup.setTouchInterceptor(mTouchInterceptor);
|
||||
mPopup.showAsDropDown(getAnchorView(),
|
||||
mDropDownHorizontalOffset, mDropDownVerticalOffset);
|
||||
mDropDownHorizontalOffset, mDropDownVerticalOffset, mDropDownGravity);
|
||||
mDropDownList.setSelection(ListView.INVALID_POSITION);
|
||||
|
||||
if (!mModal || mDropDownList.isInTouchMode()) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.android.internal.view.menu.MenuPresenter;
|
||||
import com.android.internal.view.menu.SubMenuBuilder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
@@ -64,12 +65,25 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
|
||||
* is room, or above it if there is not.
|
||||
*/
|
||||
public PopupMenu(Context context, View anchor) {
|
||||
this(context, anchor, Gravity.NO_GRAVITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new PopupMenu.
|
||||
*
|
||||
* @param context Context for the PopupMenu.
|
||||
* @param anchor Anchor view for this popup. The popup will appear below the anchor if there
|
||||
* is room, or above it if there is not.
|
||||
* @param gravity The {@link Gravity} value for aligning the popup with its anchor
|
||||
*/
|
||||
public PopupMenu(Context context, View anchor, int gravity) {
|
||||
// TODO Theme?
|
||||
mContext = context;
|
||||
mMenu = new MenuBuilder(context);
|
||||
mMenu.setCallback(this);
|
||||
mAnchor = anchor;
|
||||
mPopup = new MenuPopupHelper(context, mMenu, anchor);
|
||||
mPopup.setGravity(gravity);
|
||||
mPopup.setCallback(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,9 @@ public class PopupWindow {
|
||||
* screen as needed, regardless of whether this covers the input method.
|
||||
*/
|
||||
public static final int INPUT_METHOD_NOT_NEEDED = 2;
|
||||
|
||||
|
||||
private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
|
||||
|
||||
private Context mContext;
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
@@ -135,12 +137,13 @@ public class PopupWindow {
|
||||
WindowManager.LayoutParams p = (WindowManager.LayoutParams)
|
||||
mPopupView.getLayoutParams();
|
||||
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff));
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
|
||||
mAnchoredGravity));
|
||||
update(p.x, p.y, -1, -1, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
private int mAnchorXoff, mAnchorYoff;
|
||||
private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
|
||||
|
||||
private boolean mPopupViewInitialLayoutDirectionInherited;
|
||||
|
||||
@@ -873,15 +876,38 @@ public class PopupWindow {
|
||||
* location, the popup will be moved correspondingly.</p>
|
||||
*
|
||||
* @param anchor the view on which to pin the popup window
|
||||
* @param xoff A horizontal offset from the anchor in pixels
|
||||
* @param yoff A vertical offset from the anchor in pixels
|
||||
*
|
||||
* @see #dismiss()
|
||||
*/
|
||||
public void showAsDropDown(View anchor, int xoff, int yoff) {
|
||||
showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Display the content view in a popup window anchored to the bottom-left
|
||||
* corner of the anchor view offset by the specified x and y coordinates.
|
||||
* If there is not enough room on screen to show
|
||||
* the popup in its entirety, this method tries to find a parent scroll
|
||||
* view to scroll. If no parent scroll view can be scrolled, the bottom-left
|
||||
* corner of the popup is pinned at the top left corner of the anchor view.</p>
|
||||
* <p>If the view later scrolls to move <code>anchor</code> to a different
|
||||
* location, the popup will be moved correspondingly.</p>
|
||||
*
|
||||
* @param anchor the view on which to pin the popup window
|
||||
* @param xoff A horizontal offset from the anchor in pixels
|
||||
* @param yoff A vertical offset from the anchor in pixels
|
||||
* @param gravity Alignment of the popup relative to the anchor
|
||||
*
|
||||
* @see #dismiss()
|
||||
*/
|
||||
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
|
||||
if (isShowing() || mContentView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
registerForScrollChanged(anchor, xoff, yoff);
|
||||
registerForScrollChanged(anchor, xoff, yoff, gravity);
|
||||
|
||||
mIsShowing = true;
|
||||
mIsDropdown = true;
|
||||
@@ -889,7 +915,7 @@ public class PopupWindow {
|
||||
WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
|
||||
preparePopup(p);
|
||||
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff));
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
|
||||
|
||||
if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
|
||||
if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
|
||||
@@ -1105,17 +1131,24 @@ public class PopupWindow {
|
||||
* @return true if the popup is translated upwards to fit on screen
|
||||
*/
|
||||
private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
|
||||
int xoff, int yoff) {
|
||||
int xoff, int yoff, int gravity) {
|
||||
|
||||
final int anchorHeight = anchor.getHeight();
|
||||
anchor.getLocationInWindow(mDrawingLocation);
|
||||
p.x = mDrawingLocation[0] + xoff;
|
||||
p.y = mDrawingLocation[1] + anchorHeight + yoff;
|
||||
|
||||
final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) &
|
||||
Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
if (hgrav == Gravity.RIGHT) {
|
||||
// Flip the location to align the right sides of the popup and anchor instead of left
|
||||
p.x -= mPopupWidth - anchor.getWidth();
|
||||
}
|
||||
|
||||
boolean onTop = false;
|
||||
|
||||
p.gravity = Gravity.START | Gravity.TOP;
|
||||
|
||||
p.gravity = Gravity.LEFT | Gravity.TOP;
|
||||
|
||||
anchor.getLocationOnScreen(mScreenLocation);
|
||||
final Rect displayFrame = new Rect();
|
||||
anchor.getWindowVisibleDisplayFrame(displayFrame);
|
||||
@@ -1141,6 +1174,11 @@ public class PopupWindow {
|
||||
anchor.getLocationInWindow(mDrawingLocation);
|
||||
p.x = mDrawingLocation[0] + xoff;
|
||||
p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
|
||||
|
||||
// Preserve the gravity adjustment
|
||||
if (hgrav == Gravity.RIGHT) {
|
||||
p.x -= mPopupWidth - anchor.getWidth();
|
||||
}
|
||||
|
||||
// determine whether there is more space above or below the anchor
|
||||
anchor.getLocationOnScreen(mScreenLocation);
|
||||
@@ -1148,7 +1186,7 @@ public class PopupWindow {
|
||||
onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) <
|
||||
(mScreenLocation[1] - yoff - displayFrame.top);
|
||||
if (onTop) {
|
||||
p.gravity = Gravity.START | Gravity.BOTTOM;
|
||||
p.gravity = Gravity.LEFT | Gravity.BOTTOM;
|
||||
p.y = root.getHeight() - mDrawingLocation[1] + yoff;
|
||||
} else {
|
||||
p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
|
||||
@@ -1436,7 +1474,7 @@ public class PopupWindow {
|
||||
* @param height the new height, can be -1 to ignore
|
||||
*/
|
||||
public void update(View anchor, int width, int height) {
|
||||
update(anchor, false, 0, 0, true, width, height);
|
||||
update(anchor, false, 0, 0, true, width, height, mAnchoredGravity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1455,11 +1493,11 @@ public class PopupWindow {
|
||||
* @param height the new height, can be -1 to ignore
|
||||
*/
|
||||
public void update(View anchor, int xoff, int yoff, int width, int height) {
|
||||
update(anchor, true, xoff, yoff, true, width, height);
|
||||
update(anchor, true, xoff, yoff, true, width, height, mAnchoredGravity);
|
||||
}
|
||||
|
||||
private void update(View anchor, boolean updateLocation, int xoff, int yoff,
|
||||
boolean updateDimension, int width, int height) {
|
||||
boolean updateDimension, int width, int height, int gravity) {
|
||||
|
||||
if (!isShowing() || mContentView == null) {
|
||||
return;
|
||||
@@ -1468,11 +1506,12 @@ public class PopupWindow {
|
||||
WeakReference<View> oldAnchor = mAnchor;
|
||||
final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
|
||||
if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
|
||||
registerForScrollChanged(anchor, xoff, yoff);
|
||||
registerForScrollChanged(anchor, xoff, yoff, gravity);
|
||||
} else if (needsUpdate) {
|
||||
// No need to register again if this is a DropDown, showAsDropDown already did.
|
||||
mAnchorXoff = xoff;
|
||||
mAnchorYoff = yoff;
|
||||
mAnchoredGravity = gravity;
|
||||
}
|
||||
|
||||
WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
|
||||
@@ -1494,9 +1533,10 @@ public class PopupWindow {
|
||||
int y = p.y;
|
||||
|
||||
if (updateLocation) {
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff));
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
|
||||
} else {
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff));
|
||||
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
|
||||
mAnchoredGravity));
|
||||
}
|
||||
|
||||
update(p.x, p.y, width, height, x != p.x || y != p.y);
|
||||
@@ -1525,7 +1565,7 @@ public class PopupWindow {
|
||||
mAnchor = null;
|
||||
}
|
||||
|
||||
private void registerForScrollChanged(View anchor, int xoff, int yoff) {
|
||||
private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
|
||||
unregisterForScrollChanged();
|
||||
|
||||
mAnchor = new WeakReference<View>(anchor);
|
||||
@@ -1536,6 +1576,7 @@ public class PopupWindow {
|
||||
|
||||
mAnchorXoff = xoff;
|
||||
mAnchorYoff = yoff;
|
||||
mAnchoredGravity = gravity;
|
||||
}
|
||||
|
||||
private class PopupViewContainer extends FrameLayout {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.ActionProvider;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.View;
|
||||
@@ -665,6 +666,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter
|
||||
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
|
||||
boolean overflowOnly) {
|
||||
super(context, menu, anchorView, overflowOnly);
|
||||
setGravity(Gravity.END);
|
||||
setCallback(mPopupPresenterCallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.internal.view.menu;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Parcelable;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
@@ -69,6 +70,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
|
||||
/** Cached content width from {@link #measureContentWidth}. */
|
||||
private int mContentWidth;
|
||||
|
||||
private int mDropDownGravity = Gravity.NO_GRAVITY;
|
||||
|
||||
public MenuPopupHelper(Context context, MenuBuilder menu) {
|
||||
this(context, menu, null, false);
|
||||
}
|
||||
@@ -102,6 +105,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
|
||||
mForceShowIcon = forceShow;
|
||||
}
|
||||
|
||||
public void setGravity(int gravity) {
|
||||
mDropDownGravity = gravity;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!tryShow()) {
|
||||
throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
|
||||
@@ -126,6 +133,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
|
||||
if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
|
||||
anchor.addOnAttachStateChangeListener(this);
|
||||
mPopup.setAnchorView(anchor);
|
||||
mPopup.setDropDownGravity(mDropDownGravity);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user