这是一种解决方案,它可以使菜单项左对齐,同时将溢出菜单图标保持在右侧。此解决方案使用工具栏/操作栏的标准实现,但预计操作视图将如何布局,以便它们在工具栏中按我们希望的方式定位。
下面的大部分代码都是您提供的。我已将创建菜单项的 for 循环移动到 onCreateOptionsMenu() 中,这样我就可以利用已经是工具栏菜单结构一部分的 ActionMenuView 而不是添加另一个。
在onCreateOptionsMenu() 中,随着菜单项被放入菜单中,菜单项消耗的空间的运行计数被维护。只要有空间,菜单项就会被标记为“已显示”(MenuItem.SHOW_AS_ACTION_ALWAYS)。如果该项目将侵占为溢出菜单图标保留的区域,则该项目被放置,但作为溢出菜单的目标 (MenuItem.SHOW_AS_ACTION_NEVER)。
在所有视图都放入菜单后,计算松弛空间。这是屏幕上最后一个可见菜单项和溢出图标之间的区域(如果使用溢出)或最后一个可见项和工具栏末尾之间的区域(如果未使用溢出)。
计算松弛空间后,将创建一个Space 小部件并将其放入菜单中。此小部件强制所有其他项目左对齐。
大部分更改已对MainActivity.java 进行,但我可能在 XML 文件中更改了一两件事。为了完整起见,我将它们包括在这里。
这里是一些结果的屏幕截图。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = findViewById(R.id.toolbar);
mToolbar.setTitle("");
setSupportActionBar(mToolbar); // Ensures that onCreateOptionsMenu is called
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
final float density = getResources().getDisplayMetrics().density;
final int overflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = mToolbar.getWidth() - overflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, mToolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
final String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
final MenuItem menuItem = menu
.add(text).setActionView(menuItemView).setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@SuppressLint("MissingPermission")
@Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width.
slackWidth = widthLeftToFill + overflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
final MenuItem menuItem = menu.add("").setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
private static final int OVERFLOW_CELL_WIDTH = 40; // dips
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layoutDirection="ltr"
android:padding="0px"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:contentInsetEnd="0px"
app:contentInsetEndWithActions="0px"
app:contentInsetLeft="0px"
app:contentInsetRight="0px"
app:contentInsetStart="0px"
app:contentInsetStartWithNavigation="0px"
app:logo="@null"
app:title="@null"
app:titleMargin="0px"
app:titleTextColor="#757575"
tools:ignore="UnusedAttribute"
tools:title="toolbar">
</android.support.v7.widget.Toolbar>
</FrameLayout>
action_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="8dp">
<ImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
android:scaleType="center"
tools:src="@android:drawable/sym_def_app_icon" />
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
android:layout_marginLeft="6dp"
android:layout_marginStart="6dp"
android:gravity="center"
android:textColor="#c2555555"
android:textSize="15sp"
tools:text="text" />
</LinearLayout>
更新:要使用工具栏而不将其设置为操作栏,请添加一个全局布局侦听器以等待工具栏设置完成。
MainActivity.java - 使用全局布局监听器而不是操作栏
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = findViewById(R.id.toolbar);
mToolbar.setTitle("");
// setSupportActionBar(mToolbar); // Ensures that onCreateOptionsMenu is called
mToolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mToolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
setupMenu(mToolbar.getMenu());
}
});
}
public boolean setupMenu(Menu menu) {
final float density = getResources().getDisplayMetrics().density;
int mOverflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = mToolbar.getWidth() - mOverflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, mToolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text ,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
final MenuItem menuItem = menu
.add(text).setActionView(menuItemView).setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@SuppressLint("MissingPermission")
@Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width.
slackWidth = widthLeftToFill + mOverflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
final MenuItem menuItem = menu.add("").setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
private static final int OVERFLOW_CELL_WIDTH = 40; // dips
}
以下示例应用程序通过引入方法notifyMenuItemsChanged 将菜单创建与菜单左对齐分开。在应用程序中,单击按钮以删除位置 1 的菜单项。
这段代码与上面基本相同,但Space 小部件需要一个id,以便在菜单更改时可以将其删除以重新添加。
MainActivity.Java:示例应用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("");
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Menu menu = toolbar.getMenu();
// Remove item at position 1 on click of button.
if (menu.size() > 1) {
menu.removeItem(menu.getItem(1).getItemId());
notifyMenuItemsChanged(toolbar);
}
}
});
toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
setupMenu(toolbar);
}
});
}
private void setupMenu(Toolbar toolbar) {
Menu menu = toolbar.getMenu();
// Since we are resetting the menu, get rid of what may have been placed there before.
menu.clear();
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, toolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text ,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
menu.add(Menu.NONE, View.generateViewId(), Menu.NONE, text)
.setActionView(menuItemView)
.setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@SuppressLint("MissingPermission")
@Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
}
// Now take the menu and left-justify it.
notifyMenuItemsChanged(toolbar);
}
/**
* Call this routine whenever the Toolbar menu changes. Take all action views and
* left-justify those that fit on the screen. Force to overflow those that don't.
*
* @param toolbar The Toolbar that holds the menu.
*/
private void notifyMenuItemsChanged(Toolbar toolbar) {
final int OVERFLOW_CELL_WIDTH = 40; // dips
final Menu menu = toolbar.getMenu();
final float density = getResources().getDisplayMetrics().density;
final int mOverflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = toolbar.getWidth() - mOverflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
MenuItem menuItem;
// Index of the spacer that will be removed/replaced.
int spaceIndex = View.NO_ID;
if (menu.size() == 0) {
return;
}
// Examine each MenuItemView to determine if it will fit on the screen. If it can,
// set its MenuItem to always show; otherwise, set the MenuItem to never show.
for (int i = 0; i < menu.size(); i++) {
menuItem = menu.getItem(i);
View menuItemView = menuItem.getActionView();
if (menuItemView instanceof Space) {
spaceIndex = menuItem.getItemId();
continue;
}
if (!menuItem.isVisible()) {
continue;
}
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (spaceIndex != View.NO_ID) {
// Assume that this is our spacer. It may need to change size, so eliminate it for now.
menu.removeItem(spaceIndex);
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width, so there is no overflow.
slackWidth = widthLeftToFill + mOverflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
// Need an if for the spacer so it can be deleted later if the menu is modified.
// Need API 17+ for generateViewId().
menuItem = menu.add(Menu.NONE, View.generateViewId(), Menu.NONE, "")
.setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
}
}
activity_main.xml:示例应用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Click the button to add/remove item #1 from the menu."/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Click to modify menu" />
</LinearLayout>