Android Use Sub Menu Item as Spinner/Dropdown/Combobox in AppBar

March 19, 2019

If you allow user to make a selection (e.g. Pending, Approved or Rejected) in AppBar to update the list in RecyclerView.

  • You can add a Spinner into Toolbar
  • Or you can use menu with sub menu items to simulate UI of a Spinner

This article will use menu with sub menu items to simulate UI of a Spinner

Create a menu with sub menus: res/menu/testlist.xml

NOTE: Use app:showAsAction="always" to ensure menu is always shown.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_status"
        android:title="Status"
        app:showAsAction="always">
        <menu>
            <item
                android:id="@+id/action_status_pending"
                android:title="Pending"
                />

            <item
                android:id="@+id/action_status_approved"
                android:title="Approved"
                />

            <item
                android:id="@+id/action_status_rejected"
                android:title="Rejected"
                />
        </menu>
    </item>
</menu>

Use ViewModel and SingleLiveEvent/LiveData to store selected menu status.

class TestListViewModel: ViewModel() {
    internal var statusEvent = SingleLiveEvent<Int>()
}

Activity

class Approval {
    companion object {
        const val APPROVED = 1
        const val PENDING = 0
        const val REJECTED = -1
    }
}

class TestListActivity : AppCompatActivity() {

    private lateinit var viewModel: TestListViewModel

      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.testlist)
        setSupportActionBar(toolbar)

        viewModel = ViewModelProviders.of(this).get(TestListViewModel::class.java)

        initUi()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.adminquotelist, menu)

        val statusMenuItem = menu.findItem(R.id.action_status)

        // update menu item title based on selected sub menu item
        statusMenuItem.title = when(viewModel.statusEvent.value) {
            Approval.APPROVED -> "Approved"
            Approval.REJECTED -> "Rejected"
            Approval.PENDING -> "Pending"
            else -> "Unknown"
        }

        return super.onCreateOptionsMenu(menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // sub menu selection trigger LiveData
        return when(item.itemId) {
            R.id.action_status_approved -> {
                viewModel.statusEvent.value = Approval.APPROVED
                true
            }
            R.id.action_status_pending -> {
                viewModel.statusEvent.value = Approval.PENDING
                true
            }
            R.id.action_status_rejected -> {
                viewModel.statusEvent.value = Approval.REJECTED
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    private fun initUi() {
        viewModel.statusEvent.observe(this, Observer { status ->
            // update menu title with selected sub menu item
            invalidateOptionsMenu()

            // do something
        })

        // initialize
        viewModel.statusEvent.value = Approval.PENDING
    }    
}
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.