Gradle dependencies
compile group: 'androidx.appcompat', name: 'appcompat', version: '1.6.0-alpha04'
- groupId: androidx.appcompat
- artifactId: appcompat
- version: 1.6.0-alpha04
Artifact androidx.appcompat:appcompat:1.6.0-alpha04 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.appcompat:appcompat com.android.support:appcompat-v7
Androidx class mapping:
androidx.appcompat.widget.ActionMenuView android.support.v7.widget.ActionMenuView
Overview
ActionMenuView is a presentation of a series of menu options as a View. It provides
several top level options as action buttons while spilling remaining options over as
items in an overflow menu. This allows applications to present packs of actions inline with
specific or repeating content.
Summary
Methods |
---|
protected boolean | checkLayoutParams(ViewGroup.LayoutParams p)
|
public void | dismissPopupMenus()
Dismiss any popups associated with this menu view. |
public boolean | dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
|
protected LinearLayoutCompat.LayoutParams | generateDefaultLayoutParams()
Returns a set of layout parameters with a width of
and a height of
when the layout's orientation is LinearLayoutCompat.VERTICAL. |
public ActionMenuView.LayoutParams | generateLayoutParams(AttributeSet attrs)
|
protected ActionMenuView.LayoutParams | generateLayoutParams(ViewGroup.LayoutParams p)
|
public ActionMenuView.LayoutParams | generateOverflowButtonLayoutParams()
|
public Menu | getMenu()
Returns the Menu object that this ActionMenuView is currently presenting. |
public Drawable | getOverflowIcon()
Return the current drawable used as the overflow icon. |
public int | getPopupTheme()
|
public int | getWindowAnimations()
|
protected boolean | hasSupportDividerBeforeChildAt(int childIndex)
|
public boolean | hideOverflowMenu()
Hide the overflow items from the associated menu. |
public void | initialize(MenuBuilder menu)
|
public boolean | invokeItem(MenuItemImpl item)
|
public boolean | isOverflowMenuShowing()
Check whether the overflow menu is currently showing. |
public boolean | isOverflowMenuShowPending()
|
public boolean | isOverflowReserved()
|
public void | onConfigurationChanged(Configuration newConfig)
|
public void | onDetachedFromWindow()
|
protected void | onLayout(boolean changed, int left, int top, int right, int bottom)
|
protected void | onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
public MenuBuilder | peekMenu()
Returns the current menu or null if one has not yet been configured. |
public void | setExpandedActionViewsExclusive(boolean exclusive)
|
public void | setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb)
Must be called before the first call to getMenu() |
public void | setOnMenuItemClickListener(ActionMenuView.OnMenuItemClickListener listener)
|
public void | setOverflowIcon(Drawable icon)
Set the icon to use for the overflow button. |
public void | setOverflowReserved(boolean reserveOverflow)
|
public void | setPopupTheme(int resId)
Specifies the theme to use when inflating popup menus. |
public void | setPresenter(androidx.appcompat.widget.ActionMenuPresenter presenter)
|
public boolean | showOverflowMenu()
Show the overflow items from the associated menu. |
from LinearLayoutCompat | getBaseline, getBaselineAlignedChildIndex, getDividerDrawable, getDividerPadding, getDividerWidth, getGravity, getOrientation, getShowDividers, getWeightSum, hasDividerBeforeChildAt, isBaselineAligned, isMeasureWithLargestChildEnabled, onDraw, onInitializeAccessibilityEvent, onInitializeAccessibilityNodeInfo, setBaselineAligned, setBaselineAlignedChildIndex, setDividerDrawable, setDividerPadding, setGravity, setHorizontalGravity, setMeasureWithLargestChildEnabled, setOrientation, setShowDividers, setVerticalGravity, setWeightSum, shouldDelayChildPressedState |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructors
public
ActionMenuView(Context context)
public
ActionMenuView(Context context, AttributeSet attrs)
Methods
public void
setPopupTheme(int resId)
Specifies the theme to use when inflating popup menus. By default, uses
the same theme as the action menu view itself.
Parameters:
resId: theme used to inflate popup menus
See also: ActionMenuView.getPopupTheme()
public int
getPopupTheme()
Returns:
resource identifier of the theme used to inflate popup menus, or
0 if menus are inflated against the action menu view theme
See also: ActionMenuView.setPopupTheme(int)
public void
setPresenter(androidx.appcompat.widget.ActionMenuPresenter presenter)
Parameters:
presenter: Menu presenter used to display popup menu
public void
onConfigurationChanged(Configuration newConfig)
protected void
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
protected void
onLayout(boolean changed, int left, int top, int right, int bottom)
public void
onDetachedFromWindow()
public void
setOverflowIcon(Drawable icon)
Set the icon to use for the overflow button.
Parameters:
icon: Drawable to set, may be null to clear the icon
public Drawable
getOverflowIcon()
Return the current drawable used as the overflow icon.
Returns:
The overflow icon drawable
public boolean
isOverflowReserved()
public void
setOverflowReserved(boolean reserveOverflow)
Returns a set of layout parameters with a width of
and a height of
when the layout's orientation is LinearLayoutCompat.VERTICAL. When the orientation is
LinearLayoutCompat.HORIZONTAL, the width is set to LinearLayoutCompat.LayoutParams
and the height to LinearLayoutCompat.LayoutParams.
protected boolean
checkLayoutParams(ViewGroup.LayoutParams p)
public int
getWindowAnimations()
Returns the Menu object that this ActionMenuView is currently presenting.
Applications should use this method to obtain the ActionMenuView's Menu object
and inflate or add content to it as necessary.
Returns:
the Menu presented by this view
Must be called before the first call to getMenu()
Returns the current menu or null if one has not yet been configured.
public boolean
showOverflowMenu()
Show the overflow items from the associated menu.
Returns:
true if the menu was able to be shown, false otherwise
public boolean
hideOverflowMenu()
Hide the overflow items from the associated menu.
Returns:
true if the menu was able to be hidden, false otherwise
public boolean
isOverflowMenuShowing()
Check whether the overflow menu is currently showing. This may not reflect
a pending show operation in progress.
Returns:
true if the overflow menu is currently showing
public boolean
isOverflowMenuShowPending()
public void
dismissPopupMenus()
Dismiss any popups associated with this menu view.
protected boolean
hasSupportDividerBeforeChildAt(int childIndex)
public boolean
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
public void
setExpandedActionViewsExclusive(boolean exclusive)
Source
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.appcompat.widget;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
import androidx.appcompat.view.menu.ActionMenuItemView;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.view.menu.MenuItemImpl;
import androidx.appcompat.view.menu.MenuPresenter;
import androidx.appcompat.view.menu.MenuView;
/**
* ActionMenuView is a presentation of a series of menu options as a View. It provides
* several top level options as action buttons while spilling remaining options over as
* items in an overflow menu. This allows applications to present packs of actions inline with
* specific or repeating content.
*/
@SuppressWarnings("HiddenSuperclass")
public class ActionMenuView extends LinearLayoutCompat implements MenuBuilder.ItemInvoker,
MenuView {
private static final String TAG = "ActionMenuView";
static final int MIN_CELL_SIZE = 56; // dips
static final int GENERATED_ITEM_PADDING = 4; // dips
private MenuBuilder mMenu;
/** Context against which to inflate popup menus. */
private Context mPopupContext;
/** Theme resource against which to inflate popup menus. */
private int mPopupTheme;
private boolean mReserveOverflow;
private ActionMenuPresenter mPresenter;
private MenuPresenter.Callback mActionMenuPresenterCallback;
MenuBuilder.Callback mMenuBuilderCallback;
private boolean mFormatItems;
private int mFormatItemsWidth;
private int mMinCellSize;
private int mGeneratedItemPadding;
OnMenuItemClickListener mOnMenuItemClickListener;
public ActionMenuView(@NonNull Context context) {
this(context, null);
}
public ActionMenuView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setBaselineAligned(false);
final float density = context.getResources().getDisplayMetrics().density;
mMinCellSize = (int) (MIN_CELL_SIZE * density);
mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
mPopupContext = context;
mPopupTheme = 0;
}
/**
* Specifies the theme to use when inflating popup menus. By default, uses
* the same theme as the action menu view itself.
*
* @param resId theme used to inflate popup menus
* @see #getPopupTheme()
*/
public void setPopupTheme(@StyleRes int resId) {
if (mPopupTheme != resId) {
mPopupTheme = resId;
if (resId == 0) {
mPopupContext = getContext();
} else {
mPopupContext = new ContextThemeWrapper(getContext(), resId);
}
}
}
/**
* @return resource identifier of the theme used to inflate popup menus, or
* 0 if menus are inflated against the action menu view theme
* @see #setPopupTheme(int)
*/
public int getPopupTheme() {
return mPopupTheme;
}
/**
* @param presenter Menu presenter used to display popup menu
* @hide
*/
@RestrictTo(LIBRARY)
public void setPresenter(ActionMenuPresenter presenter) {
mPresenter = presenter;
mPresenter.setMenuView(this);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (mPresenter != null) {
mPresenter.updateMenuView(false);
if (mPresenter.isOverflowMenuShowing()) {
mPresenter.hideOverflowMenu();
mPresenter.showOverflowMenu();
}
}
}
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mOnMenuItemClickListener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// If we've been given an exact size to match, apply special formatting during layout.
final boolean wasFormatted = mFormatItems;
mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
if (wasFormatted != mFormatItems) {
mFormatItemsWidth = 0; // Reset this when switching modes
}
// Special formatting can change whether items can fit as action buttons.
// Kick the menu and update presenters when this changes.
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
mFormatItemsWidth = widthSize;
mMenu.onItemsChanged(true);
}
final int childCount = getChildCount();
if (mFormatItems && childCount > 0) {
onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
} else {
// Previous measurement at exact format may have set margins - reset them.
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.leftMargin = lp.rightMargin = 0;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
// We already know the width mode is EXACTLY if we're here.
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
final int widthPadding = getPaddingLeft() + getPaddingRight();
final int heightPadding = getPaddingTop() + getPaddingBottom();
final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding,
ViewGroup.LayoutParams.WRAP_CONTENT);
widthSize -= widthPadding;
// Divide the view into cells.
final int cellCount = widthSize / mMinCellSize;
final int cellSizeRemaining = widthSize % mMinCellSize;
if (cellCount == 0) {
// Give up, nothing fits.
setMeasuredDimension(widthSize, 0);
return;
}
final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
int cellsRemaining = cellCount;
int maxChildHeight = 0;
int maxCellsUsed = 0;
int expandableItemCount = 0;
int visibleItemCount = 0;
boolean hasOverflow = false;
// This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
long smallestItemsAt = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) continue;
final boolean isGeneratedItem = child instanceof ActionMenuItemView;
visibleItemCount++;
if (isGeneratedItem) {
// Reset padding for generated menu item views; it may change below
// and views are recycled.
child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.expanded = false;
lp.extraPixels = 0;
lp.cellsUsed = 0;
lp.expandable = false;
lp.leftMargin = 0;
lp.rightMargin = 0;
lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
// Overflow always gets 1 cell. No more, no less.
final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
itemHeightSpec, heightPadding);
maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
if (lp.expandable) expandableItemCount++;
if (lp.isOverflowButton) hasOverflow = true;
cellsRemaining -= cellsUsed;
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
if (cellsUsed == 1) smallestItemsAt |= (1 << i);
}
// When we have overflow and a single expanded (text) item, we want to try centering it
// visually in the available space even though overflow consumes some of it.
final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
// Divide space for remaining cells if we have items that can expand.
// Try distributing whole leftover cells to smaller items first.
boolean needsExpansion = false;
while (expandableItemCount > 0 && cellsRemaining > 0) {
int minCells = Integer.MAX_VALUE;
long minCellsAt = 0; // Bit locations are indices of relevant child views
int minCellsItemCount = 0;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
// Don't try to expand items that shouldn't.
if (!lp.expandable) continue;
// Mark indices of children that can receive an extra cell.
if (lp.cellsUsed < minCells) {
minCells = lp.cellsUsed;
minCellsAt = 1L << i;
minCellsItemCount = 1;
} else if (lp.cellsUsed == minCells) {
minCellsAt |= 1L << i;
minCellsItemCount++;
}
}
// Items that get expanded will always be in the set of smallest items when we're done.
smallestItemsAt |= minCellsAt;
if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
// We have enough cells, all minimum size items will be incremented.
minCells++;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if ((minCellsAt & (1 << i)) == 0) {
// If this item is already at our small item count, mark it for later.
if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
continue;
}
if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
// Add padding to this item such that it centers.
child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
}
lp.cellsUsed++;
lp.expanded = true;
cellsRemaining--;
}
needsExpansion = true;
}
// Divide any space left that wouldn't divide along cell boundaries
// evenly among the smallest items
final boolean singleItem = !hasOverflow && visibleItemCount == 1;
if (cellsRemaining > 0 && smallestItemsAt != 0 &&
(cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
float expandCount = Long.bitCount(smallestItemsAt);
if (!singleItem) {
// The items at the far edges may only expand by half in order to pin to either side.
if ((smallestItemsAt & 1) != 0) {
LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
}
if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
}
}
final int extraPixels = expandCount > 0 ?
(int) (cellsRemaining * cellSize / expandCount) : 0;
for (int i = 0; i < childCount; i++) {
if ((smallestItemsAt & (1 << i)) == 0) continue;
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (child instanceof ActionMenuItemView) {
// If this is one of our views, expand and measure at the larger size.
lp.extraPixels = extraPixels;
lp.expanded = true;
if (i == 0 && !lp.preventEdgeOffset) {
// First item gets part of its new padding pushed out of sight.
// The last item will get this implicitly from layout.
lp.leftMargin = -extraPixels / 2;
}
needsExpansion = true;
} else if (lp.isOverflowButton) {
lp.extraPixels = extraPixels;
lp.expanded = true;
lp.rightMargin = -extraPixels / 2;
needsExpansion = true;
} else {
// If we don't know what it is, give it some margins instead
// and let it center within its space. We still want to pin
// against the edges.
if (i != 0) {
lp.leftMargin = extraPixels / 2;
}
if (i != childCount - 1) {
lp.rightMargin = extraPixels / 2;
}
}
}
cellsRemaining = 0;
}
// Remeasure any items that have had extra space allocated to them.
if (needsExpansion) {
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.expanded) continue;
final int width = lp.cellsUsed * cellSize + lp.extraPixels;
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
itemHeightSpec);
}
}
if (heightMode != MeasureSpec.EXACTLY) {
heightSize = maxChildHeight;
}
setMeasuredDimension(widthSize, heightSize);
}
/**
* Measure a child view to fit within cell-based formatting. The child's width
* will be measured to a whole multiple of cellSize.
*
* <p>Sets the expandable and cellsUsed fields of LayoutParams.
*
* @param child Child to measure
* @param cellSize Size of one cell
* @param cellsRemaining Number of cells remaining that this view can expand to fill
* @param parentHeightMeasureSpec MeasureSpec used by the parent view
* @param parentHeightPadding Padding present in the parent view
* @return Number of cells this child was measured to occupy
*/
static int measureChildForCells(View child, int cellSize, int cellsRemaining,
int parentHeightMeasureSpec, int parentHeightPadding) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
parentHeightPadding;
final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
(ActionMenuItemView) child : null;
final boolean hasText = itemView != null && itemView.hasText();
int cellsUsed = 0;
if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
final int childWidthSpec = MeasureSpec.makeMeasureSpec(
cellSize * cellsRemaining, MeasureSpec.AT_MOST);
child.measure(childWidthSpec, childHeightSpec);
final int measuredWidth = child.getMeasuredWidth();
cellsUsed = measuredWidth / cellSize;
if (measuredWidth % cellSize != 0) cellsUsed++;
if (hasText && cellsUsed < 2) cellsUsed = 2;
}
final boolean expandable = !lp.isOverflowButton && hasText;
lp.expandable = expandable;
lp.cellsUsed = cellsUsed;
final int targetWidth = cellsUsed * cellSize;
child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
childHeightSpec);
return cellsUsed;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!mFormatItems) {
super.onLayout(changed, left, top, right, bottom);
return;
}
final int childCount = getChildCount();
final int midVertical = (bottom - top) / 2;
final int dividerWidth = getDividerWidth();
int overflowWidth = 0;
int nonOverflowCount = 0;
int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
boolean hasOverflow = false;
final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
if (v.getVisibility() == GONE) {
continue;
}
LayoutParams p = (LayoutParams) v.getLayoutParams();
if (p.isOverflowButton) {
overflowWidth = v.getMeasuredWidth();
if (hasSupportDividerBeforeChildAt(i)) {
overflowWidth += dividerWidth;
}
int height = v.getMeasuredHeight();
int r;
int l;
if (isLayoutRtl) {
l = getPaddingLeft() + p.leftMargin;
r = l + overflowWidth;
} else {
r = getWidth() - getPaddingRight() - p.rightMargin;
l = r - overflowWidth;
}
int t = midVertical - (height / 2);
int b = t + height;
v.layout(l, t, r, b);
widthRemaining -= overflowWidth;
hasOverflow = true;
} else {
final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
widthRemaining -= size;
if (hasSupportDividerBeforeChildAt(i)) {
}
nonOverflowCount++;
}
}
if (childCount == 1 && !hasOverflow) {
// Center a single child
final View v = getChildAt(0);
final int width = v.getMeasuredWidth();
final int height = v.getMeasuredHeight();
final int midHorizontal = (right - left) / 2;
final int l = midHorizontal - width / 2;
final int t = midVertical - height / 2;
v.layout(l, t, l + width, t + height);
return;
}
final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
if (isLayoutRtl) {
int startRight = getWidth() - getPaddingRight();
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
if (v.getVisibility() == GONE || lp.isOverflowButton) {
continue;
}
startRight -= lp.rightMargin;
int width = v.getMeasuredWidth();
int height = v.getMeasuredHeight();
int t = midVertical - height / 2;
v.layout(startRight - width, t, startRight, t + height);
startRight -= width + lp.leftMargin + spacerSize;
}
} else {
int startLeft = getPaddingLeft();
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
if (v.getVisibility() == GONE || lp.isOverflowButton) {
continue;
}
startLeft += lp.leftMargin;
int width = v.getMeasuredWidth();
int height = v.getMeasuredHeight();
int t = midVertical - height / 2;
v.layout(startLeft, t, startLeft + width, t + height);
startLeft += width + lp.rightMargin + spacerSize;
}
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
dismissPopupMenus();
}
/**
* Set the icon to use for the overflow button.
*
* @param icon Drawable to set, may be null to clear the icon
*/
public void setOverflowIcon(@Nullable Drawable icon) {
getMenu();
mPresenter.setOverflowIcon(icon);
}
/**
* Return the current drawable used as the overflow icon.
*
* @return The overflow icon drawable
*/
@Nullable
public Drawable getOverflowIcon() {
getMenu();
return mPresenter.getOverflowIcon();
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public boolean isOverflowReserved() {
return mReserveOverflow;
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void setOverflowReserved(boolean reserveOverflow) {
mReserveOverflow = reserveOverflow;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
return params;
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
if (p != null) {
final LayoutParams result = p instanceof LayoutParams
? new LayoutParams((LayoutParams) p)
: new LayoutParams(p);
if (result.gravity <= Gravity.NO_GRAVITY) {
result.gravity = Gravity.CENTER_VERTICAL;
}
return result;
}
return generateDefaultLayoutParams();
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public LayoutParams generateOverflowButtonLayoutParams() {
LayoutParams result = generateDefaultLayoutParams();
result.isOverflowButton = true;
return result;
}
/** @hide */
@Override
@RestrictTo(LIBRARY_GROUP_PREFIX)
public boolean invokeItem(MenuItemImpl item) {
return mMenu.performItemAction(item, 0);
}
/** @hide */
@Override
@RestrictTo(LIBRARY_GROUP_PREFIX)
public int getWindowAnimations() {
return 0;
}
/** @hide */
@Override
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void initialize(MenuBuilder menu) {
mMenu = menu;
}
/**
* Returns the Menu object that this ActionMenuView is currently presenting.
*
* <p>Applications should use this method to obtain the ActionMenuView's Menu object
* and inflate or add content to it as necessary.</p>
*
* @return the Menu presented by this view
*/
public Menu getMenu() {
if (mMenu == null) {
final Context context = getContext();
mMenu = new MenuBuilder(context);
mMenu.setCallback(new MenuBuilderCallback());
mPresenter = new ActionMenuPresenter(context);
mPresenter.setReserveOverflow(true);
mPresenter.setCallback(mActionMenuPresenterCallback != null
? mActionMenuPresenterCallback : new ActionMenuPresenterCallback());
mMenu.addMenuPresenter(mPresenter, mPopupContext);
mPresenter.setMenuView(this);
}
return mMenu;
}
/**
* Must be called before the first call to getMenu()
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
mActionMenuPresenterCallback = pcb;
mMenuBuilderCallback = mcb;
}
/**
* Returns the current menu or null if one has not yet been configured.
* @hide Internal use only for action bar integration
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public MenuBuilder peekMenu() {
return mMenu;
}
/**
* Show the overflow items from the associated menu.
*
* @return true if the menu was able to be shown, false otherwise
*/
public boolean showOverflowMenu() {
return mPresenter != null && mPresenter.showOverflowMenu();
}
/**
* Hide the overflow items from the associated menu.
*
* @return true if the menu was able to be hidden, false otherwise
*/
public boolean hideOverflowMenu() {
return mPresenter != null && mPresenter.hideOverflowMenu();
}
/**
* Check whether the overflow menu is currently showing. This may not reflect
* a pending show operation in progress.
*
* @return true if the overflow menu is currently showing
*/
public boolean isOverflowMenuShowing() {
return mPresenter != null && mPresenter.isOverflowMenuShowing();
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public boolean isOverflowMenuShowPending() {
return mPresenter != null && mPresenter.isOverflowMenuShowPending();
}
/**
* Dismiss any popups associated with this menu view.
*/
public void dismissPopupMenus() {
if (mPresenter != null) {
mPresenter.dismissPopupMenus();
}
}
/**
* @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
protected boolean hasSupportDividerBeforeChildAt(int childIndex) {
if (childIndex == 0) {
return false;
}
final View childBefore = getChildAt(childIndex - 1);
final View child = getChildAt(childIndex);
boolean result = false;
if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
}
if (childIndex > 0 && child instanceof ActionMenuChildView) {
result |= ((ActionMenuChildView) child).needsDividerBefore();
}
return result;
}
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
return false;
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void setExpandedActionViewsExclusive(boolean exclusive) {
mPresenter.setExpandedActionViewsExclusive(exclusive);
}
/**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
*/
public interface OnMenuItemClickListener {
/**
* This method will be invoked when a menu item is clicked if the item itself did
* not already handle the event.
*
* @param item {@link MenuItem} that was clicked
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
*/
public boolean onMenuItemClick(MenuItem item);
}
private class MenuBuilderCallback implements MenuBuilder.Callback {
MenuBuilderCallback() {
}
@Override
public boolean onMenuItemSelected(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
return mOnMenuItemClickListener != null &&
mOnMenuItemClickListener.onMenuItemClick(item);
}
@Override
public void onMenuModeChange(@NonNull MenuBuilder menu) {
if (mMenuBuilderCallback != null) {
mMenuBuilderCallback.onMenuModeChange(menu);
}
}
}
private static class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback {
ActionMenuPresenterCallback() {
}
@Override
public void onCloseMenu(@NonNull MenuBuilder menu, boolean allMenusAreClosing) {
}
@Override
public boolean onOpenSubMenu(@NonNull MenuBuilder subMenu) {
return false;
}
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public interface ActionMenuChildView {
boolean needsDividerBefore();
boolean needsDividerAfter();
}
public static class LayoutParams extends LinearLayoutCompat.LayoutParams {
@ViewDebug.ExportedProperty()
public boolean isOverflowButton;
@ViewDebug.ExportedProperty()
public int cellsUsed;
@ViewDebug.ExportedProperty()
public int extraPixels;
@ViewDebug.ExportedProperty()
public boolean expandable;
@ViewDebug.ExportedProperty()
public boolean preventEdgeOffset;
boolean expanded;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(ViewGroup.LayoutParams other) {
super(other);
}
public LayoutParams(LayoutParams other) {
super((ViewGroup.LayoutParams) other);
isOverflowButton = other.isOverflowButton;
}
public LayoutParams(int width, int height) {
super(width, height);
isOverflowButton = false;
}
LayoutParams(int width, int height, boolean isOverflowButton) {
super(width, height);
this.isOverflowButton = isOverflowButton;
}
}
}