You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:54:26