意外と簡単なので簡単に知りたい人向け。
アクションバーの新しいバージョンのToolbarをいろいろ試してみると、Android Developerの説明の内容がアクションバーと混在していたり、やり方の説明が詳しくないので、いろいろつまづくことが多い。
まず、Toolbarはandroid.widget.Toolbarとandroid.support.v7.widget.Toolbarの二つがあることに注意が必要。さらにツールバーに格納するwidgetもandroid.widgetにあるものと、android.support.v7.widgetにあるものと二つがあるので注意が必要。それぞれ別のものにキャストしようとするとアプリが落ちるので、しっかりと指定しておく必要がある。
Toolbarは簡単に設置でき、通常のUI部品のようにアクセスできる。Android StudioのレイアウトデザイナーからToolbarを設置するだけ。具体的には以下のように設置できる。
メニュー関係のレイアウトxmlファイルを作る場合はAndroid Studioの機能で作ると様々な設定をしてくれたり、標準的なテンプレートを表示してくれるので任せたほうが楽。
具体的なやり方はAndroid Studioのプロジェクトツリービューのリソースを保存するresディレクトリの上で右クリックし、New→Android resource directoryを選択するとウィザードが開くのでResource Typeでmenuを選択するとメニュー関係のレイアウトファイルを作るディレクトリが作成される。さらに、作成したmenuディレクトリから右クリックしNew→Manu resource fileを選択することでmenuのレイアウトファイルを作成することができる。
今回は下記のように検索ツールバー(SearchView)、スイッチ(Switch)、メニューアイテムの3つを設置した。
これはAndroid Studioのデザイナから設置できるが注意が必要。どの種類のアイテムを設置するかはactionViewClassかactionLayoutで対象を指定することで設定するわけだが、switchはそのままだとandroid.support.v7.widget.SwitchCompatのものが設置されないのでキャストするアプリが落ちるので注意が必要。どのウィジェットを設置するかはactionViewClassで自分で明示的に設置するようにしよう。
また、ここではshowActionでツールバー内に表示するか、ツールバーの右側のメニュー(「・・・」ボタン)にまとめる(オーバーフロー)かを設定できる。alwaysで常にツールバーに表示し、neverで常にオーバーフロー表示になる。ifRoomは表示できるスペースがあればツールバーに表示させ、そうでなければオーバーフロー表示になる。titleはオーバーフロー時に表示される文字になるので必ず記入が必要でないとアプリが落ちる。
カスタムビューとメニュー以外は以下のようにして設定することができ、さらにナビゲーションアイコンをクリックしたときにコールバックすることができる。
Toolbarにはitemがクリックされたときに発生するイベントを取得できるので、以下のようにitem.itemIdでメニューアイテムに設定したidからメニューを区別できるので、それぞれ処理を振り分けてあげればいい。
するとSearchViewのインスタンスにアクセスすることができるので、SearchView.OnQueryTextListenerを継承したクラスを渡してあげればいい。継承させて参照を渡すか、下記のように無名クラスを使ってインラインで記述するかはお好み。
別クラスを作って継承させるとスコープが異なってしまうのでアクティビティ内にinner classをネストして作るか、無名クラスを使うか、activityにインターフェースとして継承させてメソッドをoverrideするのがいいかも?
アクションバーの新しいバージョンのToolbarをいろいろ試してみると、Android Developerの説明の内容がアクションバーと混在していたり、やり方の説明が詳しくないので、いろいろつまづくことが多い。
まず、Toolbarはandroid.widget.Toolbarとandroid.support.v7.widget.Toolbarの二つがあることに注意が必要。さらにツールバーに格納するwidgetもandroid.widgetにあるものと、android.support.v7.widgetにあるものと二つがあるので注意が必要。それぞれ別のものにキャストしようとするとアプリが落ちるので、しっかりと指定しておく必要がある。
Toolbarは簡単に設置でき、通常のUI部品のようにアクセスできる。Android StudioのレイアウトデザイナーからToolbarを設置するだけ。具体的には以下のように設置できる。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/tbTestToolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.Toolbar>
</android.support.constraint.ConstraintLayout>
そのままではこれまでのアクションバーが表示されたままになってしまうので、マニフェストファイルでテーマを設定し、アクションバーがないテーマを設定しておく。
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
こまかな設定をする場合は、自分で例によってレイアウトファイルを作成しておく必要がある。メニュー関係のレイアウトxmlファイルを作る場合はAndroid Studioの機能で作ると様々な設定をしてくれたり、標準的なテンプレートを表示してくれるので任せたほうが楽。
具体的なやり方はAndroid Studioのプロジェクトツリービューのリソースを保存するresディレクトリの上で右クリックし、New→Android resource directoryを選択するとウィザードが開くのでResource Typeでmenuを選択するとメニュー関係のレイアウトファイルを作るディレクトリが作成される。さらに、作成したmenuディレクトリから右クリックしNew→Manu resource fileを選択することでmenuのレイアウトファイルを作成することができる。
今回は下記のように検索ツールバー(SearchView)、スイッチ(Switch)、メニューアイテムの3つを設置した。
これはAndroid Studioのデザイナから設置できるが注意が必要。どの種類のアイテムを設置するかはactionViewClassかactionLayoutで対象を指定することで設定するわけだが、switchはそのままだとandroid.support.v7.widget.SwitchCompatのものが設置されないのでキャストするアプリが落ちるので注意が必要。どのウィジェットを設置するかはactionViewClassで自分で明示的に設置するようにしよう。
また、ここではshowActionでツールバー内に表示するか、ツールバーの右側のメニュー(「・・・」ボタン)にまとめる(オーバーフロー)かを設定できる。alwaysで常にツールバーに表示し、neverで常にオーバーフロー表示になる。ifRoomは表示できるスペースがあればツールバーに表示させ、そうでなければオーバーフロー表示になる。titleはオーバーフロー時に表示される文字になるので必ず記入が必要でないとアプリが落ちる。
<?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/itmSearch"
android:icon="@drawable/ic_search_black_24dp"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
<item
android:id="@+id/itmSwitch"
android:title="Switch"
app:actionViewClass="android.support.v7.widget.SwitchCompat"
app:showAsAction="always" />
<item
android:id="@+id/itm01"
android:title="item01"
app:showAsAction="ifRoom" />
<item
android:id="@+id/itm02"
android:title="item02"
app:showAsAction="never" />
</menu>
レイアウトファイルを記述できたら、あとはツールバーにレイアウトを読み込むだけで設置できる。
tbTestToolbar.inflateMenu(R.menu.test_actionbar_layout)
ツールバーはレイアウトとして、左側からナビケーションアイコン、アプリのロゴアイコン、タイトル/サブタイトル、カスタムビュー、メニュー(・・・ボタン)というレイアウトになっている。カスタムビューとメニュー以外は以下のようにして設定することができ、さらにナビゲーションアイコンをクリックしたときにコールバックすることができる。
//ナビゲーションアイコンを設定して、クリック時にコールバックする方法
tbTestToolbar.setNavigationIcon(R.drawable.abc_ic_arrow_drop_right_black_24dp)
tbTestToolbar.setNavigationOnClickListener {
Log.v("nullpo", "nav")
}
//アプリのロゴアイコンを表示させる方法
tbTestToolbar.setLogo(R.drawable.abc_ic_star_black_48dp)
//アプリ名とサブタイトルを表示する方法
tbTestToolbar.title = "ぬるぽ"
tbTestToolbar.subtitle = "nullpo"
設置したメニューアイテムからコールバックさせるには、以下のようにする。Toolbarにはitemがクリックされたときに発生するイベントを取得できるので、以下のようにitem.itemIdでメニューアイテムに設定したidからメニューを区別できるので、それぞれ処理を振り分けてあげればいい。
tbTestToolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.itm01 -> {
Log.v("nullpo", "itm01")
//相変わらずよくわからないが、恐らくitemに設定されたイベントを実行しない場合はtrue
true
}
R.id.itm02 -> {
Log.v("nullpo", "itm02")
true
}
else -> {
false
}
}
}
ツールバーに設置したSearchViewから検索するために入力した文字列を取得するためには、以下のようにする。Toolbarにはイベントを受信するメソッドはないので、itemを自分で検索してキャストしてあげる必要がある。このときにキャストする場合はandroid.support.v7.widgetのほうのSearchViewにキャストすること。するとSearchViewのインスタンスにアクセスすることができるので、SearchView.OnQueryTextListenerを継承したクラスを渡してあげればいい。継承させて参照を渡すか、下記のように無名クラスを使ってインラインで記述するかはお好み。
別クラスを作って継承させるとスコープが異なってしまうのでアクティビティ内にinner classをネストして作るか、無名クラスを使うか、activityにインターフェースとして継承させてメソッドをoverrideするのがいいかも?
val seatchView = tbTestToolbar.menu.findItem(R.id.itmSearch).actionView as SearchView
seatchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
//文字入力が確定したときに送られてくる 決定ボタンを押したりしたとき
override fun onQueryTextSubmit(query: String?): Boolean {
Log.v("nullpo_q", query?.toString())
//相変わらずよくわからない返値 たぶんfalseで元の設定されたイベントを行う trueでしない
return true
}
//文字が入力されるたびに入力文字列が送られてくる 部分一致のサジェスト機能などな利用できる
override fun onQueryTextChange(newText: String?): Boolean {
Log.v("nullpo_n", newText?.toString())
return true
}
})
Switchに関しても上記と同様にandroid.support.v7.widgetのほうのSwitchCompatにキャストする必要がある。じゃないとアプリが落ちる。そうするとSwitchCompatのインスタンスにアクセスできるのでリスナーに実行させたいクラスを登録してあげればいい。
val switchView = tbTestToolbar.menu.findItem(R.id.itmSwitch).actionView as SwitchCompat
switchView.setOnCheckedChangeListener { buttonView, isChecked ->
Log.v("nullpo", isChecked.toString())
}