android - 如何让工具栏只包含操作项,并尽可能多地利用其空间?

标签 android android-toolbar androidx

背景

我试图让工具栏只显示操作项。无题。我希望它们尽可能地填充空间,如果没有空间,请将它们放在溢出菜单项上。

这可以包括所有类型的操作项(包括具有自定义操作 View 的操作项)。

问题

出于某种原因,即使我在工具栏上设置了所有内边距/间距,并且为每个操作项设置了 SHOW_AS_ACTION_IF_ROOM,它也不会发生。

即使还有很多空间,它最多显示 2 个项目:

enter image description here

我尝试过的

我尝试设置工具栏的各种填充/间距重置:

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
        android:padding="0px" app:contentInsetEnd="0px" app:contentInsetEndWithActions="0px" app:contentInsetLeft="0px"
        app:contentInsetRight="0px" app:contentInsetStart="0px" app:contentInsetStartWithNavigation="0px"
        app:layout_constraintEnd_toEndOf="parent" app:logo="@null" app:title="@null" app:titleMargin="0px"
        tools:background="#ffcc0000" />

为了测试它,我使用了:

        for (i in 0..10) 
            toolbar.menu.add("item $i").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)

遗憾的是,正如我所说,这没有帮助。

我还尝试使用 ActionMenuView 而不是 Toolbar 编写一些代码,但它有太多仅从库调用并依赖于它的函数。我找不到办法让它看起来与工具栏相似,而且在某些情况下它还存在一些奇怪的点击效果问题。

问题

如何设置工具栏,让所有项目都能在有足够空间的情况下自行查看,如果有些项目放不下,则让它们显示在溢出菜单中?

有没有办法覆盖工具栏的行为?这种行为到底写在哪里?

最佳答案

好吧,我有办法解决这个问题,但它有一些重要的注释和假设。我认为通过一些调整,它也可以解决我过去问过的类似问题 to have the action items on the left 。全部写在代码中:

ToolbarAdjuster.kt

object ToolbarAdjuster {
    /**
     * a special behavior for Toolbar, to put all of its menu items that can fit - to actually fit, and the rest to be in overflow menu
     * Important notes and assumptions:
     * 1. You should call it each time you reset the menu items
     * 2. Toolbar has a size. You can use in `onCreateOptionsMenu` if it's the actionBar, or `doOnPreDraw` otherwise.
     * 3. Only action items that have actionView can be on the toolbar. The rest should always be in the overflow menu
     * 4. Toolbar should consist only of action items. No spacing, no padding, no title, ... Otherwise the calculation will be incorrect.
     * Meaning:
     * android:padding="0px" app:contentInsetEnd="0px" app:contentInsetEndWithActions="0px" app:contentInsetLeft="0px"
     * app:contentInsetRight="0px" app:contentInsetStart="0px" app:contentInsetStartWithNavigation="0px"
     * app:layout_constraintEnd_toEndOf="parent" app:logo="@null" app:title="@null" app:titleMargin="0px"
     */
    fun adjustToolbar(context: Context, toolbar: Toolbar, menu: Menu) {
        val toolbarWidth = toolbar.width
        val indexToWidthMap = HashMap<Int, Int>()
        val menuItemsCount = menu.size
        var sizeSoFar = 0
        var foundNeedForOverflowItem = false
        //first find if we will need an overflow menu item:
        for (i in 0 until menuItemsCount) {
            val menuItem = menu[i]
            val menuItemView = menuItem.actionView
            if (!menuItem.isVisible)
                continue
            if (menuItemView == null) {
                foundNeedForOverflowItem = true
                continue
            }
            menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
            val menuItemViewWidth = menuItemView.measuredWidth
            indexToWidthMap[i] = menuItemViewWidth
            if (menuItemViewWidth + sizeSoFar > toolbarWidth) {
                foundNeedForOverflowItem = true
                break
            }
            sizeSoFar += menuItemViewWidth
        }
        //now we know if we need an overflow menu or not, so go over again, and set each menu item to how it will be shown
        var spaceLeft = if (foundNeedForOverflowItem) toolbarWidth - getDefaultOverflowWidth(context) else toolbarWidth
        for (i in 0 until menuItemsCount) {
            val menuItem = menu[i]
            val menuItemView = menuItem.actionView
            if (!menuItem.isVisible)
                continue
            if (menuItemView == null || spaceLeft <= 0) {
                menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
                continue
            }
            val measuredAItemViewWidth = indexToWidthMap[i]!!
            if (spaceLeft - measuredAItemViewWidth < 0) {
                //this item's view can't fit into the space we have left, so none of the next ones will fit
                spaceLeft = 0
                menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
            } else {
                //this item's view can still fit
                spaceLeft -= measuredAItemViewWidth
                menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
            }
        }
    }

    private fun getDefaultOverflowWidth(context: Context): Int {
        val overflowMenuButton = OverflowMenuButton(context)
        overflowMenuButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
        val overflowCellSize = overflowMenuButton.measuredWidth
        return if (overflowCellSize > 0) overflowCellSize else TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, context.resources.displayMetrics).toInt()
    }

    /**fake view, copied from ActionMenuPresenter, which is used only to get its width*/
    private class OverflowMenuButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.actionOverflowButtonStyle) : AppCompatImageView(context, attrs, defStyleAttr)
}

用法示例:

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = null
        setSupportActionBar(toolbar)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        for (i in 0..10) {
            val actionView = LayoutInflater.from(this).inflate(R.layout.action_item, toolbar, false)
            actionView.textView.text = "item $i"
            menu.add("item $i").setActionView(actionView).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
        }
        for (i in 0..10)
            menu.add("item $i").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
        ToolbarAdjuster.adjustToolbar(this, toolbar, menu)
        return super.onCreateOptionsMenu(menu)
    }

}

action_item.xml

<androidx.appcompat.widget.AppCompatTextView
    android:id="@+id/textView" 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="?attr/selectableItemBackground" android:clickable="true" android:drawableLeft="@android:drawable/ic_dialog_email"
    android:drawablePadding="8dp" android:focusable="true" android:focusableInTouchMode="false"
    android:gravity="center_vertical|start" android:paddingLeft="15dp" android:paddingRight="15dp"
    android:text="text" tools:background="#11ff0000"
    tools:layout_gravity="center" tools:layout_height="?attr/actionBarSize" />

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">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
        android:padding="0px" app:contentInsetEnd="0px" app:contentInsetEndWithActions="0px" app:contentInsetLeft="0px"
        app:contentInsetRight="0px" app:contentInsetStart="0px" app:contentInsetStartWithNavigation="0px"
        app:layout_constraintEnd_toEndOf="parent" app:logo="@null" app:title="@null" app:titleMargin="0px"
        tools:background="#ffcc0000" />

</FrameLayout>

结果如下:

enter image description here

关于android - 如何让工具栏只包含操作项,并尽可能多地利用其空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58838220/

相关文章:

android - 在 Android 上向现有设备管理员添加新的使用策略

android - 从谷歌云打印 API 打印时设置纸张尺寸 A4

android - Icecast:在 Android 上开始播放时暂停

android - <include> FrameLayout 和 App/Action Bar 碰撞

android - ViewPager 滚动布局行为不适用于工具栏

android - 在 android listview 中使用 picasso 加载自定义位图图像

android - 如何在 Pre-Lollipop 版本中更改工具栏文本颜色

android - 如何在androidx中访问RecyclerView?

android - 找不到满足版本限制的 'androidx.arch.core:core-common' 版本

java - 我的 Android 应用程序在从 Playstore 下载时崩溃,但当我从 Android Studio 运行到任何设备时它可以正常工作