List popup window menusの実装方法を紹介します。
ターゲットViewをクリックすることで、アンカーViewの下に表示されるメニューです。
※表示例はターゲット=アンカーViewになっています。

メニューの実態はApi 11で追加されたListPopupWindowクラスです。
ListPopupWindowはPopupWindow(パネルが浮き上がるような表示)とListView(アイテムを一覧表示)を組み合わせたもので、メニュー表示に特化して開発されたクラスではありません。
しかし、メニューと同等な振る舞いが期待できます。
Material Design ComponentsのMenusカテゴリに属し、メニューの表現方法の一つとして紹介されています。
アイテムを一覧表示する部分がListViewなので、メニューアイテムの指定にandroid.widget.ListAdapterを用いる点が大きな特徴です。
※現在(’21.08)ListViewはレガシー扱い、単独での使用は推奨されません。
メニューの実装
実装はListViewそのものです。
サンプルの実装
配列でメニューのアイテムを用意したので、ListAdapterにArrayAdapterを用いています。
メニューのアイテムをListAdapter経由で、ListPopupWindow内のListViewへ登録するだけです。
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?attr/textAppearanceSubtitle1" />
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
// ViewのクリックでList popup window menusを起動
findViewById<Button>(R.id.btnSLT).setOnClickListener {
openMenu(it)
}
findViewById<Button>(R.id.btnSRB).setOnClickListener {
openMenu(it)
}
}
fun openMenu(anchor: View) {
val _items = arrayOf("Item 1", "Item 2", "Item 3")
val _adapter = ArrayAdapter(this, R.layout.menu_item, _items)
val _popup = ListPopupWindow(this)
_popup.setAdapter(_adapter)
_popup.setOnItemClickListener { parent, view, position, id ->
_popup.dismiss() // アイテムのクリックで自身を閉じる
}
_popup.anchorView = anchor
_popup.show()
}
}
注意点として、メニューのアイテムがクリックされた時に、自身を閉じる必要があります。ListPopupWindow#dissmiss( )が閉じる処理を行ってい部分です。
カスタマイズは他のListAdapter(Simple/CursorAdapterなど)を使ったり、アイテムのレイアウトを変更したり、ListViewと同じ方法が使えそうです。
サンプルの実行結果
図は上記で実装したList popup window menusの実行結果です。

ターゲットViewをクリックすると、アンカーViewの下に左上を起点としたドロップダウンメニューが開いて、アイテムが現れます。ちなみに、サンプルはターゲット=アンカーViewになっています。
ドロップダウンメニューが画面の外へ出てしまうときは、メニュー全体が表示できる位置へ移動されます。
アイテムのクリックイベント処理
AdapterView.OnItemClickListenerリスナーを実装し、ListPopupWindowオブジェクトへ登録します。
アイテムのクリックが行われるとAdapterView.OnItemClickListenerにコールバックが返ってきます。
引数のpositionがクリックされたアイテムの位置を示しています。後は位置に基づいて処理を行います(サンプルはToastを発行)。
アイテムを一覧表示している部分はListViewなので、イベントの処理方法はListViewと全く同じです。
class MainActivity : AppCompatActivity() {
...
fun openMenu(anchor: View) {
val _items = arrayOf("Item 1", "Item 2", "Item 3")
val _adapter = ArrayAdapter(this, R.layout.menu_item, _items)
val _popup = ListPopupWindow(this)
_popup.anchorView = anchor
_popup.setAdapter(_adapter)
_popup.setOnItemClickListener { parent, view, position, id ->
val _item = parent.getItemAtPosition(position) as String
when(position) {
0 -> { // Item 1の処理
Toast.makeText(parent.context, _item, Toast.LENGTH_LONG)
.show() }
1 -> { // Item 2の処理
Toast.makeText(parent.context, _item, Toast.LENGTH_LONG)
.show() }
2 -> { // Item 3の処理
Toast.makeText(parent.context, _item, Toast.LENGTH_LONG)
.show() }
else -> {}
}
_popup.dismiss()
}
_popup.show()
}
}
関連記事:
