diff options
author | Adam Powell <adamp@google.com> | 2013-09-26 15:36:34 -0700 |
---|---|---|
committer | Adam Powell <adamp@google.com> | 2013-09-26 15:49:27 -0700 |
commit | 54c94dea8a26e66fa59a31fd9170ca221052d3aa (patch) | |
tree | 76575b5ed456ff88aff056c19a43f24d0b36f251 | |
parent | bde988ade5f75c3d241422dce2ee4124871fab53 (diff) | |
download | frameworks_base-54c94dea8a26e66fa59a31fd9170ca221052d3aa.zip frameworks_base-54c94dea8a26e66fa59a31fd9170ca221052d3aa.tar.gz frameworks_base-54c94dea8a26e66fa59a31fd9170ca221052d3aa.tar.bz2 |
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
-rw-r--r-- | api/current.txt | 3 | ||||
-rw-r--r-- | core/java/android/widget/ListPopupWindow.java | 15 | ||||
-rw-r--r-- | core/java/android/widget/PopupMenu.java | 14 | ||||
-rw-r--r-- | core/java/android/widget/PopupWindow.java | 73 | ||||
-rw-r--r-- | core/java/com/android/internal/view/menu/ActionMenuPresenter.java | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/view/menu/MenuPopupHelper.java | 8 |
6 files changed, 98 insertions, 17 deletions
diff --git a/api/current.txt b/api/current.txt index c2dfa92..8d7ad8e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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); diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index b7e1fdd..66fe46f 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -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; @@ -439,6 +442,16 @@ public class ListPopupWindow { } /** + * 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. */ public int getWidth() { @@ -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()) { diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java index 9ac6a59..111dadc 100644 --- a/core/java/android/widget/PopupMenu.java +++ b/core/java/android/widget/PopupMenu.java @@ -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); } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 1460737..5663959 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -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 { diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 4c6ddbf..6471e14 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -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); } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index dbb78c2..05e9a66 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -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; } |