如何设置BottomSheet初始位置?使其默认显示在屏幕中部偏下
实现自定义初始位置的BottomSheet方案
这问题我之前做项目时刚好折腾过,完全可以通过自定义BottomSheetBehavior来实现你要的效果——初始在屏幕中部偏下,上滑展开到标题栏下方,下面给你一步步讲清楚:
核心思路
系统默认的BottomSheetBehavior只有STATE_COLLAPSED(折叠到底部)、STATE_HALF_EXPANDED(半展开)、STATE_EXPANDED(完全展开)这几种状态,我们需要新增一个自定义的中间状态,然后重写Behavior的布局和滑动逻辑,让它默认停在这个中间位置,同时把展开的终点设为标题栏下方。
具体实现步骤
1. 定义自定义Behavior类
继承BottomSheetBehavior,新增自定义状态常量,并重写关键方法来处理布局和状态切换:
import android.content.Context import android.util.AttributeSet import android.view.View import androidx.coordinatorlayout.widget.CoordinatorLayout import com.google.android.material.bottomsheet.BottomSheetBehavior class CustomMiddleBottomSheetBehavior<V : View>(context: Context, attrs: AttributeSet?) : BottomSheetBehavior<V>(context, attrs) { // 自定义中间状态,数值避开系统默认状态值(系统用0、1、2、3) companion object { const val STATE_MIDDLE = 100 } private var toolbarHeight = 0 // 标题栏高度,后续传入 private var middleSheetHeight = 0 // 中间位置的BottomSheet高度 // 设置标题栏高度,用于计算展开后的位置 fun setToolbarHeight(height: Int) { toolbarHeight = height // 自定义中部偏下位置,这里取屏幕高度的60%,可按需调整 middleSheetHeight = (context.resources.displayMetrics.heightPixels * 0.6).toInt() // 展开后距离顶部的偏移量,刚好对齐标题栏底部 expandedOffset = toolbarHeight } override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean { // 先让系统完成基础布局 val layoutResult = super.onLayoutChild(parent, child, layoutDirection) // 初始化时强制切换到中间状态 if (state == STATE_COLLAPSED) { state = STATE_MIDDLE } return layoutResult } override fun calculateSlideOffset(child: V): Float { return when (state) { STATE_MIDDLE -> { // 计算中间状态的偏移量:屏幕高度减去中间Sheet的高度 val desiredTop = context.resources.displayMetrics.heightPixels - middleSheetHeight child.top = desiredTop 1f - (desiredTop.toFloat() / peekHeight) } else -> super.calculateSlideOffset(child) } } override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) { super.onStopNestedScroll(coordinatorLayout, child, target, type) // 处理滑动结束后的状态自动切换 val currentTop = child.top val screenHeight = context.resources.displayMetrics.heightPixels // 计算滑动阈值:中间位置到展开位置的中点,超过则自动展开 val expandThreshold = screenHeight - (middleSheetHeight + (screenHeight - middleSheetHeight - toolbarHeight) / 2) when { currentTop < expandThreshold -> state = STATE_EXPANDED currentTop > screenHeight - middleSheetHeight -> state = STATE_MIDDLE } } }
2. 布局文件中使用自定义Behavior
在CoordinatorLayout里,给BottomSheet的根View(比如你的RecyclerView)设置自定义Behavior:
<?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" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 你的主布局:包含标题栏 --> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary"/> <!-- BottomSheet容器:包含RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/bottom_sheet_recycler" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" app:layout_behavior="com.yourpackage.CustomMiddleBottomSheetBehavior"/> </androidx.coordinatorlayout.widget.CoordinatorLayout>
3. 在Activity/Fragment中初始化配置
获取Toolbar高度,传给自定义Behavior,完成初始设置:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.google.android.material.bottomsheet.BottomSheetBehavior class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar) setSupportActionBar(toolbar) val bottomSheetRecycler = findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.bottom_sheet_recycler) // 强转成自定义Behavior val behavior = BottomSheetBehavior.from(bottomSheetRecycler) as CustomMiddleBottomSheetBehavior // 获取标题栏高度(用系统默认的actionBarSize,也可手动测量) val toolbarHeight = resources.getDimensionPixelSize(androidx.appcompat.R.dimen.abc_action_bar_default_height_material) behavior.setToolbarHeight(toolbarHeight) // 可选配置:禁用默认半展开状态,避免和自定义中间状态冲突 behavior.isFitToContents = false } }
关键细节调整
- 中间位置自定义:修改
middleSheetHeight = (screenHeight * 0.6).toInt()中的0.6,比如0.5是屏幕正中间,0.7更靠下,按需调整即可。 - 展开位置适配:如果你的标题栏是自定义高度,直接传入实际测量的高度就行,
expandedOffset会自动让BottomSheet顶部对齐标题栏底部。 - 滑动灵敏度:调整
expandThreshold的计算逻辑,可以改变滑动触发状态切换的灵敏度,比如把/2改成/3,需要滑动更多才会展开。
要是你用的是BottomSheetDialogFragment,只需要在onViewCreated里获取dialog的BottomSheet根View,再设置这个自定义Behavior,逻辑完全一致~
内容的提问来源于stack exchange,提问作者Nah
相关产品推荐
相关产品推荐

