複数のfragmentで画面を切り替えていて、特定の画面だけアクションバーの下の影を消したい場面があったので、その時のメモ。
環境:
Android Studio 3.5
※minSdkVersion 22
【目的】ここをこうしたい
elevation
影を付けるのは「elevation」というプロパティで、以下のようにすれば影を消すことができる。
【app_bar_nav.xml】
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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=".NavActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
android:stateListAnimator="@null"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_nav" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
AppBarLayout の android:stateListAnimator に「@null」を設定し、app:elevation に「0dp」を設定。
※android:stateListAnimator にnullを設定する理由は、デフォルトで設定されているアニメーション内でelevationを設定しているためで、
nullにしておかないと指定したelevationが上書きされてしまう。
すると以下のようにアクションバーの影が消えるが…
fragmentで遷移する全ての画面の影が消えてしまう。
適切なelevation値は何か
fragmentが切り替わるときに、画面によってelevationを設定しなおさないといけない。
elevationを再設定する際、適切な値が知りたいので stateListAnimator でどのように設定されているか確認。
いろいろ探した結果、design_appbar_state_list_animator.xmlが設定されているぽい。
【design_appbar_state_list_animator.xml】
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:state_enabled="true"
app:state_lifted="false"
app:state_liftable="true">
<objectAnimator
android:duration="@integer/app_bar_elevation_anim_duration"
android:propertyName="elevation"
android:valueTo="0dp"
android:valueType="floatType"/>
</item>
<item android:state_enabled="true">
<objectAnimator
android:duration="@integer/app_bar_elevation_anim_duration"
android:propertyName="elevation"
android:valueTo="@dimen/design_appbar_elevation"
android:valueType="floatType"/>
</item>
<item>
<objectAnimator
android:duration="0"
android:propertyName="elevation"
android:valueTo="0"
android:valueType="floatType"/>
</item>
</selector>
@dimen/design_appbar_elevation(4dp) が設定されていた。
そういえばマテリアルデザインのサイトに書いてあったな…
animatorをコピってきて自作してもいいのだが、そこまでする必要は今回ないので当初の予定通りfragment切り替え時にelevationを設定するようにする。
最終的なソース
【app_bar_nav.xml】
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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=".NavActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
android:stateListAnimator="@null">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_nav" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
レイアウトxmlには android:stateListAnimator=”@null” のみ。
【dimens.xml】
<dimen name="appbar_elevation">4dp</dimen>
animatorで使用されている @dimen/design_appbar_elevation はprivateだと警告されるので、自前で値を作成。
【NavActivity.kt】
class NavActivity : AppCompatActivity() {
…中略
override fun onCreate(savedInstanceState: Bundle?) {
…中略
val navController = findNavController(R.id.nav_host_fragment)
…中略
navController.addOnDestinationChangedListener { _, destination, _ ->
val appBar = findViewById<AppBarLayout>(R.id.appBarLayout)
if (destination.id == R.id.nav_list) {
appBar.elevation = 0f
} else {
appBar.elevation = resources.getDimension(R.dimen.appbar_elevation)
}
}
…中略
}
}
fragmentの親のactivityで、遷移先のfragmentで分岐してelevationを設定。
【fragment_list.xml】
<?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"
xmlns:tools="http://schemas.android.com/tools">
…中略
<FrameLayout
android:id="@+id/filter_bar_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="@dimen/appbar_elevation"
android:background="@color/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
…中略
</layout>
list画面の検索条件枠にelevationを設定。
実行すると…
想定通りにできた。





