Long press on Android menu item?

Viewed 6247

Is it possible to perform a long press on a menu item to perform a certain action? I'm trying to have a menu dropdown when I long press a certain item.

Here's the menu xml

<item
    android:id="@+id/add_item"
    android:icon="@drawable/ic_add_black_24dp"
    android:showAsAction="ifRoom"
    android:title="Add Item">
</item>

<item
    android:id="@+id/open_menu"
    android:icon="@drawable/ic_menu_black_24dp"
    android:showAsAction="ifRoom|withText"
    android:title="Open Menu">
</item>

I want to have a dropdown appear when the first item is long pressed

4 Answers

Easy with Kotlin (and keeping the same style as other icons):

fun MenuItem.onMenuItemLongClickListener(menu: Menu, function: () -> (Unit)) {
    setActionView(R.layout.view_action_button)
    actionView.find<ImageButton>(R.id.item).setImageDrawable(icon)
    actionView.setOnLongClickListener {
        function()
        true }
    actionView.setOnClickListener { menu.performIdentifierAction(itemId, 0) }
}

view_action_button.xml:

<?xml version="1.0" encoding="utf-8"?>

<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item"
    style="?android:attr/actionButtonStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Usage:

menu.findItem(R.id.your_item).onMenuItemLongClickListener(menu) { anyFunction() }

Just change R.id.your_item and anyFunction()

Remie's answer is perfect for me. Thanks!

In my case, implemented into Fragment with MenuHost.

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val menuHost: MenuHost = requireActivity() as MenuHost
        menuHost.addMenuProvider(
            object : MenuProvider {
                override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                    menuInflater.inflate(R.menu.your_menu, menu)
                    menu.findItem(R.id.your_item_id_in_your_menu_resource).setOnTextTapEventListener(
                        onTap = { /** do something **/ },
                        onLongTap = { /** do something **/ },
                    )
                }

                override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
                    return when (menuItem.itemId) {
                        // Not required because it was overwritten in onTap above. 
                        // When you call `menu.performIdentifierAction` in `actionView.setOnClickListener, you can handle here, maybe.
                        // R.id.your_item_id_in_your_menu_resource -> { }
                        android.R.id.home -> {
                            findNavController().navigateUp()
                            true
                        }
                        else -> false
                    }
                }
            },
            viewLifecycleOwner
        )


    }

Here is my extension of MenuItem inspired by Remie's answer.

import android.view.MenuItem
import com.google.android.material.button.MaterialButton


fun MenuItem.setOnTextTapEventListener(
    onTap: () -> Unit,
    onLongTap: () -> Unit,
) {
    setActionView(R.layout.your_action_view)
    // It's enough to use `this.title` in "my" case.
    actionView.findViewById<MaterialButton>(R.id.menu_action_text_button).text = this.title 
    actionView.setOnLongClickListener {
        onLongTap.invoke()
        true
    }
    actionView.setOnClickListener {
        onTap.invoke()
    }
}
Related