需求:实现底部弹窗固定最小显示高度 ,实现弹窗最大展示高度 实现弹窗高度根据内容自适应
(最左边)默认初始显示高度 (中间)根据内容自适应弹窗高度 ( 最右边)弹窗最大高度限制
实现方式 BottomSheetBehavior
说到BottomSheetBehavior 肯定联想到CoordinatorLayout协调布局 我们在日常开发中 最常用的就是app:layout_behavior="@string/appbar_scrolling_view_behavior"
先介绍下BottomSheetBehavior
从名字即可以看出,BottomSheetBehavior继承自CoordinatorLayout.Behavior,借用behavior的布局和事件分发能力来实现底部弹出动画及手势拖拽效果。下面首先分析下bottomsheet初始弹出时是如何实现弹出动画。
一个简单的半屏滑动布局如下:
3.1 BottomSheetBehavior的几种状态
- STATE_HIDDEN :隐藏状态,关联的View此时并不是GONE,而是此时在屏幕最下方之外,此时只是无法肉眼看到
- STATE_COLLAPSED :折叠状态,一般是一种半屏形态
- STATE_EXPANDED:完全展开,完全展开的高度是可配置,默认即屏幕高度。类似地图首页一般完全展开态的高度配置为距离屏幕高差一小截距离。
-
STATE_DRAGGING:拖拽状态,标识人为手势拖拽中(手指未离开屏幕)
-
STATE_SETTLING :视图从脱离手指自由滑动到最终停下的这一小段时间,与STATE_DRAGGING差异在于当前并没有手指在拖拽。主要表达两种场景:初始弹出时动画状态、手指手动拖拽释放后的滑动状态。
然后介绍下BottomSheetBehavior的属性:
- isFitToContents:是否填充整个内容
- expandedOffset:展开后距离顶部的高度
- halfExpandedRatio:半展开占比
- setState:设置当前状态:隐藏、半展开、全展开等等
- setPeekHeight:设置初始显示高度
根据需求 默认有一部分弹窗需要固定在底部
具体代码
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical"
app:behavior_peekHeight="164dp"
app:layout_behavior="@string/bottom_sheet_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottomDialogTopRoot"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_164">
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/llRunFootPrintTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
bottomDialogTopRoot里面的布局 是固定在屏幕底部的弹窗头部文件 llRunFootPrintTitle是弹窗的其他布局文件 内部包含一个recycleview列表 布局文件大致就是如此
具体kotlin代码
private val maxHeightPercentage: Float = 0.75f // 最大高度占屏幕的70%
private val peekHeight: Int = DensityUtils.dp2px(164f) // 默认peek高度为164dp(转换为像素后的值)
private fun initBottomBehavior() {
bottomBehaivor = BottomSheetBehavior.from(mBinding.bottomSheet)
bottomBehaivor?.peekHeight = peekHeight
// bottomBehaivor.isHideable = false
//获取屏幕的高度
val screenHeight = resources.displayMetrics.heightPixels
maxScreenHeight = (screenHeight * maxHeightPercentage).toInt()
bottomBehaivor?.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
YzLog.e("bottomBehaivor ->>>$newState")
if (newState == STATE_SETTLING) {
adjustBottomSheetHeight(bottomSheet)
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
})
}
// 调整BottomSheet高度的方法
private fun adjustBottomSheetHeight(bottomSheet: View) {
val currentHeight = bottomSheet.height
if (currentHeight != maxScreenHeight) {
val layoutParams = bottomSheet.layoutParams
if (bottomSheet.height >= maxScreenHeight) {
layoutParams.height = maxScreenHeight
bottomSheet.post {
bottomSheet.setLayoutParams(layoutParams)
}
}
}
}
- 在这我们设置了设置初始显示高度: bottomBehaivor?.peekHeight = peekHeight
- 计算出弹窗最大的显示高度 这边用的是屏幕的最大高度占屏幕的75%:
- 一般BottomSheetBehavior使用的场景为从底部弹出,这种场景下,当设置STATE_STATE_EXPANDED 状态时,经历了STATE_SETTLING -> STATE_EXPANDED 变化。
- 测量弹窗的高度 如果大于最大设定的屏幕最大高度 则限制弹窗的高度为最大高度: