如何使工具栏仅包含操作项,并为其使用尽可能多的空间?

时间:2019-11-13 13:28:28

标签: android android-toolbar androidx

背景

我正在尝试让工具栏仅显示操作项。无题。我希望它们尽可能地填充空间,如果没有空间,可以在溢出菜单项中放置它们。

这可以包括各种动作项(包括具有自定义动作视图的动作项)。

问题

由于某种原因,即使我已经在工具栏上设置了所有填充/间距,并且即使我为每个操作项都设置了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编写一些代码,但是它有太多的函数,只能从库中调用并依赖于此。我找不到使它看起来与工具栏相似的方法,并且在某些情况下,它还存在一些奇怪的单击效果问题。

问题

如果有足够的空间,如何设置工具栏以使所有项目都可以自己查看;如果有些项目无法容纳,则将其置于溢出菜单中?

有没有一种方法可以覆盖工具栏的行为?这种行为甚至写在哪里?

1 个答案:

答案 0 :(得分:2)

好的,我有解决的办法,但是其中有一些重要的注意事项和假设。我认为,通过一些调整,它也可以解决我过去问过的类似问题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