ActionbarからToolbarへ
前回、ActionBarをFragmentに対応させるでActionbarをActivity、と複数フラグメントに対応させましたが、googleは今はActionbarではなくて、Toolbarを推奨しています。
公式ページ:アプリバーの追加
公式では、ActionbarやToolbarのように画面の上部に表示されるばーを「アプリバー」と総称しています。
ActionbarとToolbarの大きな違いは
- Actionbarはthemesの一部
- ToolbarはLayoutの部品
が大きな違いです。
themes.xml
themes.xmlの親スタイルはActionbarの場合は
parent="Theme.MaterialComponents.DayNight.DarkActionBar"
でしたが、Toolbarの場合は
parent="Theme.MaterialComponents.DayNight.NoActionBar"
もしくは
parent="Theme.Material3.DayNight.NoActionBar"
となります。Material3では、DarkActionBarは既になくなっています。Material3ではActionbarはもう使うなってことなんでしょうかね?
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Base.Theme.Toolbar" parent="Theme.Material3.DayNight.NoActionBar">
・・・
</resources>
nightテーマの方のthemes.xmlも同様に修正します。
メニュー定義ファイル
Toolbarのアイコン、メニューを定義するメニュー定義ファイルはActionbarもToolbarも同じなのでそのまま使います。
レイアウト、activity_main.xml
Toolbarはレイアウトの中のひとつの部品なのでactivity_main.xmlの上部に配置します。その下にFragmentContainerViewを配置します。Toolbar以外の残りはFragmentContainerViewが目いっぱい、画面全体を覆うようにします。この、FragmentContainerViewをクルクルとFragmentを入れ替えて画面遷移します。
公式ではToolbarの部品に
android.support.v7.widget.Toolbar
を使っていますが、これは古いようです。最新版は
androidx.appcompat.widget.Toolbar
を使います。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="10dp"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="com.example.toolbar.Fragment1"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
Toolbarの設定値でテーマの設定値を適用する場合は?attrを使います。
- ?attr/actionBarSize バーの縦サイズ
- ?attr/colorPrimary テーマのPrimaryの色
- ?attr/actionBarTheme バーのテーマ
MainActivity、Fragmentの作り
MainActivityはActionbarの時と同じように、MainActivity側にToolbarの設定、menuのinflateを集約します。Fragment側は各Fragmentで異なる場合、Toolbarのアイコン、メニューの表示、非表示だけを制御します。
class MainActivity : AppCompatActivity() {
/** ViewBinding */
private lateinit var binding: ActivityMainBinding
/**
* アクティビティの初期化
* @param[savedInstanceState] Bundle
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater).apply {
setContentView(this.root)
}
val toolbar = binding.toolbar.apply {
setLogo(R.mipmap.ic_launcher)
title = "タイトル"
setTitleTextColor(Color.WHITE)
subtitle = "サブタイトル"
setSubtitleTextColor(Color.GRAY)
}
setSupportActionBar(toolbar)
}
・・・
}
レイアウト上に配置してある部品なので普通にviewBindingで取得してプロパティを設定します。サブタイトルは例として設定しいますが。マテリアルデザインでは非推奨となっています。
メニューのinflateとアクションの追加
公式ではアクションへの対応で、onOptionsItemSelected関数を実装していますが、この実装は古いようです。
前回、ActionBarをFragmentに対応させるでの例と同様に
androidx.core.view.MenuHost
androidx.core.view.MenuProvider
を使います。これはそっくり、そのままActionbarの時と同じ実装が使えます。
private fun setupMenuBar() {
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_sample, menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.iconItem1 -> {
・・・
}
・・・
else -> {}
}
return true
}
})
}
Fragment側も、そのままActionbarの時と同じ実装が使えます。
private fun setupMenuBar() {
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// Fragmentの場合はinflateしなくていい
}
override fun onPrepareMenu(menu: Menu) {
// Fragment毎に、ActionBarのicon、menuの表示/非表示を制御する場合はここでやる
menu.findItem(R.id.iconItem1).isVisible = false
menu.findItem(R.id.iconItem2).isVisible = true
・・・
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
// アイコン、メニューが押された場合のハンドリングはMainActivityでやる
return true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
Fragment側で実装すべきなのはMenuProvider#onPrepareMenu関数だけです。ここは各Fragment毎に表示するアイコンメニューを変えるため、表示、非表示の制御をします。
ActionbarからToolbarへの書き換えは意外と簡単
以上のように、ActionbarとToolbarで違うのは
- themes.xml(dayLight、night)の親スタイル
- activitymain.xmlにToolbarを配置
- MainActivity.ktでToolbarの初期化と設定
なので意外と簡単です。
最後に
ソースはgitHubにpushしました