ITPub博客

首页 > 移动开发 > IOS > 技术解读:【HarmonyOS实战】bindSheet半屏弹窗:打造流畅的底部抽屉效果_底部抽屉面板(bottom sheet):

技术解读:【HarmonyOS实战】bindSheet半屏弹窗:打造流畅的底部抽屉效果_底部抽屉面板(bottom sheet):

IOS 作者:LogPivot 时间:2026-10-14 11:03:53 0 删除 编辑

前言

"附近加油站"里有一个很好看的交互:进入地图页后,底部会滑出一个半透明的面板,展示附近的加油站列表。这个效果叫 半屏弹窗 (Bottom Sheet),在 HarmonyOS 里用 bindSheet 实现。

bindSheet 是 ArkUI 里非常强大的 API,参数很多,但用对了能做出 iOS 级别的流畅体验。这篇文章把项目里的用法完整讲解,让你彻底掌握它。

项目预览

一、bindSheet 的基本用法

bindSheet 是任何组件都可以调用的属性方法,语法:

组件
.
bindSheet
(
$$isShow
,
builder
(
)
,
options
)

二、项目中的 bindSheet 实现

Stack
(
)
{
MapComponent
(
{
...
}
)
;
this
.
titleBuilder
(
)
;
}
.
width
(
'100%'
)
.
height
(
'100%'
)
.
bindSheet
(
$$
this
.
isShow
,
this
.
bindBuilder
(
)
,
{
detents
:
[
Constants
.
BIND_SHEET_HEIGHT
,
SheetSize
.
MEDIUM
]
,
dragBar
:
false
,
title
:
{
title
:
$r
(
'app.string.gas_station'
)
}
,
blurStyle
:
BlurStyle
.
COMPONENT_THICK
,
backgroundColor
:
$r
(
'app.color.bind_sheet_background'
)
,
onWillDismiss
:
(
(
dismissSheetAction
:
DismissSheetAction
)
=>
{
if
(
this
.
curMarker
)
{
this
.
imageScale
=
1
;
mapUtil
.
imageAnimation
(
this
.
curMarker
,
this
.
imageScale
)
;
}
dismissSheetAction
.
dismiss
(
)
;
}
)
}
)
;

三、参数详解

3.1 $$this.isShow:双向绑定控制显示

@
State
isShow
:
boolean
=
false
;
.
bindSheet
(
$$
this
.
isShow
,
...
)

$$ 是 ArkUI 的"状态变量双向绑定"语法:

  • isShow = true :弹窗显示

  • isShow = false :弹窗关闭

  • 用户下拉关闭弹窗时, isShow 也会自动变为 false

this
.
isShow
=
true
;
setTimeout
(
(
)
=>
{
this
.
isShow
=
true
;
}
,
Constants
.
TIME
)
;

3.2 detents:弹窗高度配置

detents
:
[
Constants
.
BIND_SHEET_HEIGHT
,
SheetSize
.
MEDIUM
]

detents 是弹窗可以停留的高度锚点数组,用户可以拖拽在这些高度之间切换:

| 值 | 说明 |
| --- | --- |
| number (如 400) | 固定高度(px) |
| SheetSize.FIT_CONTENT | 自适应内容高度 |
| SheetSize.MEDIUM | 屏幕高度的 50% |
| SheetSize.LARGE | 屏幕高度的 90% |
  • 默认展示高度:400px(第一个锚点)

  • 上拉后展开至:屏幕 50%( SheetSize.MEDIUM )

  • 用户可以在两个高度之间拖拽切换

3.3 dragBar:拖拽把手

dragBar
:
false
,

默认 dragBar: true 会在弹窗顶部显示一个灰色横条(拖拽指示器)。项目里关闭了,因为有 title 标题作为顶部元素,视觉上更简洁。

3.4 title:弹窗标题

title
:
{
title
:
$r
(
'app.string.gas_station'
)
}

在弹窗顶部显示标题文字,支持自定义样式:

title
:
{
title
:
'加油站列表'
,
subtitle
:
'附近4个加油站'
}

3.5 blurStyle:毛玻璃效果

blurStyle
:
BlurStyle
.
COMPONENT_THICK
,

BlurStyle 给弹窗背景加上毛玻璃模糊效果,透过弹窗能隐约看到地图:

| BlurStyle | 模糊程度 | 适用场景 |
| --- | --- | --- |
| THIN | 轻度模糊 | 浅色背景 |
| REGULAR | 常规模糊 | 通用 |
| THICK | 重度模糊 | 深色背景 |
| COMPONENT_THIN | 组件级轻度模糊 | 推荐新项目用 |
| COMPONENT_THICK | 组件级重度模糊 | 本项目使用 |
| NONE | 无模糊 | 不需要毛玻璃 |

3.6 backgroundColor:背景颜色

backgroundColor
:
$r
(
'app.color.bind_sheet_background'
)
,

背景色和 blurStyle 叠加使用,创造半透明毛玻璃效果。

3.7 onWillDismiss:关闭前回调

onWillDismiss
:
(
(
dismissSheetAction
:
DismissSheetAction
)
=>
{
if
(
this
.
curMarker
)
{
this
.
imageScale
=
1
;
mapUtil
.
imageAnimation
(
this
.
curMarker
,
this
.
imageScale
)
;
}
dismissSheetAction
.
dismiss
(
)
;
}
)

onWillDismiss 在弹窗即将关闭时(用户下拉或点击遮罩)触发:

  • 恢复当前选中 Marker 的大小

  • 必须调用 dismissSheetAction.dismiss() 才会真正关闭弹窗(否则弹窗不会关闭)

这个设计允许你在"拦截"关闭时做一些操作,比如弹一个确认对话框:

onWillDismiss
:
(
(
action
:
DismissSheetAction
)
=>
{
AlertDialog
.
show
(
{
message
:
'确定关闭吗?'
,
confirm
:
{
value
:
'确定'
,
action
:
(
)
=>
{
action
.
dismiss
(
)
;
}
}
}
)
;
}
)

四、bindBuilder:弹窗内容

@
Builder
bindBuilder
(
)
{
Column
(
)
{
Scroll
(
)
{
if
(
this
.
stationInfoList
&&
this
.
stationInfoList
.
length
>
0
)
{
List
(
)
{
ForEach
(
this
.
stationInfoList
,
(
station
:
StationData
)
=>
{
ListItem
(
)
{
Row
(
{
space
:
Constants
.
SPACE_12
}
)
{
this
.
stationInfoCard
(
station
)
;
}
.
width
(
'100%'
)
;
}
;
}
,
(
station
:
StationData
)
=>
{
return
station
.
id
+
station
.
name
;
}
)
;
}
.
width
(
'100%'
)
;
}
}
.
backgroundColor
(
$r
(
'app.color.start_window_background'
)
)
.
borderRadius
(
Constants
.
BORDER_RADIUS
)
.
margin
(
{
left
:
16
,
right
:
16
}
)
.
opacity
(
Constants
.
ONE
)
.
scrollable
(
ScrollDirection
.
Vertical
)
.
edgeEffect
(
EdgeEffect
.
Fade
)
.
layoutWeight
(
Constants
.
ONE
)
.
align
(
Alignment
.
Top
)
.
height
(
Constants
.
MY_BUILDER_HEIGHT
)
.
width
(
'100%'
)
.
scrollBar
(
BarState
.
Off
)
;
}
.
height
(
Constants
.
MY_BUILDER_COLUMN_HEIGHT
)
;
}

弹窗内容是一个可滚动的加油站列表,用 Scroll 包裹 List 实现滚动, edgeEffect(EdgeEffect.Fade) 让列表边缘有渐隐效果。

五、弹窗的完整生命周期

isShow = false(弹窗隐藏)
  ↓ this.isShow = true(代码触发)
弹窗从底部滑入
  ↓ 动画完成
弹窗显示(默认高度400px)
  ↓ 用户上拉
弹窗扩展(高度50%)
  ↓ 用户下拉或点遮罩
onWillDismiss 触发
  ├─> 恢复 Marker 大小
  └─> dismissSheetAction.dismiss()
       ↓
弹窗从底部滑出
  ↓ 动画完成
isShow = false(自动更新)

六、bindSheet vs 自定义弹窗

| 方式 | 优点 | 缺点 |
| --- | --- | --- |
| bindSheet | 原生动画流畅、支持拖拽、代码简洁 | 样式自定义有限制 |
| 自定义(Stack+动画) | 完全自定义样式 | 代码复杂、动画要自己写 |
| Dialog | 居中弹窗,简单 | 不适合底部抽屉样式 |

对于底部抽屉效果, 强烈推荐用 bindSheet ,原生的手势交互(拖拽、弹性效果)是自定义组件很难复现的。

总结

  • $$isShow :双向绑定的布尔变量,控制显示/隐藏

  • detents :高度锚点数组,定义弹窗可停留的高度

  • blurStyle :毛玻璃效果, COMPONENT_THICK 效果最好

  • onWillDismiss :关闭前回调, 必须调用 dismiss() 才会真正关闭

  • 弹窗内容是 @Builder 函数,任何 ArkUI 组件都可以放进去

下一篇讲 GasStationPage 的完整实现解析 ——把整个地图页的代码从头到尾串一遍,让你看懂每一行的作用。


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:CSDN HarmonyOS

原创地址:https://blog.csdn.net/qq_33681891/article/details/161706937

原文作者:全栈若城;如有侵权,请联系管理员删除。

上一篇: 没有了~
下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2021-03-18

  • 博文量
    1
  • 访问量
    227