Android BottomSheet圆角阴影遇色差问题求最优解决方案
Android BottomSheet带圆角阴影的最优实现方案
我开发了一个内嵌FragmentContainerView的BottomSheet,顶部边角带圆角。用渐变View模拟顶部阴影时,侧边出现了渐变导致的色差,试过第三方库也没达到预期效果,求这个场景的最优解决方案。
原实现代码
BottomSheet布局
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/bottom_sheet_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:behavior_hideable="false" app:behavior_peekHeight="30dp" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <View android:id="@+id/view_shadow" android:layout_width="match_parent" android:layout_height="20dp" android:background="@drawable/shape_bottomsheet_shadow" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraint_stroke" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="4dp" android:background="@drawable/shape_home_bottomsheet_stroke" android:paddingTop="1dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_goneMarginTop="10dp"> <androidx.fragment.app.FragmentContainerView android:id="@+id/bottom_sheet_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:background="@drawable/shape_home_bottomsheet_background" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:layout="@layout/fragment_blank" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
渐变阴影shape文件(shape_bottomsheet_shadow)
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:topRightRadius="16dp" android:topLeftRadius="16dp"/> <gradient android:angle="90" android:endColor="@android:color/transparent" android:startColor="@color/homepage_bottomsheet_shadow_start" /> </shape>
最优解决方案
方案一:利用Material BottomSheet原生属性(推荐)
Material组件的BottomSheetBehavior本身支持shape和阴影,不需要额外的渐变View,阴影会自动贴合圆角,完全避免侧边色差。
- 定义圆角shape样式(res/values/styles.xml):
<style name="BottomSheetShapeAppearance" parent="ShapeAppearance.MaterialComponents.LargeComponent"> <item name="cornerFamily">rounded</item> <item name="cornerSizeTopLeft">16dp</item> <item name="cornerSizeTopRight">16dp</item> <item name="cornerSizeBottomLeft">0dp</item> <item name="cornerSizeBottomRight">0dp</item> </style>
- 修改BottomSheet布局,移除多余的阴影View和嵌套层级:
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/bottom_sheet_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:elevation="8dp" android:backgroundTint="@color/your_bottomsheet_background_color" app:shapeAppearanceOverlay="@style/BottomSheetShapeAppearance" app:behavior_hideable="false" app:behavior_peekHeight="30dp" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <androidx.fragment.app.FragmentContainerView android:id="@+id/bottom_sheet_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:background="@android:color/transparent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:layout="@layout/fragment_blank" /> </androidx.constraintlayout.widget.ConstraintLayout>
- 说明:
elevation控制阴影强度,shapeAppearanceOverlay指定圆角样式,backgroundTint设置BottomSheet背景色,阴影会自动跟随圆角边缘,无任何色差问题。
方案二:自定义OutlineProvider(兼容低版本)
如果项目不支持高版本Material组件,可通过自定义OutlineProvider让阴影贴合圆角:
- 修改布局,移除阴影View,给根布局添加elevation:
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/bottom_sheet_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:elevation="8dp" android:background="@drawable/shape_home_bottomsheet_background" app:behavior_hideable="false" app:behavior_peekHeight="30dp" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <androidx.fragment.app.FragmentContainerView android:id="@+id/bottom_sheet_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:background="@android:color/transparent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:layout="@layout/fragment_blank" /> </androidx.constraintlayout.widget.ConstraintLayout>
- 在代码中设置自定义OutlineProvider:
val bottomSheetLayout = findViewById<ConstraintLayout>(R.id.bottom_sheet_layout) val radius = resources.getDimensionPixelSize(R.dimen.bottomsheet_corner_radius).toFloat() bottomSheetLayout.outlineProvider = object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { // 只保留顶部圆角,底部圆角设为0 outline.setRectAndRoundRect(0, 0, view.width, view.height, 0f, 0f, radius, radius) } } bottomSheetLayout.clipToOutline = true
方案三:使用LayerDrawable组合背景
把圆角背景和阴影层组合成一个Drawable,替代渐变模拟阴影:
- 创建layer_drawable_bottomsheet.xml:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 阴影层:用模糊色+padding模拟阴影 --> <item android:top="0dp" android:bottom="0dp" android:left="0dp" android:right="0dp"> <shape android:shape="rectangle"> <corners android:topLeftRadius="16dp" android:topRightRadius="16dp"/> <solid android:color="@color/homepage_bottomsheet_shadow_start"/> <padding android:top="4dp"/> </shape> </item> <!-- 内容背景层 --> <item android:top="4dp"> <shape android:shape="rectangle"> <corners android:topLeftRadius="16dp" android:topRightRadius="16dp"/> <solid android:color="@color/your_bottomsheet_background_color"/> </shape> </item> </layer-list>
- 修改布局,移除阴影View,给根布局设置该背景:
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/bottom_sheet_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/layer_drawable_bottomsheet" app:behavior_hideable="false" app:behavior_peekHeight="30dp" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <!-- 内部FragmentContainerView --> </androidx.constraintlayout.widget.ConstraintLayout>
内容的提问来源于stack exchange,提问作者Hugo Oliveira
相关产品推荐
相关产品推荐

