如何完全模仿工具栏中的 Action item 视图,以进行自定义?

How to fully mimic Action item view in the toolbar, for a customized one?

背景

我有一个不太标准的行动项目。它有自己的布局,因为我没能为它创建一个漂亮的 Drawable(关于它的文章)。

基本上,它只是一个 TextView,其背景包裹着它显示的短文本。

问题

遗憾的是我找不到一种方法来完全模仿它看起来像正常的:

  1. 涟漪效果(可能还有旧版本的简单点击效果)没有相同的颜色和大小。
  2. 长按没有toast
  3. 也许还有其他我没有注意到的事情?

以下是展示差异的屏幕截图:

新的非标准行动项目:

原生操作项:

我试过的

对于波纹效果的颜色,我想我可以使用"colorControlHighlight",但我无法正确找出默认使用的颜色。我查看了支持库的 "values.xml" 文件,注意到它是 "ripple_material_dark" 颜色(或 "ripple_material_light",以防工具栏应该是白色的),但这似乎是有点像黑客。

不确定大小,但查看布局检查器,我认为视图有填充:

我还注意到工具栏的视图 class 名称是 ActionMenuItemView 。我试着查看它的代码(也可能在网上可用,在这里),但没有注意到那里提到的任何关于背景的内容。对于填充,我认为它只是试图将图标放在中间:

无论如何,这是 POC 的当前代码:

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var goToTodayView: View
    lateinit var goToTodayTextView: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        goToTodayView = LayoutInflater.from(this).inflate(R.layout.go_to_today_action_item, toolbar, false)
        goToTodayTextView = goToTodayView.goToTodayTextView
        goToTodayTextView.setBackgroundDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_backtodate))
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menu.add("goToToday").setActionView(goToTodayView).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
        menu.add("asd").setIcon(R.drawable.abc_ic_menu_copy_mtrl_am_alpha).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) //for comparison
        return super.onCreateOptionsMenu(menu)
    }
}

go_to_today_action_item.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
    android:background="?attr/selectableItemBackgroundBorderless" android:backgroundTint="#fff" android:clickable="true"
    android:focusable="true">

    <TextView
        android:id="@+id/goToTodayTextView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="center" android:gravity="center" android:text="1"
        android:textColor="#fff" android:textSize="12dp" tools:background="@drawable/ic_backtodate" tools:layout_gravity="center"/>
</FrameLayout>

问题

  1. 如何设置与普通操作项相同的背景?
  2. 如何把吐司当成普通的动作项?
  3. 普通操作项和我用布局设置的操作项之间是否还有其他不同之处?
  4. 换句话说,是否可以完全模仿原生操作项?也许以某种方式使用 ActionMenuItemView class?也许是与我尝试过的非常不同的解决方案?

编辑:对于行动项目的背景,我把它做得有点像原来的,但还是不一样。点击效果似乎有点不同,我还没有找到如何在长按它时显示操作项的吐司。结果如下:

无论如何,这是我所做的:

go_to_today_action_item.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:clickable="true"
    android:focusable="true">

    <ImageView
        android:layout_width="@dimen/action_item_background_size"
        android:layout_height="@dimen/action_item_background_size" android:layout_gravity="center"
        android:background="@drawable/action_item_selector" android:duplicateParentState="true"/>

    <TextView
        android:id="@+id/goToTodayTextView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="center" android:gravity="center" android:text="31" android:textColor="#fff"
        android:textSize="12dp" tools:background="@drawable/ic_backtodate" tools:layout_gravity="center"/>
</FrameLayout>

drawable/action_item_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/clicking_semi_white_ripple_color"/>
        </shape>
    </item>
    <item android:drawable="@android:color/transparent"/>
</selector>

drawable-v21/action_item_selector.xml

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/clicking_semi_white_ripple_color">
    <item android:id="@android:id/mask">
        <shape android:shape="oval">
            <solid android:color="@android:color/white"/>
        </shape>
    </item>
</ripple>

colors.xml

<color name="clicking_semi_white_ripple_color">#33ffffff</color>

values/dimens.xml

<dimen name="action_item_background_size">48dp</dimen>

values-v21/dimens.xml

<dimen name="action_item_background_size">40dp</dimen>

以下代码将使用您的布局来填充日期的标准菜单项。作为标准菜单项,它将展示您正在寻找的所有特征,并且应该与将来对菜单项行为的更改兼容。

基本概念是用装箱日期扩充布局,并使用其绘图缓存创建一个位图,然后用作菜单项图标的可绘制对象。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private var goToTodayView: View? = null
    private var goToTodayTextView: TextView? = null
    private val textDrawable: TextDrawable? = null
    private var mOptionsMenu: Menu? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(findViewById<View>(R.id.toolbar) as Toolbar)
        testWithCustomViews()
    }

    private fun updateDateView() {
        if (mOptionsMenu == null)
            return
        goToTodayView!!.invalidate()
        goToTodayView!!.buildDrawingCache()
        val bmp = Bitmap.createBitmap(goToTodayView!!.drawingCache)
        val d = BitmapDrawable(resources, bmp)
        mOptionsMenu!!.getItem(0).icon = d
    }

    private fun testWithCustomViews() {
        val toolbar = findViewById<View>(R.id.toolbar) as Toolbar

        goToTodayView = LayoutInflater.from(this).inflate(R.layout.go_to_today_action_item, toolbar, false)
        goToTodayView!!.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
        goToTodayView!!.layout(0, 0, goToTodayView!!.measuredWidth, goToTodayView!!.measuredHeight)
        goToTodayTextView = goToTodayView!!.findViewById(R.id.goToTodayTextView)
        goToTodayTextView!!.setBackgroundDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_backtodate))
        goToTodayView!!.isDrawingCacheEnabled = true
        val handler = Handler()
        val runnable = object : Runnable {
            internal var i = 0

            override fun run() {
                if (isFinishing || isDestroyed)
                    return
                goToTodayTextView!!.text = (i + 1).toString()
                i = (i + 1) % 31
                updateDateView()
                handler.postDelayed(this, 1000)
            }
        }
        runnable.run()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        mOptionsMenu = menu
        menu.add("goToToday").setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
        updateDateView()
        menu.add("asd").setIcon(R.drawable.abc_ic_menu_copy_mtrl_am_alpha).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
        return super.onCreateOptionsMenu(menu)
    }
}

由于 go_to_today_action_item.xml 现在不需要额外的视图来模仿标准菜单项行为,因此可以将其剥离。事实上,菜单项图标在没有调整的情况下显得太小了。将 go_to_today_action_item.xml 更改为以下内容:

<TextView
    android:id="@+id/goToTodayTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@drawable/ic_backtodate"
    android:gravity="center"
    android:text="31"
    android:textColor="#fff"
    android:textSize="12dp"
    tools:layout_gravity="center" />