"附近加油站"里有一个很好看的交互:进入地图页后,底部会滑出一个半透明的面板,展示附近的加油站列表。这个效果叫 半屏弹窗 (Bottom Sheet),在 HarmonyOS 里用 bindSheet 实现。
bindSheet 是 ArkUI 里非常强大的 API,参数很多,但用对了能做出 iOS 级别的流畅体验。这篇文章把项目里的用法完整讲解,让你彻底掌握它。
项目预览


bindSheet 是任何组件都可以调用的属性方法,语法:
组件 . bindSheet ( $$isShow , builder ( ) , options )
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
(
)
;
}
)
}
)
;@ State isShow : boolean = false ; . bindSheet ( $$ this . isShow , ... )
$$ 是 ArkUI 的"状态变量双向绑定"语法:
isShow = true :弹窗显示
isShow = false :弹窗关闭
用户下拉关闭弹窗时, isShow 也会自动变为 false
this
.
isShow
=
true
;
setTimeout
(
(
)
=>
{
this
.
isShow
=
true
;
}
,
Constants
.
TIME
)
;detents : [ Constants . BIND_SHEET_HEIGHT , SheetSize . MEDIUM ]
detents 是弹窗可以停留的高度锚点数组,用户可以拖拽在这些高度之间切换:
| 值 | 说明 | | --- | --- | | number (如 400) | 固定高度(px) | | SheetSize.FIT_CONTENT | 自适应内容高度 | | SheetSize.MEDIUM | 屏幕高度的 50% | | SheetSize.LARGE | 屏幕高度的 90% |
默认展示高度:400px(第一个锚点)
上拉后展开至:屏幕 50%( SheetSize.MEDIUM )
用户可以在两个高度之间拖拽切换
dragBar : false ,
默认 dragBar: true 会在弹窗顶部显示一个灰色横条(拖拽指示器)。项目里关闭了,因为有 title 标题作为顶部元素,视觉上更简洁。
title
:
{
title
:
$r
(
'app.string.gas_station'
)
}在弹窗顶部显示标题文字,支持自定义样式:
title
:
{
title
:
'加油站列表'
,
subtitle
:
'附近4个加油站'
}blurStyle : BlurStyle . COMPONENT_THICK ,
BlurStyle 给弹窗背景加上毛玻璃模糊效果,透过弹窗能隐约看到地图:
| BlurStyle | 模糊程度 | 适用场景 | | --- | --- | --- | | THIN | 轻度模糊 | 浅色背景 | | REGULAR | 常规模糊 | 通用 | | THICK | 重度模糊 | 深色背景 | | COMPONENT_THIN | 组件级轻度模糊 | 推荐新项目用 | | COMPONENT_THICK | 组件级重度模糊 | 本项目使用 | | NONE | 无模糊 | 不需要毛玻璃 |
backgroundColor : $r ( 'app.color.bind_sheet_background' ) ,
背景色和 blurStyle 叠加使用,创造半透明毛玻璃效果。
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
(
)
;
}
}
}
)
;
}
)@
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 | 原生动画流畅、支持拖拽、代码简洁 | 样式自定义有限制 | | 自定义(Stack+动画) | 完全自定义样式 | 代码复杂、动画要自己写 | | Dialog | 居中弹窗,简单 | 不适合底部抽屉样式 |
对于底部抽屉效果, 强烈推荐用 bindSheet ,原生的手势交互(拖拽、弹性效果)是自定义组件很难复现的。
$$isShow :双向绑定的布尔变量,控制显示/隐藏
detents :高度锚点数组,定义弹窗可停留的高度
blurStyle :毛玻璃效果, COMPONENT_THICK 效果最好
onWillDismiss :关闭前回调, 必须调用 dismiss() 才会真正关闭
弹窗内容是 @Builder 函数,任何 ArkUI 组件都可以放进去
下一篇讲 GasStationPage 的完整实现解析 ——把整个地图页的代码从头到尾串一遍,让你看懂每一行的作用。
转载声明:本文为转载整理,原文版权归原作者及原站点所有。
原文来源:CSDN HarmonyOS
原创地址:https://blog.csdn.net/qq_33681891/article/details/161706937
原文作者:全栈若城;如有侵权,请联系管理员删除。