複数の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を設定。
実行すると…
想定通りにできた。