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

如何用BottomSheetDialogFragment实现谷歌地图路线底部弹窗扩展行为?

实现谷歌地图风格的固定顶部BottomSheetDialogFragment

这个需求我之前做导航类APP的时候刚好实现过,用BottomSheetDialogFragment完全能搞定谷歌地图那种「点击展开时顶部固定、底部延伸至屏幕下(内容可滚动)」的效果,核心思路是把BottomSheet的布局拆成固定顶部栏和可扩展滚动内容区两部分,再配合BottomSheetBehavior来控制展开/收起状态。

一、先写布局文件:拆分固定顶部与可滚动内容

首先创建BottomSheet的布局layout_bottom_sheet_route.xml,用LinearLayout或者ConstraintLayout都可以,关键是把顶部栏和内容区分离,顶部栏直接挂在最外层容器顶部:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <!-- 固定顶部栏:始终停在BottomSheet顶部,不会随内容滚动 -->
    <LinearLayout
        android:id="@+id/top_fixed_bar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#ffffff"
        android:gravity="center_vertical"
        android:paddingHorizontal="16dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="路线规划"
            android:textSize="18sp"
            android:textStyle="bold" />

        <!-- 展开/收起切换按钮 -->
        <ImageView
            android:id="@+id/btn_toggle_expand"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_marginStart="auto"
            android:src="@drawable/ic_expand_more" />
    </LinearLayout>

    <!-- 可扩展内容区:超出屏幕时允许滚动,底部会延伸到屏幕下方 -->
    <androidx.core.widget.NestedScrollView
        android:id="@+id/content_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fillViewport="true">

        <!-- 这里放你的路线详情内容,比如多个路线卡片、距离/时间信息等 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="8dp">

            <View
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:background="#f5f5f5"
                android:layout_marginVertical="4dp"/>
            <View
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:background="#f5f5f5"
                android:layout_marginVertical="4dp"/>
            <View
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:background="#f5f5f5"
                android:layout_marginVertical="4dp"/>
            <View
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:background="#f5f5f5"
                android:layout_marginVertical="4dp"/>
        </LinearLayout>
    </NestedScrollView>
</LinearLayout>

二、实现BottomSheetDialogFragment子类:控制展开行为

接下来创建RouteBottomSheetDialogFragment类,加载上面的布局,通过BottomSheetBehavior设置初始高度、展开状态,以及切换按钮的逻辑:

class RouteBottomSheetDialogFragment : BottomSheetDialogFragment() {

    private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>
    private var isExpanded = false

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.layout_bottom_sheet_route, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initBottomSheetBehavior(view)
        setupToggleButton(view)
    }

    private fun initBottomSheetBehavior(view: View) {
        // 获取BottomSheet的根布局(Material Design自带的ID)
        val bottomSheetRoot = dialog?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
        bottomSheetRoot?.let {
            bottomSheetBehavior = BottomSheetBehavior.from(it)
            // 设置初始折叠高度:比如屏幕高度的1/3,刚好显示顶部栏+部分内容
            val screenHeight = resources.displayMetrics.heightPixels
            bottomSheetBehavior.peekHeight = screenHeight / 3

            // 监听BottomSheet状态变化,更新按钮图标
            bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                override fun onStateChanged(bottomSheet: View, newState: Int) {
                    isExpanded = newState == BottomSheetBehavior.STATE_EXPANDED
                    val toggleBtn = view.findViewById<ImageView>(R.id.btn_toggle_expand)
                    toggleBtn.setImageResource(if (isExpanded) R.drawable.ic_expand_less else R.drawable.ic_expand_more)
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {
                    // 滑动过程中的自定义逻辑,可选
                }
            })
        }
    }

    private fun setupToggleButton(view: View) {
        val toggleBtn = view.findViewById<ImageView>(R.id.btn_toggle_expand)
        toggleBtn.setOnClickListener {
            // 切换展开/收起状态
            if (isExpanded) {
                bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
            } else {
                // 展开时让BottomSheet占满屏幕高度,内容超出部分通过NestedScrollView滚动
                bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
                val screenHeight = resources.displayMetrics.heightPixels
                bottomSheetBehavior.maxHeight = screenHeight
            }
        }
    }
}

三、关键逻辑解析

  • 固定顶部栏:因为顶部栏直接放在最外层LinearLayout的顶部,没有嵌套在滚动容器里,所以无论BottomSheet怎么展开,它都会始终固定在BottomSheet的顶部位置。
  • peekHeight属性:设置折叠状态下BottomSheet的显示高度,比如设为屏幕1/3,刚好让用户看到顶部栏和部分路线内容,引导点击展开。
  • STATE_EXPANDED状态:展开时BottomSheet会自动延伸到屏幕顶部,此时内容区的NestedScrollView会处理超出屏幕的内容,用户可以滚动查看,而顶部栏保持不动。
  • 切换按钮:通过点击箭头图标手动切换展开/收起状态,同时更新图标反馈当前状态。

四、注意事项

  • 内容区必须用NestedScrollView或RecyclerView:如果用普通LinearLayout,超出屏幕的内容会被截断,无法滚动查看。
  • 可自定义展开高度:如果不想让BottomSheet占满整个屏幕,可以把maxHeight设为屏幕高度的80%之类的数值,按需调整。
  • 禁止拖动展开:如果只想让用户通过按钮切换,不想允许拖动,可以设置bottomSheetBehavior.isDraggable = false。

内容的提问来源于stack exchange,提问作者Angelina

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:47:17