Be simple

”当たり前”が誰かのためになる

BottomAppBarとBottomSheetDialogFragmentを使ってみた時のメモ

はじめに

 こんにちは、今日はBottomAppBarとBottomSheetDialogFragment実装について調べていたのでそれに関するメモになります。

 

BottomAppBarとは

 Google I/O 2018で紹介された新しいAndroid Material Componentの1つです。

 すでに製品版のアプリに導入されているのを各所で見られます。

 

material.io

 

このBottomAppBarを用いて作ってみた、今回のサンプルはこちら

f:id:rozkey59:20190404084329p:plain

sample

 

 

すごく簡単に実装できるので、実際に作りながらやっていきましょう。

 

まずは、準備から

BottomAppBarを利用するために、app配下のbuild.gradleのなかで以下のように記述してください。

 

dependencies {
implementation 'com.google.android.material:material:1.1.0-alpha05'
}

 

この後は、Gradle Syncしましょう。

導入はこれだけです。次に、レイアウトのリソースファイルを用意していきましょう。

 

レイアウトの用意をする

 次に、レイアウトを用意しましょう。新規でプロジェクトを作成し、Activityのxmlに下記のように記述します。

 今回は、Floating Action Button(FAB)を用意しているので一緒に定義します。

 

<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
>

<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:backgroundTint="@color/colorPrimary"
app:fabAlignmentMode="end"
app:fabCradleRoundedCornerRadius="16dp"
app:navigationIcon="@drawable/ic_menu"/>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tint="@color/blue_06"
android:src="@drawable/ic_search"
app:layout_anchor="@+id/bar"/>

<!-- layout_anchorはBottomAppBarにFabくっつけるのに必要だよ!忘れずに〜 -->

</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

 

app:navigationIconは表示させたいイメージを指定します。

 画像でいうと下記の赤枠の部分になります。

f:id:rozkey59:20190402182307p:plain

navigationIcon

とりあえず、これだけで上の画像のようなサンプルが作れます。
BottomSheetDialogFragmentを使ってメニューのBottomSheetを実装する前に、BottomAppBarのattributeを色々いじることで変更できることがあるので、それぞれについて確認していきましょう。

 

fabAlignmentMode

まずは、fabAlignmentModeです

app:fabAlignmentMode="end"

これはFabの位置を変えることができます。

endcenterがあります。

最初のサンプルではendを指定しているので、右端に寄っています。

 centerを指定した場合には次のようになります。

f:id:rozkey59:20190402183552p:plain

centerを指定した場合

真ん中にFabの位置が変わっていることを確認できます。

 

backgroundTint

backgroundTintではBottomAppBarの背景色を変えることができます。

app:backgroundTint="@color/colorPrimary"

上のサンプルでは、colorPrimaryを#4DD0E1で指定しているため、青色に変わっています。

ここでは、下記のマテリアルデザインのThe color systemからLight Greenの900を指定してみましょう。

material.io

 

xml上ではテスト的に次のように指定します。

app:backgroundTint="#33691E"

 

これで実行してみると次のような結果になります。

f:id:rozkey59:20190402184219p:plain

backgroundTint

barの色が変わっていることを確認できます。

 

fabCradleMargin

fabCragleMarginでは、BottomAppBarとの距離を指定できます。

サンプルに対して、次のように指定してみます。

app:fabCradleRoundedCornerRadius="16dp"

これを実行した結果が次のようになります。

f:id:rozkey59:20190402184532p:plain

fabCradleMargin

barとのマージンが大きく開いているのを確認することができます。

 

fabCradleRoundedCornerRadius

fabの周りのコーナーの丸みを調節できます。

何も指定しないときは、内部のコードを見るとdefaultValueに0が入っています。

デフォルトは以下のようになります。

f:id:rozkey59:20190402191049p:plain

何も指定しないとき

今度は、次のように指定しましょう。

app:fabCradleRoundedCornerRadius="36dp"

これを実行した時が次のようになります。

f:id:rozkey59:20190402191236p:plain

36dp指定した時

若干、Fabの周りのBarの丸みが変わっていることを確認できます。

 

fabCradleVerticalOffset

先ほどの状態で垂直方向の位置を変更することができます。

次のようにBottomAppBarに対して指定してみましょう。

app:fabCradleVerticalOffset="16dp"

 これを実行すると次のようになります。

f:id:rozkey59:20190404084930p:plain

fabCradleVerticalOffset

Fabの垂直方向の位置が変わっていることを確認できます。

少しだけくぼみを入れたようなデザインにしたいときに使えそうですね。

 

下タブにアイテムをつける

BottomAppBarに対してタブをつけたい場合があると思います。

そういう時には、BottomAppBarの中で各タブのアイテムを入れます。

<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:backgroundTint="@color/colorPrimary"
app:fabAlignmentMode="center"
app:fabCradleRoundedCornerRadius="16dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/clip"
android:gravity="center"
android:textColor="@color/white"
android:drawableTop="@drawable/ic_clip"
/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:layout_marginEnd="24dp"
android:text="@string/my_page"
android:textColor="@color/white"
android:drawableTop="@drawable/ic_my_page"
/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:layout_marginStart="24dp"
android:text="@string/home"
android:textColor="@color/white"
android:drawableTop="@drawable/ic_home"
/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/post"
android:layout_marginEnd="12dp"
android:textColor="@color/white"
android:drawableTop="@drawable/ic_post"
/>

</LinearLayout>

</com.google.android.material.bottomappbar.BottomAppBar>

 

これを実行すると次のようになります。

f:id:rozkey59:20190404093840p:plain

BottomAppBarにタブをつける

ただし、このやり方は一例です。CustomBottomNavigationViewを作るか、そのほかの方法があれば、教えていただけると幸いです。

 

次は、BottomSheetDialogFragmentを併用してメニューを作っていきましょう。

 

BottomSheetDialogFragmentを併用する

 作るサンプルはこちらです

f:id:rozkey59:20190404094124g:plain

BottomSheetDialogFragmentを併用した場合

ますは、BottomSheetDialogFragmentを継承したCustomBottomSheetDialogFragmentを作成します。

私は、次のようにCustomBottomSheetDialogFragmentをBottomAppMenuFragmentとして作成しました。

class BottomAppMenuFragment: BottomSheetDialogFragment() {

lateinit var binding: FragmentBottomMenuBinding

companion object {
fun newInstance(): BottomAppMenuFragment {
return BottomAppMenuFragment()
}
}

override fun getTheme(): Int {
// Fragment全体のスタイルを指定する
return R.style.BottomSheetDialogTheme
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// getThemeにて指定したthemeでインスタンス化してBottomSheetDialogを作成する
return BottomSheetDialog(requireContext(), theme)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_bottom_menu, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.navigationView.setNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.home -> {
Toast.makeText(context, "home", Toast.LENGTH_SHORT).show()
}
R.id.popular -> {
Toast.makeText(context, "popular", Toast.LENGTH_SHORT).show()
}
R.id.post -> {
Toast.makeText(context, "post", Toast.LENGTH_SHORT).show()
}
R.id.clip -> {
Toast.makeText(context, "clip", Toast.LENGTH_SHORT).show()
}
R.id.my_page -> {
Toast.makeText(context, "my_page", Toast.LENGTH_SHORT).show()
}
}
true
}
}
}

getThemeではFragment全体に適用するスタイルを指定して、onCreateDialogにて適用したスタイルを元にBottomSheetDialogをインスタンス化します。これで、スタイルで定義したようにBottomSheetを作れます。

setNavigationItemSelectedListenerでは、メニューそれぞれに対して何を処理するか記述できます。

 

次に、MainActivityのonOptionsItemSelectedのなかで、作成したBottomAppMenuFragmentをインスタンス化します。

実際には次のようにコードを書きます。

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item?.itemId) {
android.R.id.home -> {
val fragment = BottomAppMenuFragment.newInstance()
fragment.show(supportFragmentManager, fragment.tag)
}
}
return true
}

 

つぎに、スタイルについてみていきましょう。

styles.xmlのなかで次のように書きます。

<style name="BottomSheet" parent="@style/Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/background_bottom_sheet</item>
</style>

<style name="BottomSheetDialogTheme" parent="@style/Theme.Design.Light.BottomSheetDialog">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/colorPrimary</item>
<item name="android:textColorSecondary">@color/white</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:windowIsFloating">false</item>
<item name="bottomSheetStyle">@style/BottomSheet</item>
</style>

BottomSheetDialogThemeは親をTheme.Design.Light.BottomSheetDialogに指定します。

statusBarColorは透過させた黒を指定したいので、@android:color/transparentを設定し、navigationBarColorは一番下のNavigationBarの色を指定し、青色にしたいのでcolorPrimaryを指定しています。

 

また、windowIsFloatingNavigationBarの色を変えたいがためにfalseを指定しています。変える必要がなければdefaultでtrueなので指定する必要はありません。

 

textColorSecondaryは、メニューのアイコンの色を指定します。今回は白にしたいのでwhiteにしてます。

textColorPrimaryでは、メニューのテキストの色を指定します。今回は白にしたいのでwhiteにしてます。

 

BottomSheetでは、角丸を含ませたレイアウトにしたいため、parentをBottomSheet.ModalにしてShapeで定義したリソースを背景に指定します。

 

背景のdrawableは次のように書きます。background_bottom_sheet.xmlは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp"/>
<padding android:top="8dp"/>
<solid android:color="@color/colorPrimary"/>
</shape>

shapeタグを使って四角形を定義して、cornersタグで左上と右上の角を丸めて、paddingタグで上部に余白を持たせ、solidで全体の色を定義します。

 

これで、gifのようなサンプルを作成することができます。

 

おわりに

BottomAppBarの実装につまづいている人に対して参考になれば嬉しいです。

意外に簡単に実装できるので、ぜひ試してみてください。

他にも良い実装があったり、質問などあればコメントしていただけると幸いです。

それでは、またの機会に。