【鸿蒙心迹】记得那个被 Scroll 组件 ' 卡' 住的凌晨三点
我在一家普通公司当程序员,公司专门做航标,然后为一些海事和港航道部门提供软件服务。整个团队规模也不大,硬件软件加一起才 10几个人。刚入职那会,软件需求也比较简单。就是对航标进行遥测遥控。报警处置啊,日常维护这些。说起我和鸿蒙的故事。我印象最深的就是那个凌晨三点的夜晚调试和问题解决。
一:事情起因
当时公司为一个航道中心做系统。这个鸿蒙航标控制系统承载着港口调度员的期待。开始去港口调研时,老调度员王师傅指着布满按钮的实体控制台叹气:“小伙子,我们这眼睛早就花了,每次调航标参数,盯着屏幕等加载的那几秒,心都悬着。” 他的话让我在需求书上重重画了条红线:操作延迟必须控制在 0.3 秒内。回去进行开发时。系统首页的结构比想象中复杂得多。最外层的 Scroll 要展示辖区内 12 个水域的总览列表,每个水域卡片里嵌套着横向 Scroll,用来陈列该区域的航标设备,而每个航标卡片内部还有一层实时状态监控条 —— 这三层 Scroll 就像港口的三道防波堤,少了哪一层都无法实现精准调度。最初的代码用传统 NestedScroll 实现,逻辑清晰得像港口的航道图。可实际运行起来的效果却像老式雷达的扫描线,滑动时屏幕会出现明显的残影。我盯着 Profiler 工具里锯齿状的帧率曲线,看着那些低于 50fps 的波谷,突然想起工程师同事老周说的话:“航标系统的每一行代码都系着船锚,你得让数据流动得像水流一样自然,该快的时候要如急流,该缓的时候要似静水。”是啊,这样肯定是不行的。当时系统也是急着使用。
初次嵌套代码:
// 最初的嵌套滑动实现
@Component
struct BeaconList {
@State waterAreas: WaterArea[] = []
@State currentTime: string = ''
aboutToAppear() {
this.loadWaterAreaData() // 加载12个水域的基础数据
this.startRealTimeUpdate() // 启动每秒一次的状态刷新
}
build() {
Scroll() {
Column() {
Text(`实时更新:${this.currentTime}`).fontSize(12).padding(10)
ForEach(this.waterAreas, (area) => {
Scroll({ direction: Axis.Horizontal }) {
Row() {
ForEach(area.beacons, (beacon) => {
BeaconCard({
beacon: beacon,
onStatusChange: (newStatus) => this.updateBeaconStatus(beacon.id, newStatus)
})
})
}.padding(15)
}
.border({ width: 1, color: '#e5e5e5' })
.borderRadius(8)
.margin(10)
})
}
}
}
}
解决其他问题,已经加班到凌晨3点。显示器蓝光在凌晨三点的房间里洇开一片冷色,键盘旁的速溶咖啡已经凉透,杯壁凝着的水珠顺着桌沿滴落在地板上,像在为我第 17 次按下运行键计数。模拟器里的航标控制页面又一次卡在了半空中 ——Scroll 组件的滑动条僵在水域分组卡片中间,实时刷新的航标状态数据像被冻住的浪花,明明 API 9 的文档里白纸黑字写着支持嵌套滑动,可我的代码就像驶入浅滩的船,怎么也驶不进流畅交互的深水区。是换方案?还是找答案?
二、从社区中得来的灵感
回想起圈子里经常说的一句话“自己解决不了的问题,就多在社区看看别人怎么做的”。我开始在社区文章中进行查找,我在开发者社区翻到第 37 篇技术帖时,眼皮已经开始打架。桌上的手机突然震动,是值班室的发来的消息:“明天有大风预警,系统最好能赶在早班调试。” 这句话像冷水浇在头上,我猛地掐了下大腿,强迫自己清醒。就在这时,一篇标题为《鸿蒙(HarmonyOS)性能优化实战-应用列表场景性能提升》的帖子跳进视野。之中提到的列表各种优化思路,给我当头棒喝,我拍着额头才反应过来 —— 辖区内 275个航标、12 个水域分组,每次滑动都要全量渲染,就像让所有航标同时闪烁,不卡顿才怪。
重构代码花了整整两个小时。我把外层的 ForEach 换成 LazyForEach,为每个水域卡片添加了可见性监听,只有当卡片进入屏幕视野时才加载完整数据,还把实时刷新的频率从每秒一次调整为按需更新 —— 当用户停止滑动 300 毫秒后,才触发状态同步。当模拟器里的界面第一次实现无卡顿滑动,航标状态随着指尖滑动流畅切换时,我盯着屏幕笑出了声。窗外的天已经泛白,晨光透过纱窗在代码上投下细碎的光斑,像洒在水面的阳光,那一刻突然觉得所有的熬夜都有了意义。
感谢社区大佬的各种分享,也给我打开了开发的新思路。
优化后,部分代码:
// 优化后的嵌套滑动实现
@Component
struct OptimizedBeaconList {
@State waterAreas: WaterArea[] = []
@State currentTime: string = ''
@State isScrolling: boolean = false // 标记是否正在滑动
private scroller = new Scroller()
private areaDataSource: LazyForEachDataSource<WaterArea> = new class implements LazyForEachDataSource<WaterArea> {
private data: WaterArea[] = []
constructor(data: WaterArea[]) {
this.data = data
}
totalCount(): number {
return this.data.length
}
getData(index: number): WaterArea {
return this.data[index]
}
registerDataChangeListener(listener: DataChangeListener): void {}
unregisterDataChangeListener(listener: DataChangeListener): void {}
}(this.waterAreas)
aboutToAppear() {
this.loadWaterAreaBasicData() // 仅加载基础数据
this.startScrollListener() // 监听滑动状态
}
// 监听滑动状态,控制数据刷新时机
startScrollListener() {
this.scroller.onScroll(() => {
this.isScrolling = true
// 滑动停止300ms后允许刷新
setTimeout(() => {
this.isScrolling = false
if (!this.isScrolling) {
this.syncVisibleAreaStatus()
}
}, 300)
})
}
// 只同步可见区域的航标状态
syncVisibleAreaStatus() {
const visibleAreas = this.getVisibleAreas() // 获取当前可见的水域卡片
visibleAreas.forEach(area => {
this.loadBeaconRealTimeData(area.id)
})
}
build() {
Scroll(this.scroller) {
Column() {
Text(`实时更新:${this.currentTime}`).fontSize(12).padding(10)
LazyForEach(this.areaDataSource, (area) => {
Scroll({ direction: Axis.Horizontal }) {
Row() {
LazyForEach(new BeaconDataSource(area.beacons), (beacon) => {
BeaconCard({
beacon: beacon,
onStatusChange: (newStatus) => this.updateBeaconStatus(beacon.id, newStatus),
visible: this.isAreaVisible(area.id) // 控制卡片渲染时机
})
})
}.padding(15)
}
.border({ width: 1, color: '#e5e5e5' })
.borderRadius(8)
.margin(10)
.onVisibleAreaChange((isVisible: boolean) => {
if (isVisible) {
this.loadAreaDetailData(area.id) // 进入视野时加载详细数据
}
})
})
}
}
}
}
三、最终结局-指尖的 "航标灯"
最后在港航中心进行现场测试时,老调度员王师傅用布满老茧的手指划过屏幕,航标状态在他指尖流畅切换。“以前调个数据要等三秒,现在跟摸实体控制台一样顺手。” 他指着屏幕上跳动的绿灯说,“上次大风天天气,就是因为系统反应慢了半秒,差点让巡逻艇走错航道。这才是能救命的系统。”站在一旁的我突然想起那个被 Scroll 组件困住的夜晚。原来开发里的 “小确幸” 从不是突然降临的,它藏在反复调试的代码里,躲在社区前辈的经验分享中,藏在用户每一次流畅的操作里。就像港口的航标灯,看似只是微弱的光芒,却能在黑夜中为航船指引方向。而我们写下的每一行代码,或许也在悄悄守护着某个不为人知的角落,在技术的海洋里亮起温暖的光。
更多推荐
所有评论(0)