今回はアプリでよく見る上部のバーにアイコン式で設置されてるメニューを実現するToolbarを使う。
Toolbarを上部メニューとして使う上で、準備しなきゃいけない事がいくつかある。
・レイアウト: Toolbarを置いただけ。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
・Activity:
public class MainActivity extends AppCompatActivity{
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.toolbar_layout);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add:
break;
}
return false;
}
}
・Toolbarにセットする情報はメニューに使うリソースを参照する為、res階層menuフォルダにmenu用のリソースファイルを作る。
(menuフォルダを右クリック→New→Menu resource file
<?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/add"
android:icon="@mipmap/baseline_add_black_48"
android:title="@string/add"
app:showAsAction="ifRoom" />
</menu>
・icon: 項目用のアイコン
・title: タイトル
・app:showAsAction: 仮に上部のメニューバーにアイコンを表示する十分な領域がなかったらどうするか。一つだけだったら特に気にしなくていい。
項目を増やすには<itemで追加はできるが、追加しすぎると分かりやすさが犠牲になったり、表示領域が足りなくなったりするので、多くても2つか3つがGoogleの推奨数。
アイコンについては、自ら追加しない限りデフォルトではアイコン用の画像しか用意されてないので、手動で追加する必要がある。ただ、Androidは端末によって画面サイズや解像度がバラバラなので、できるだけ最適な素材を用意しないと端末によっては見え方が汚くなる。
よく使うアイコンとかはGoogleがアプリとかで使える用の画像を用意してくれてるサイトがあるので、そこから取るのがいいだろう。(ただし、ルールは破らないように。
https://material.io/tools/icons/?style=baseline
上からAndroid向けの物をダウンロードすると、こんな感じで展開される
それぞれのサイズに合った素材が用意されてるので、各フォルダから使う画像をコピペする
コピペ先はAndroid Studioのres階層のmipmapフォルダで、使うサイズを決めてhdpiからxxxhdpiに追加する。(今回は48
あとはAndroid Manifest.xmlを編集する必要がある。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="galaxysoftware.sampleapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".KotlinActivity">
</activity>
</application>
</manifest>
ここのandroid:theme=の箇所をNoActionBarと、ActionBarがないテーマに変更する必要がある。(後々ToolbarにActionBarとしての役割を持たせる際に障害になる
準備は整ったので、Activity側の実装を見ていく。
まず、Toolbar用の変数を用意した後、安定の
toolbarにfindVIewByIdでViewを参照付けてsetSupportActionBar()を実行。これによってToolbarをActionBarと同じ様に使う事ができる。
・onCreateMenu(): Activity開始時にメニューを設定する為に呼ばれる
getMenuInflater().inflate(R.menu.main, menu);
これで設置したいメニューを指定する。
・onOptionsItemSelected(): 設置したメニューの項目がクリックされた時に呼ばれる。
押された項目の判別は引数のmenuからitemidを取得して、メニューファイルで設定したidを指定する。
ここまでがToolbarにメニューを設置して上メニューにする実装だが、設置しただけの最低限の事しかしてないから、レイアウトや押した時の反応はすっからかん。
#もし画面遷移した時とか、一度設定したメニューを別の物に変えたくなった時
Activityに用意されている**invalidateOptionsMenu()**を実行する事で
・onPrepareOptionsMenu(): が呼ばれ、ここでメニューを指定する事で変えられる
#Toolbarに検索バーを設置する: SearchView
<?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/search"
android:icon="@mipmap/baseline_search_black_48"
android:title="@string/search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
<item
android:id="@+id/add"
android:icon="@mipmap/baseline_add_black_48"
android:title="@string/add"
app:showAsAction="ifRoom" />
</menu>
<itemにapp:actionViewClass="android.support.v7.widget.SearchView"を追加する事でSearchViewとしての機能を持たせる事ができる。
Toolbarに検索バーを設置するにはSearchViewと呼ばれる物を使う。これはメニューの一種なので、定義するのはレイアウトファイルではなく、メニューファイルになる。
Activityから操作するコードは以下の通り。
このSearchViewはmenuに定義してあるので、通常のfindViewById()ではなく、menu内から探す様にする。でmenu.findItemが直接SearchViewの型を返す訳ではないので、キャストしてSearchViewとして扱う様にする。
また、SearchViewが含まれるメニューを設置する時じゃないとクラッシュするので気を付けよう。
SearchView searchView = (SearchView)menu.findItem(R.id.search).getActionView();
searchView.setIconified(false);
searchView.setQueryHint(getString(R.string.search));
searchView.clearFocus();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
return false;
}
@Override
public boolean onQueryTextChange(String s) {
return false;
}
});
・setIconified(): 検索ボックスを出さずにアイコン化して表示するか
・setQueryHint(): 「検索」の様に、ヒントを表示する
・clearFocus(): フォーカスを外す。キーボードが開いてたら閉じる。
・setOnQueryTextListener(): SearchViewの文字入力を監視するリスナー
・onQueryTextSubmit(): 検索ボタンを押した時に呼ばれる。引数はSearchViewに入力されてる文字
・onQueryTextChange(): SearchViewに入力されてる文字が変わる度に呼ばれる。引数は入力されてる文字。もし、suggestionとか予測を出す場合はここを活用することになる。
Toolbarにメニューを実装してよくある上部のアイコン式メニューを実装するやり方は以上となる。実行するとこんな感じ。でも最低限しか実装してないから何も表示されない。
#おまけ:これをKotlinで書くとどうなるか
class KotlinActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.toolbar_layout)
setSupportActionBar(toolbar)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main, menu)
val searchView = menu?.findItem(R.id.search)?.actionView as SearchView
searchView.apply {
isIconified = false
queryHint = getString(R.string.search)
clearFocus()
setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextChange(query: String?): Boolean {
return false
}
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
})
}
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item?.itemId) {
R.id.add -> SampleFragment.newInstance()
}
return super.onOptionsItemSelected(item)
}
}
感が良い人は気付いたかもしれないが、メニューをクリックした時にadd項目だったら**SampleFragment(Fragment)**を生成しているのだ!
後々やるのだが、このFragmentという物がAndroidでの画面遷移において重要な役割を果たすことになる。