标准 BottomSheet 的海拔覆盖

Elevation overlay for a standard BottomSheet

我遵循 material3 规范,使用 android material components 库,并实现明暗主题。

我有一个作为 BottomSheet 的 ConstraintLayout,它的背景颜色应该受到 elevation overlay tint. BottomSheets are on the list of material 实现高程覆盖的组件的影响,但我的保留其默认颜色 colorSurface,而不是在深色模式下变亮:

(展开 BottomSheet 时颜色不变。)

唯一将我的 ConstraintLayout 定义为 BottomSheet 的是 layout_behaviour 属性,我想知道这实际上会如何影响背景颜色。 BottomSheets 是否只出现在之前的列表中,因为它们的 modal 变体?

如果是这样,如何在整个 ConstraintLayout 上实现高度覆盖?通过上面链接的第二页所建议的 ElevationOverlayProvider ?


这是我主要的简化布局 Activity :

<androidx.coordinatorlayout.widget.CoordinatorLayout
    ...>

    <com.google.android.material.appbar.AppBarLayout
        ...>

        <com.google.android.material.appbar.MaterialToolbar
            .../>

    </com.google.android.material.appbar.AppBarLayout>

    <fragment
        android:id="@+id/navigation_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        ... />

    <include
        android:id="@+id/band_list_bottomsheet"
        layout="@layout/bottomsheet_filter_and_sort"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

这是包含的 BottomSheet 布局(再次简化):

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
    app:behavior_peekHeight="?android:attr/listPreferredItemHeight"
    android:fitsSystemWindows="true"
    android:clickable="true"
    android:focusable="true"
    android:elevation="8dp"
    android:background="?attr/colorSurface">

    <View
        android:id="@+id/title_background"
        android:layout_width="0dp"
        android:layout_height="?android:attr/listPreferredItemHeight"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <ImageView
        android:id="@+id/pill"
        android:layout_width="25dp"
        android:layout_height="5dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/bottomsheet_dragable_pill"
        tools:ignore="ContentDescription"/>

    <TextView
        android:id="@+id/title"
        style="@style/TextStyle.BottomSheetTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="@string/filterandsort_sheet_label"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pill" />

    <com.google.android.material.divider.MaterialDivider
        android:id="@+id/divider_title_filter"
        style="@style/HorizontalDivider"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title_background"/>

    <TextView
        android:id="@+id/filter_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/filter_title"
        style="@style/TextStyle.BottomSheetSubheader"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/divider_title_filter"/>
        
    ...

</androidx.constraintlayout.widget.ConstraintLayout>

我已经在 ConstraintLayout 上尝试了一些方法来触发海拔覆盖,特别是:

最初,ConstaintLayout 没有设置背景,而 title_background 视图有一个,用于在 BottomSheet 顶部显示“Google 地图样式”圆角的可绘制对象。

我的主题来自 Material Theme Builder,因此它们扩展了 Theme.Material3.Light.NoActionBarTheme.Material3.Dark.NoActionBar。如上所示,我尽可能地使用系统属性来保持标准行为。

假设 material-components-android 库的 BottomSheetBehaviour 不用于处理海拔叠加层,下面是将其应用于布局的方法。

扩展对应布局如下:

class ElevationOverlayConstraintLayout(context: Context, attributes: AttributeSet) : ConstraintLayout(context, attributes) {

    init {
        background = MaterialShapeDrawable.createWithElevationOverlay(context, elevation)
    }

    override fun setElevation(elevation: Float) {
        super.setElevation(elevation)

        MaterialShapeUtils.setElevation(this, elevation)
    }
}

只需在您的 xml 布局定义中通过此 class 切换原始布局,然后 voila,应用海拔叠加!

来源:深色主题 documentation page referenced in the question, and this post


奖励:对于 BottomSheet 顶部的圆角,按如下方式更改 init 方法,并在尺寸中定义圆角半径(例如 8dp)。

    init {
        val model = ShapeAppearanceModel()
                .toBuilder()
                .setTopLeftCorner(CornerFamily.ROUNDED, resources.getDimension(R.dimen.bottomsheet_corner_radius))
                .setTopRightCorner(CornerFamily.ROUNDED, resources.getDimension(R.dimen.bottomsheet_corner_radius))
                .build()

        val shape = MaterialShapeDrawable.createWithElevationOverlay(context, elevation)
        shape.shapeAppearanceModel = model

        background = shape
    }