孔老愣 發表於 2025-12-26 08:55:35

Android BottomSheetBehavior使用方法及常见问题详解

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>一、基础准备</li><ul class="second_class_ul"><li>1. 依赖引入</li><li>2. 布局基础结构</li></ul><li>二、核心 API 与基础使用</li><ul class="second_class_ul"><li>1. 获取 BottomSheetBehavior 实例</li><li>2. 核心状态常量</li><li>3. 关键配置项</li><ul class="third_class_ul"><li>(1)设置折叠高度(peekHeight)</li><li>(2)设置拖拽禁用</li></ul></ul><li>三、状态监听</li><ul class="second_class_ul"></ul><li>四、高级用法</li><ul class="second_class_ul"><li>1. 全屏底部面板(无折叠高度)</li><ul class="third_class_ul"></ul><li>2. 嵌套滚动处理</li><ul class="third_class_ul"></ul><li>3. 自定义底部面板样式</li><ul class="third_class_ul"></ul></ul><li>五、常见问题与解决方案</li><ul class="second_class_ul"><li>1. 面板无法隐藏</li><ul class="third_class_ul"></ul><li>2. 半展开状态不生效</li><ul class="third_class_ul"></ul><li>3. 拖拽时与其他 View 冲突</li><ul class="third_class_ul"></ul><li>4. 面板高度超出屏幕</li><ul class="third_class_ul"></ul></ul><li>六、完整示例代码</li><ul class="second_class_ul"><li>布局文件(activity_main.xml)</li><ul class="third_class_ul"></ul><li>Kotlin 代码(MainActivity.kt)</li><ul class="third_class_ul"></ul></ul><li>七、总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>前言</h2>
<p>BottomSheetBehavior 是 Android Support Library(现 AndroidX)中 <code>com.google.android.material.bottomsheet.BottomSheetBehavior</code> 提供的一个行为类,用于实现底部弹出式面板(底部抽屉)效果,支持拖拽、展开/收起、状态监听等核心能力,广泛应用于底部菜单、筛选面板、详情弹窗等场景。</p>
<p class="maodian"></p><h2>一、基础准备</h2>
<p class="maodian"></p><h3>1. 依赖引入</h3>
<p>确保项目中引入 Material Design 依赖(AndroidX 版本):</p>
<div class="jb51code"><pre class="brush:java;">dependencies {
    // 核心 Material Design 库(包含 BottomSheetBehavior)
    implementation 'com.google.android.material:material:1.12.0'
    // 基础 AppCompat 库(可选,根据项目需求)
    implementation 'androidx.appcompat:appcompat:1.7.0'
}
</pre></div>
<p class="maodian"></p><h3>2. 布局基础结构</h3>
<p>BottomSheetBehavior 需作用于 <code>CoordinatorLayout</code> 的子 View,核心布局结构如下:</p>
<div class="jb51code"><pre class="brush:xml;">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!-- 必须使用 CoordinatorLayout 作为父布局 --&gt;
&lt;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"&gt;

    &lt;!-- 主内容区域(如 RecyclerView、LinearLayout 等) --&gt;
    &lt;LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:padding="16dp"&gt;

      &lt;Button
            android:id="@+id/btnShowBottomSheet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="显示底部面板"/&gt;

    &lt;/LinearLayout&gt;

    &lt;!-- 底部面板(BottomSheet) --&gt;
    &lt;LinearLayout
      android:id="@+id/bottomSheet"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:background="@android:color/white"
      android:padding="16dp"
      &lt;!-- 核心属性:绑定 BottomSheetBehavior --&gt;
      app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"&gt;

      &lt;TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="底部面板标题"
            android:textSize="18sp"
            android:textStyle="bold"/&gt;

      &lt;TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="底部面板内容区域,支持拖拽展开/收起"
            android:marginTop="8dp"/&gt;

      &lt;Button
            android:id="@+id/btnClose"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="关闭面板"
            android:marginTop="16dp"/&gt;

    &lt;/LinearLayout&gt;

&lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&gt;
</pre></div>
<p class="maodian"></p><h2>二、核心 API 与基础使用</h2>
<p class="maodian"></p><h3>1. 获取 BottomSheetBehavior 实例</h3>
<p>在 Activity/Fragment 中获取 Behavior 实例,用于控制底部面板:</p>
<div class="jb51code"><pre class="brush:java;">import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    // 声明 BottomSheetBehavior 实例
    private lateinit var bottomSheetBehavior: BottomSheetBehavior&lt;*&gt;

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      // 初始化 BottomSheetBehavior
      bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)

      // 初始化点击事件
      initClickEvents()
      // 初始化 Behavior 配置
      initBottomSheetConfig()
    }

    private fun initClickEvents() {
      // 显示底部面板
      btnShowBottomSheet.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
      }

      // 关闭底部面板
      btnClose.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
      }
    }
}
</pre></div>
<p class="maodian"></p><h3>2. 核心状态常量</h3>
<p>BottomSheetBehavior 提供了多种状态控制面板行为,常用状态如下:</p>
<table><thead><tr><th>状态常量</th><th>说明</th></tr></thead><tbody><tr><td><code>STATE_COLLAPSED</code></td><td>折叠状态(默认高度,可通过 <code>peekHeight</code> 设置)</td></tr><tr><td><code>STATE_EXPANDED</code></td><td>完全展开状态(占满屏幕高度)</td></tr><tr><td><code>STATE_HIDDEN</code></td><td>隐藏状态(需先设置 <code>behavior.setHideable(true)</code>)</td></tr><tr><td><code>STATE_HALF_EXPANDED</code></td><td>半展开状态(Android 11+ 支持,需设置 <code>fitToContents = false</code>)</td></tr><tr><td><code>STATE_DRAGGING</code></td><td>拖拽中状态(不可手动设置,仅监听)</td></tr><tr><td><code>STATE_SETTLING</code></td><td>滑动中状态(不可手动设置,仅监听)</td></tr></tbody></table>
<p class="maodian"></p><h3>3. 关键配置项</h3>
<p class="maodian"></p><h4>(1)设置折叠高度(peekHeight)</h4>
<p>控制面板折叠时显示的高度,默认值为 56dp:</p>
<div class="jb51code"><pre class="brush:java;">private fun initBottomSheetConfig() {
    // 设置折叠高度(单位:px)
    bottomSheetBehavior.peekHeight = 200 // 也可使用 dp2px 工具类转换

    // 允许隐藏面板(设置后才能使用 STATE_HIDDEN 状态)
    bottomSheetBehavior.isHideable = true

    // 是否适配内容高度(false 时支持半展开状态)
    bottomSheetBehavior.isFitToContents = false

    // 设置最大展开高度(Android 11+ 支持)
    bottomSheetBehavior.maxWidth = resources.displayMetrics.widthPixels
    bottomSheetBehavior.maxHeight = resources.displayMetrics.heightPixels / 2
}

// 工具类:dp 转 px
private fun dp2px(dp: Int): Int {
    val density = resources.displayMetrics.density
    return (dp * density + 0.5f).toInt()
}
</pre></div>
<p class="maodian"></p><h4>(2)设置拖拽禁用</h4>
<p>禁止用户手动拖拽面板,仅通过代码控制状态:</p>
<div class="jb51code"><pre class="brush:java;">bottomSheetBehavior.isDraggable = false
</pre></div>
<p class="maodian"></p><h2>三、状态监听</h2>
<p>通过 <code>BottomSheetCallback</code> 监听面板状态变化,实现业务逻辑联动:</p>
<div class="jb51code"><pre class="brush:java;">private fun initBottomSheetListener() {
    bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
      // 面板滑动时回调(dy:垂直滑动距离)
      override fun onSlide(bottomSheet: View, slideOffset: Float) {
            // slideOffset:滑动偏移量(0:折叠状态,1:完全展开,-1:隐藏)
            Log.d("BottomSheet", "滑动偏移量:$slideOffset")
            // 示例:根据偏移量改变面板透明度
            bottomSheet.alpha = 0.8f + slideOffset * 0.2f
      }

      // 面板状态变化时回调
      override fun onStateChanged(bottomSheet: View, newState: Int) {
            when (newState) {
                BottomSheetBehavior.STATE_EXPANDED -&gt; {
                  Log.d("BottomSheet", "完全展开")
                  // 展开时的业务逻辑:如隐藏软键盘、更新UI等
                }
                BottomSheetBehavior.STATE_COLLAPSED -&gt; {
                  Log.d("BottomSheet", "折叠")
                }
                BottomSheetBehavior.STATE_HIDDEN -&gt; {
                  Log.d("BottomSheet", "隐藏")
                }
                BottomSheetBehavior.STATE_HALF_EXPANDED -&gt; {
                  Log.d("BottomSheet", "半展开")
                }
                BottomSheetBehavior.STATE_DRAGGING -&gt; {
                  Log.d("BottomSheet", "拖拽中")
                }
                BottomSheetBehavior.STATE_SETTLING -&gt; {
                  Log.d("BottomSheet", "滑动中")
                }
            }
      }
    })
}
</pre></div>
<p class="maodian"></p><h2>四、高级用法</h2>
<p class="maodian"></p><h3>1. 全屏底部面板(无折叠高度)</h3>
<p>实现点击按钮弹出全屏底部面板,且禁止折叠:</p>
<div class="jb51code"><pre class="brush:java;">// 初始化配置
bottomSheetBehavior.peekHeight = 0 // 折叠高度设为0
bottomSheetBehavior.isHideable = true
bottomSheetBehavior.isDraggable = true

// 显示全屏面板
btnShowFullScreenSheet.setOnClickListener {
    bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
</pre></div>
<p class="maodian"></p><h3>2. 嵌套滚动处理</h3>
<p>若底部面板包含 <code>RecyclerView</code>/<code>NestedScrollView</code>,需处理嵌套滚动冲突:</p>
<div class="jb51code"><pre class="brush:xml;">&lt;!-- 底部面板内的 RecyclerView 配置 --&gt;
&lt;androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/&gt;
</pre></div>
<p>同时在代码中启用嵌套滚动:</p>
<div class="jb51code"><pre class="brush:java;">recyclerView.isNestedScrollingEnabled = true
</pre></div>
<p class="maodian"></p><h3>3. 自定义底部面板样式</h3>
<p>通过设置背景、圆角、阴影美化面板:</p>
<div class="jb51code"><pre class="brush:xml;">&lt;!-- 底部面板根布局 --&gt;
&lt;LinearLayout
    android:id="@+id/bottomSheet"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_bottom_sheet"
    android:elevation="8dp"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"&gt;

    &lt;!-- 内容 --&gt;
&lt;/LinearLayout&gt;
</pre></div>
<p><code>bg_bottom_sheet.xml</code>(drawable 资源):</p>
<div class="jb51code"><pre class="brush:xml;">&lt;shape xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;solid android:color="@android:color/white"/&gt;
    &lt;corners
      android:topLeftRadius="16dp"
      android:topRightRadius="16dp"/&gt;
    &lt;padding android:all="16dp"/&gt;
&lt;/shape&gt;
</pre></div>
<p class="maodian"></p><h2>五、常见问题与解决方案</h2>
<p class="maodian"></p><h3>1. 面板无法隐藏</h3>
<ul><li>原因:未设置 <code>isHideable = true</code>;</li><li>解决方案:调用 <code>bottomSheetBehavior.isHideable = true</code> 后再设置 <code>STATE_HIDDEN</code> 状态。</li></ul>
<p class="maodian"></p><h3>2. 半展开状态不生效</h3>
<ul><li>原因:Android 11 以下不支持,或未设置 <code>isFitToContents = false</code>;</li><li>解决方案:<div class="jb51code"><pre class="brush:java;">if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.R) {
    bottomSheetBehavior.isFitToContents = false
    bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
}
</pre></div></li></ul>
<p class="maodian"></p><h3>3. 拖拽时与其他 View 冲突</h3>
<ul><li>原因:嵌套滚动未处理,或多个 Behavior 冲突;</li><li>解决方案:<ol><li>禁用非目标 View 的拖拽:<code>bottomSheetBehavior.isDraggable = false</code>;</li><li>为嵌套滚动 View 设置 <code>app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;</code>;</li><li>重写 <code>onInterceptTouchEvent</code> 处理触摸事件。</li></ol></li></ul>
<p class="maodian"></p><h3>4. 面板高度超出屏幕</h3>
<ul><li>原因:面板内容高度过高,未设置 <code>wrap_content</code>;</li><li>解决方案:<ol><li>面板根布局高度设为 <code>wrap_content</code>;</li><li>内部使用滚动布局(如 <code>NestedScrollView</code>)包裹内容。</li></ol></li></ul>
<p class="maodian"></p><h2>六、完整示例代码</h2>
<p class="maodian"></p><h3>布局文件(activity_main.xml)</h3>
<div class="jb51code"><pre class="brush:xml;">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;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"&gt;

    &lt;LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:padding="16dp"
      android:gravity="center"&gt;

      &lt;Button
            android:id="@+id/btnExpand"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="展开面板"/&gt;

      &lt;Button
            android:id="@+id/btnCollapse"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="折叠面板"
            android:marginTop="8dp"/&gt;

      &lt;Button
            android:id="@+id/btnHide"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="隐藏面板"
            android:marginTop="8dp"/&gt;

    &lt;/LinearLayout&gt;

    &lt;!-- 底部面板 --&gt;
    &lt;LinearLayout
      android:id="@+id/bottomSheet"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="@drawable/bg_bottom_sheet"
      android:elevation="8dp"
      android:orientation="vertical"
      android:padding="16dp"
      app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"&gt;

      &lt;TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="底部面板"
            android:textSize="20sp"
            android:textStyle="bold"/&gt;

      &lt;androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:marginTop="16dp"/&gt;

      &lt;Button
            android:id="@+id/btnClose"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="关闭"
            android:marginTop="16dp"/&gt;

    &lt;/LinearLayout&gt;

&lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&gt;
</pre></div>
<p class="maodian"></p><h3>Kotlin 代码(MainActivity.kt)</h3>
<div class="jb51code"><pre class="brush:java;">import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private lateinit var bottomSheetBehavior: BottomSheetBehavior&lt;*&gt;

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      // 初始化 BottomSheetBehavior
      bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)

      // 初始化配置
      initConfig()

      // 初始化监听
      initListener()

      // 初始化点击事件
      initClick()

      // 初始化 RecyclerView(示例)
      initRecyclerView()
    }

    private fun initConfig() {
      // 设置折叠高度
      bottomSheetBehavior.peekHeight = dp2px(100)
      // 允许隐藏
      bottomSheetBehavior.isHideable = true
      // 支持半展开(Android 11+)
      if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.R) {
            bottomSheetBehavior.isFitToContents = false
      }
      // 禁用拖拽(可选)
      // bottomSheetBehavior.isDraggable = false
    }

    private fun initListener() {
      bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onSlide(bottomSheet: View, slideOffset: Float) {
                Log.d("BottomSheet", "滑动偏移量:$slideOffset")
            }

            override fun onStateChanged(bottomSheet: View, newState: Int) {
                when (newState) {
                  BottomSheetBehavior.STATE_EXPANDED -&gt; Log.d("BottomSheet", "完全展开")
                  BottomSheetBehavior.STATE_COLLAPSED -&gt; Log.d("BottomSheet", "折叠")
                  BottomSheetBehavior.STATE_HIDDEN -&gt; Log.d("BottomSheet", "隐藏")
                  BottomSheetBehavior.STATE_HALF_EXPANDED -&gt; Log.d("BottomSheet", "半展开")
                  else -&gt; Log.d("BottomSheet", "其他状态:$newState")
                }
            }
      })
    }

    private fun initClick() {
      btnExpand.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
      }

      btnCollapse.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
      }

      btnHide.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
      }

      btnClose.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
      }
    }

    private fun initRecyclerView() {
      // 示例:为 RecyclerView 设置简单适配器
      recyclerView.adapter = SimpleAdapter(getData())
      recyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this)
    }

    // 模拟数据
    private fun getData(): List&lt;String&gt; {
      val list = mutableListOf&lt;String&gt;()
      for (i in 1..20) {
            list.add("列表项 $i")
      }
      return list
    }

    // dp 转 px
    private fun dp2px(dp: Int): Int {
      val density = resources.displayMetrics.density
      return (dp * density + 0.5f).toInt()
    }

    // 简单适配器
    inner class SimpleAdapter(private val data: List&lt;String&gt;) :
      androidx.recyclerview.widget.RecyclerView.Adapter&lt;SimpleAdapter.ViewHolder&gt;() {

      inner class ViewHolder(itemView: View) :
            androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
            val tv: TextView = itemView as TextView
      }

      override fun onCreateViewHolder(parent: android.view.ViewGroup, viewType: Int): ViewHolder {
            val tv = TextView(parent.context)
            tv.layoutParams = androidx.recyclerview.widget.RecyclerView.LayoutParams(
                androidx.recyclerview.widget.RecyclerView.LayoutParams.MATCH_PARENT,
                dp2px(40)
            )
            tv.padding = dp2px(8)
            return ViewHolder(tv)
      }

      override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.tv.text = data
      }

      override fun getItemCount(): Int = data.size
    }
}
</pre></div>
<p class="maodian"></p><h2>七、总结</h2>
<p>BottomSheetBehavior 是实现底部面板的核心组件,核心要点:</p>
<ol><li>必须依赖 Material Design 库,且父布局为 <code>CoordinatorLayout</code>;</li><li>通过 <code>from()</code> 方法获取 Behavior 实例,控制面板状态;</li><li>常用状态:<code>STATE_EXPANDED</code>(展开)、<code>STATE_COLLAPSED</code>(折叠)、<code>STATE_HIDDEN</code>(隐藏);</li><li>可通过 <code>peekHeight</code>、<code>isHideable</code>、<code>isFitToContents</code> 等配置自定义行为;</li><li>利用 <code>BottomSheetCallback</code> 监听状态变化,实现业务联动。</li></ol>
<p>适用于底部菜单、筛选面板、详情弹窗、地图底栏等场景,是 Android 开发中高频使用的组件。</p>
<p>到此这篇关于Android&nbsp;BottomSheetBehavior使用方法及常见问题的文章就介绍到这了,更多相关Android&nbsp;BottomSheetBehavior使用内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Android 动画之ScaleAnimation应用详解</li><li>Android的Activity跳转动画各种效果整理</li><li>深入Android MediaPlayer的使用方法详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Android BottomSheetBehavior使用方法及常见问题详解