■ 概要
onOptionsItemSelected() は、もっと簡易な呼び出しがあっても良さそうな気がするので、試しに作ってみたという話。
■ 仕様
本家でこんな感じの説明がされているので、仕様を以下のようにしてみた:
- 呼び出し方法
- itemId と、それに対応する処理を指定する。
- 実行内容
- 指定された itemId が対象だった場合は、対応する処理を実行し、問答無用で true を返し、super は呼ばない。1
- 指定された itemId が対象でなかった場合は、super を呼び出す。
■ 例
☆ 従来の書き方
// For 1 menu item.
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
android.R.id.home -> {
finish()
true
}
else -> super.onOptionsItemSelected(item)
}
// For multiple menu items.
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
android.R.id.home -> {
finish()
true
}
R.id.menu_foo -> {
foo("bar")
true
}
else -> super.onOptionsItemSelected(item)
☆ 簡略化した場合
おまじないは結構長いが2、本質的な部分はほぼ宣言的に書けるようになる。
// For 1 menu item.
override fun onOptionsItemSelected(item: MenuItem): Boolean = onOptionsItemSelected(item, { super.onOptionsItemSelected(item) },
android.R.id.home, ::finish
)
// For multiple menu items.
override fun onOptionsItemSelected(item: MenuItem): Boolean = onOptionsItemSelected(item, { super.onOptionsItemSelected(item) }, mapOf(
android.R.id.home to ::finish,
R.id.menu_foo to { foo("bar") }
))
java から呼ぶ場合の例:
// For multiple menu items.
public boolean onOptionsItemSelected(MenuItem item) {
return onOptionsItemSelectedForJava(item, () -> super.onOptionsItemSelected(item), ImmutableMap.of(
android.R.id.home, this::finish,
R.id.menu_foo , () -> foo("bar")
));
}
■ コード
Activity も Fragment も同一コードで対応できます。3
/**
* A utility extension function for [Activity.onOptionsItemSelected] and [Fragment.onOptionsItemSelected].
*
* The following process will be executed:
* - If [targetItemId] is selected, run [onSelected] and return true.
* - If [targetItemId] is not selected, call <code>super.onOptionsItemSelected(item)</code> and return the result.
*/
fun onOptionsItemSelected(item: MenuItem, callSuper: () -> Boolean, targetItemId: Int, onSelected: Runnable): Boolean =
when (item.itemId) {
targetItemId -> {
onSelected.run()
true
}
else -> callSuper()
}
/**
* @see [onOptionsItemSelected]
*/
fun onOptionsItemSelected(item: MenuItem, callSuper: () -> Boolean, targetItemIdAndOnSelectedMap: Map<Int, () -> Unit>): Boolean =
targetItemIdAndOnSelectedMap[item.itemId]?.run { invoke(); true } ?: callSuper()
/**
* @see [onOptionsItemSelected]
*/
fun onOptionsItemSelectedForJava(item: MenuItem, callSuper: () -> Boolean, targetItemIdAndOnSelectedMap: Map<Int, Runnable>): Boolean =
targetItemIdAndOnSelectedMap[item.itemId]?.run { run(); true } ?: callSuper()
■ 考察
- 対象の menuItem が存在した際に super を呼ばずに true を返す方式は、親クラスの item 処理を完全に上書きしてしまうかつ Fragment に item を伝播しないと割り切るということになる。しかしそもそも、同じ menuItem に対する処理を、継承や Fragment への伝播で拡散させるのは設計的に複雑すぎる気がするので、割り切りはアリな気がする。
- super の呼び出しは caller sensitive なので、minSdk を API26 以上にできるまでは我慢ですかね。
ということで、ひとまずおしまい。