Animate context popup menu around origin point
Also enables the context popup menu enter transition. Bug: 25801140 Change-Id: Id7fb384e8ac8974189b32a052352bd2f6cbb176e
This commit is contained in:
@@ -74,6 +74,7 @@ public class ListPopupWindow implements ShowableListMenu {
|
||||
private int mDropDownVerticalOffset;
|
||||
private int mDropDownWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
|
||||
private boolean mDropDownVerticalOffsetSet;
|
||||
private boolean mIsAnimatedFromAnchor = true;
|
||||
|
||||
private int mDropDownGravity = Gravity.NO_GRAVITY;
|
||||
|
||||
@@ -101,7 +102,13 @@ public class ListPopupWindow implements ShowableListMenu {
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
private Rect mTempRect = new Rect();
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
/**
|
||||
* Optional anchor-relative bounds to be used as the transition epicenter.
|
||||
* When {@code null}, the anchor bounds are used as the epicenter.
|
||||
*/
|
||||
private Rect mEpicenterBounds;
|
||||
|
||||
private boolean mModal;
|
||||
|
||||
@@ -451,6 +458,17 @@ public class ListPopupWindow implements ShowableListMenu {
|
||||
mDropDownVerticalOffsetSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the anchor-relative bounds of the popup's transition
|
||||
* epicenter.
|
||||
*
|
||||
* @param bounds anchor-relative bounds
|
||||
* @hide
|
||||
*/
|
||||
public void setEpicenterBounds(Rect bounds) {
|
||||
mEpicenterBounds = bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the gravity of the dropdown list. This is commonly used to
|
||||
* set gravity to START or END for alignment with the anchor.
|
||||
@@ -649,6 +667,7 @@ public class ListPopupWindow implements ShowableListMenu {
|
||||
// only set this if the dropdown is not always visible
|
||||
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
|
||||
mPopup.setTouchInterceptor(mTouchInterceptor);
|
||||
mPopup.setEpicenterBounds(mEpicenterBounds);
|
||||
mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset,
|
||||
mDropDownVerticalOffset, mDropDownGravity);
|
||||
mDropDownList.setSelection(ListView.INVALID_POSITION);
|
||||
|
||||
@@ -150,6 +150,7 @@ public class PopupWindow {
|
||||
|
||||
private Transition mEnterTransition;
|
||||
private Transition mExitTransition;
|
||||
private Rect mEpicenterBounds;
|
||||
|
||||
private boolean mAboveAnchor;
|
||||
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
|
||||
@@ -345,6 +346,25 @@ public class PopupWindow {
|
||||
mExitTransition = exitTransition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounds used as the epicenter of the enter and exit transitions.
|
||||
* <p>
|
||||
* Transitions use a point or Rect, referred to as the epicenter, to orient
|
||||
* the direction of travel. For popup windows, the anchor view bounds are
|
||||
* used as the default epicenter.
|
||||
* <p>
|
||||
* See {@link Transition#setEpicenterCallback(EpicenterCallback)} for more
|
||||
* information about how transition epicenters.
|
||||
*
|
||||
* @param bounds the epicenter bounds relative to the anchor view, or
|
||||
* {@code null} to use the default epicenter
|
||||
* @see #getTransitionEpicenter()
|
||||
* @hide
|
||||
*/
|
||||
public void setEpicenterBounds(Rect bounds) {
|
||||
mEpicenterBounds = bounds;
|
||||
}
|
||||
|
||||
private Transition getTransition(int resId) {
|
||||
if (resId != 0 && resId != R.transition.no_transition) {
|
||||
final TransitionInflater inflater = TransitionInflater.from(mContext);
|
||||
@@ -1621,7 +1641,7 @@ public class PopupWindow {
|
||||
p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
mWindowManager.updateViewLayout(decorView, p);
|
||||
|
||||
final Rect epicenter = getRelativeAnchorBounds();
|
||||
final Rect epicenter = getTransitionEpicenter();
|
||||
exitTransition.setEpicenterCallback(new EpicenterCallback() {
|
||||
@Override
|
||||
public Rect onGetEpicenter(Transition transition) {
|
||||
@@ -1646,7 +1666,17 @@ public class PopupWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private Rect getRelativeAnchorBounds() {
|
||||
/**
|
||||
* Returns the window-relative epicenter bounds to be used by enter and
|
||||
* exit transitions.
|
||||
* <p>
|
||||
* <strong>Note:</strong> This is distinct from the rect passed to
|
||||
* {@link #setEpicenterBounds(Rect)}, which is anchor-relative.
|
||||
*
|
||||
* @return the window-relative epicenter bounds to be used by enter and
|
||||
* exit transitions
|
||||
*/
|
||||
private Rect getTransitionEpicenter() {
|
||||
final View anchor = mAnchor != null ? mAnchor.get() : null;
|
||||
final View decor = mDecorView;
|
||||
if (anchor == null || decor == null) {
|
||||
@@ -1659,6 +1689,15 @@ public class PopupWindow {
|
||||
// Compute the position of the anchor relative to the popup.
|
||||
final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
|
||||
bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
|
||||
|
||||
// Use anchor-relative epicenter, if specified.
|
||||
if (mEpicenterBounds != null) {
|
||||
final int offsetX = bounds.left;
|
||||
final int offsetY = bounds.top;
|
||||
bounds.set(mEpicenterBounds);
|
||||
bounds.offset(offsetX, offsetY);
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@@ -2031,7 +2070,7 @@ public class PopupWindow {
|
||||
observer.removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
|
||||
final Rect epicenter = getRelativeAnchorBounds();
|
||||
final Rect epicenter = getTransitionEpicenter();
|
||||
enterTransition.setEpicenterCallback(new EpicenterCallback() {
|
||||
@Override
|
||||
public Rect onGetEpicenter(Transition transition) {
|
||||
|
||||
@@ -362,6 +362,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
|
||||
|
||||
final int x;
|
||||
final int y;
|
||||
final Rect epicenterBounds;
|
||||
if (parentView != null) {
|
||||
// This menu is a cascading submenu anchored to a parent view.
|
||||
popupWindow.setTouchModal(false);
|
||||
@@ -396,13 +397,16 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
|
||||
}
|
||||
|
||||
y = parentOffsetTop;
|
||||
epicenterBounds = null;
|
||||
} else {
|
||||
x = mInitXOffset;
|
||||
y = mInitYOffset;
|
||||
epicenterBounds = getEpicenterBounds();
|
||||
}
|
||||
|
||||
popupWindow.setHorizontalOffset(x);
|
||||
popupWindow.setVerticalOffset(y);
|
||||
popupWindow.setEpicenterBounds(epicenterBounds);
|
||||
|
||||
final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition);
|
||||
mShowingMenus.add(menuInfo);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.internal.view.menu;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
@@ -37,6 +38,7 @@ import android.widget.PopupWindow;
|
||||
*/
|
||||
public abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
|
||||
AdapterView.OnItemClickListener {
|
||||
private Rect mEpicenterBounds;
|
||||
|
||||
public abstract void setForceShowIcon(boolean forceShow);
|
||||
|
||||
@@ -59,6 +61,23 @@ public abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
|
||||
|
||||
public abstract void setVerticalOffset(int y);
|
||||
|
||||
/**
|
||||
* Specifies the anchor-relative bounds of the popup's transition
|
||||
* epicenter.
|
||||
*
|
||||
* @param bounds anchor-relative bounds
|
||||
*/
|
||||
public void setEpicenterBounds(Rect bounds) {
|
||||
mEpicenterBounds = bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return anchor-relative bounds of the popup's transition epicenter
|
||||
*/
|
||||
public Rect getEpicenterBounds() {
|
||||
return mEpicenterBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether a title entry should be shown in the popup menu (if a title exists for the
|
||||
* menu).
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StyleRes;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.PopupWindow.OnDismissListener;
|
||||
@@ -31,6 +33,8 @@ import android.widget.PopupWindow.OnDismissListener;
|
||||
* Presents a menu as a small, simple popup anchored to another view.
|
||||
*/
|
||||
public class MenuPopupHelper implements MenuHelper {
|
||||
private static final int TOUCH_EPICENTER_SIZE_DP = 48;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
// Immutable cached popup menu properties.
|
||||
@@ -163,6 +167,11 @@ public class MenuPopupHelper implements MenuHelper {
|
||||
* Shows the popup menu and makes a best-effort to anchor it to the
|
||||
* specified (x,y) coordinate relative to the anchor view.
|
||||
* <p>
|
||||
* Additionally, the popup's transition epicenter (see
|
||||
* {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be
|
||||
* centered on the specified coordinate, rather than using the bounds of
|
||||
* the anchor view.
|
||||
* <p>
|
||||
* If the popup's resolved gravity is {@link Gravity#LEFT}, this will
|
||||
* display the popup with its top-left corner at (x,y) relative to the
|
||||
* anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
|
||||
@@ -222,8 +231,11 @@ public class MenuPopupHelper implements MenuHelper {
|
||||
return popup;
|
||||
}
|
||||
|
||||
private void showPopup(int xOffset, int yOffset, boolean resolveOffsets, boolean showTitle) {
|
||||
if (resolveOffsets) {
|
||||
private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) {
|
||||
final MenuPopup popup = getPopup();
|
||||
popup.setShowTitle(showTitle);
|
||||
|
||||
if (useOffsets) {
|
||||
// If the resolved drop-down gravity is RIGHT, the popup's right
|
||||
// edge will be aligned with the anchor view. Adjust by the anchor
|
||||
// width such that the top-right corner is at the X offset.
|
||||
@@ -232,12 +244,21 @@ public class MenuPopupHelper implements MenuHelper {
|
||||
if (hgrav == Gravity.RIGHT) {
|
||||
xOffset -= mAnchorView.getWidth();
|
||||
}
|
||||
|
||||
popup.setHorizontalOffset(xOffset);
|
||||
popup.setVerticalOffset(yOffset);
|
||||
|
||||
// Set the transition epicenter to be roughly finger (or mouse
|
||||
// cursor) sized and centered around the offset position. This
|
||||
// will give the appearance that the window is emerging from
|
||||
// the touch point.
|
||||
final float density = mContext.getResources().getDisplayMetrics().density;
|
||||
final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2);
|
||||
final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize,
|
||||
xOffset + halfSize, yOffset + halfSize);
|
||||
popup.setEpicenterBounds(epicenter);
|
||||
}
|
||||
|
||||
final MenuPopup popup = getPopup();
|
||||
popup.setHorizontalOffset(xOffset);
|
||||
popup.setVerticalOffset(yOffset);
|
||||
popup.setShowTitle(showTitle);
|
||||
popup.show();
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
|
||||
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
||||
mPopup.setHorizontalOffset(mXOffset);
|
||||
mPopup.setVerticalOffset(mYOffset);
|
||||
mPopup.setEpicenterBounds(getEpicenterBounds());
|
||||
mPopup.show();
|
||||
|
||||
ListView listView = mPopup.getListView();
|
||||
|
||||
@@ -877,7 +877,6 @@ please see styles_device_defaults.xml.
|
||||
|
||||
<style name="Widget.Material.ContextPopupMenu" parent="Widget.Material.ListPopupWindow">
|
||||
<item name="overlapAnchor">true</item>
|
||||
<item name="popupEnterTransition">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Material.ActionButton">
|
||||
|
||||
Reference in New Issue
Block a user