HarmonyOS6 半年磨一剑 - RcList 组件事件处理机制与应用示例

HarmonyOS6 半年磨一剑 - RcList 组件事件处理机制与应用示例

文章目录

前言

Hello 各位开发者们大家好, 我是若城,今天我们开始对Rchoui三方库新的组件开始讲解, 本期我们主要讲解的是 RcList 这个组件, 话不多说我们先看下效果图吧~~~

开源计划

项目预计于 2026 年 7 月中旬正式开源,届时可通过三方库直接下载使用。在此期间,我会通过系列文章逐一介绍每个模块的设计思路与实现细节。

rchoui 官网

目前暂定 rchoui 官网地址:http://rchoui.ruocheng.site/

需要注意的是,当前官网还在完善当中,会在后续更新中逐步完善,届时可以为大家提供更加完善的说明文档。

一、触摸事件处理

1.1 触摸事件的用途

RcList 暴露了两个触摸事件,用于需要感知用户手势起止点的场景:

@EventonRcListTouchStart:(event: TouchEvent)=>void=()=>{}@EventonRcListTouchEnd:(event: TouchEvent)=>void=()=>{}

典型场景:

  • 触摸开始:暂停列表内的自动播放
  • 触摸结束:恢复自动播放或触发惯性动画

1.2 触摸事件的内部绑定

.onTouch((event: TouchEvent)=>{if(event.type === TouchType.Down){this.onRcListTouchStart(event)// 手指按下}elseif(event.type === TouchType.Up){this.onRcListTouchEnd(event)// 手指抬起}})

通过判断 event.type 将单一的 onTouch 事件拆分为更语义化的起止事件,简化了使用方的处理逻辑。


二、外部 Scroller 控制

2.1 Scroller 的注入与使用

通过 rcListScroller 参数注入外部 Scroller 实例,可以在组件外部控制列表滚动:

@Entry@ComponentV2 struct ScrollControlDemo {private scroller: Scroller =newScroller()build(){Column(){// 外部控制按钮Row({ space:12}){Button('回到顶部').onClick(()=>{this.scroller.scrollTo({ xOffset:0, yOffset:0})})Button('滚到底部').onClick(()=>{this.scroller.scrollEdge(Edge.Bottom)})}.margin({ bottom:12})// 注入 scrollerRcList({ rcListHeight:400, rcListScrollable:true, rcListScroller:this.scroller }){ForEach(Array.from<number,number>({ length:20},(_:number, i:number):number=> i),(index:number)=>{RcListItem({ rcListItemTitle:`列表项 ${index +1}`, rcListItemNote:`第 ${index +1} 条数据`})},(index:number):string=>`item-${index}`)}}.padding(16)}}

2.2 Scroller 常用 API

方法说明
scrollTo({ xOffset, yOffset })滚动到指定位置
scrollEdge(Edge.Top)滚动到顶部
scrollEdge(Edge.Bottom)滚动到底部
scrollBy(dx, dy)相对当前位置滚动
currentOffset()获取当前滚动偏移量

三、事件综合应用示例

以下是一个完整可运行的页面,综合演示所有交互机制:

import{ RcList, RcListItem }from'rchoui'@Entry@ComponentV2 struct RcListInteractionDemo {// 开关状态@Local switchA:boolean=true@Local switchB:boolean=false@Local switchC:boolean=true// 操作日志@Local logs:string[]=[]// 滚动控制器private scroller: Scroller =newScroller()// 添加日志privateaddLog(msg:string):void{const time =newDate().toLocaleTimeString()this.logs =[`[${time}] ${msg}`,...this.logs.slice(0,9)]}build(){Column({ space:0}){// 日志区域Column(){Text('操作日志').fontSize(14).fontWeight(FontWeight.Medium).margin({ bottom:8})ForEach(this.logs.slice(0,5),(log:string)=>{Text(log).fontSize(12).fontColor('#606266').width('100%')},(log:string):string=> log)if(this.logs.length ===0){Text('暂无操作记录').fontSize(12).fontColor('#c0c4cc')}}.width('100%').padding(12).backgroundColor('#fff').border({ width:{ bottom:1}, color:'#e4e7ed', style: BorderStyle.Solid })Scroll(){Column({ space:16}){// ===== 1. 点击交互 =====Text('点击交互').fontSize(16).fontWeight(FontWeight.Medium).margin({ top:16, bottom:4})RcList({ rcListBorder:true}){RcListItem({ rcListItemTitle:'普通点击项', rcListItemNote:'点击后记录日志', rcListItemClickable:true,onRcListItemClick:()=>{this.addLog('点击了「普通点击项」')}})RcListItem({ rcListItemTitle:'带右侧文字', rcListItemRightText:'详情', rcListItemClickable:true,onRcListItemClick:()=>{this.addLog('点击了「带右侧文字」,即将跳转')}})RcListItem({ rcListItemTitle:'无按压反馈', rcListItemNote:'不设置 clickable,无视觉反馈', rcListItemClickable:false,onRcListItemClick:()=>{this.addLog('点击了「无按压反馈」,事件仍然触发')}})}// ===== 2. 禁用状态 =====Text('禁用状态保护').fontSize(16).fontWeight(FontWeight.Medium).margin({ bottom:4})RcList({ rcListBorder:true}){RcListItem({ rcListItemTitle:'正常可点击', rcListItemNote:'点击会触发事件', rcListItemDisabled:false, rcListItemClickable:true,onRcListItemClick:()=>{this.addLog('「正常项」被点击')}})RcListItem({ rcListItemTitle:'已禁用(不可点击)', rcListItemNote:'即使设置了 clickable 也无效', rcListItemDisabled:true, rcListItemClickable:true,onRcListItemClick:()=>{this.addLog('这行永远不会出现在日志中')}})RcListItem({ rcListItemTitle:'已禁用的开关', rcListItemNote:'开关无法切换', rcListItemShowSwitch:true, rcListItemSwitchChecked:true, rcListItemDisabled:true, rcListItemShowArrow:false})}// ===== 3. 开关交互 =====Text('开关状态管理').fontSize(16).fontWeight(FontWeight.Medium).margin({ bottom:4})RcList({ rcListBorder:true}){RcListItem({ rcListItemTitle:'通知推送', rcListItemNote:this.switchA ?'已开启':'已关闭', rcListItemShowSwitch:true, rcListItemSwitchChecked:this.switchA, rcListItemShowArrow:false,onRcListItemSwitchChange:(checked:boolean)=>{this.switchA = checked this.addLog(`通知推送:${checked ?'开启':'关闭'}`)}})RcListItem({ rcListItemTitle:'自动更新', rcListItemNote:this.switchB ?'已开启':'已关闭', rcListItemShowSwitch:true, rcListItemSwitchChecked:this.switchB, rcListItemShowArrow:false,onRcListItemSwitchChange:(checked:boolean)=>{this.switchB = checked this.addLog(`自动更新:${checked ?'开启':'关闭'}`)}})RcListItem({ rcListItemTitle:'定位服务', rcListItemNote:this.switchC ?'已开启':'已关闭', rcListItemShowSwitch:true, rcListItemSwitchChecked:this.switchC, rcListItemShowArrow:false, rcListItemShowBorder:false,onRcListItemSwitchChange:(checked:boolean)=>{this.switchC = checked this.addLog(`定位服务:${checked ?'开启':'关闭'}`)}})}// ===== 4. 滚动事件 =====Text('滚动事件监听').fontSize(16).fontWeight(FontWeight.Medium).margin({ bottom:4})Row({ space:8}){Button('回到顶部').onClick(()=>{this.scroller.scrollEdge(Edge.Top)this.addLog('手动滚动到顶部')})Button('跳到底部').onClick(()=>{this.scroller.scrollEdge(Edge.Bottom)this.addLog('手动滚动到底部')})}RcList({ rcListHeight:240, rcListScrollable:true, rcListScroller:this.scroller, rcListScrollBarState: BarState.On, rcListBorder:true,onRcListScrollToLower:()=>{this.addLog('滚动到底部,可加载更多')},onRcListScrollToUpper:()=>{this.addLog('滚动到顶部,可下拉刷新')}}){ForEach(Array.from<number,number>({ length:15},(_:number, i:number):number=> i),(index:number)=>{RcListItem({ rcListItemTitle:`滚动列表项 ${index +1}`, rcListItemNote:`这是第 ${index +1} 条数据`, rcListItemRightText:`${index +1}`, rcListItemClickable:true,onRcListItemClick:()=>{this.addLog(`点击了第 ${index +1} 项`)}})},(index:number):string=>`scroll-item-${index}`)}Text('事件演示结束').fontSize(13).fontColor('#909399').textAlign(TextAlign.Center).margin({ top:8, bottom:30})}.width('100%').padding(16)}.layoutWeight(1).backgroundColor('#f5f5f5')}.width('100%').height('100%')}}

四、常见交互场景速查

4.1 场景对照表

场景关键参数说明
列表项可点击导航rcListItemClickable: true + onRcListItemClick标准导航项
开关设置项rcListItemShowSwitch: true + onRcListItemSwitchChange + rcListItemShowArrow: false设置页开关
禁用不可操作rcListItemDisabled: true权限不足/功能未解锁
无分割线末尾项rcListItemShowBorder: false最后一项去掉分割线
无箭头纯文字项rcListItemShowArrow: false展示信息,无跳转
加载更多onRcListScrollToLower + rcListHeight触底加载
下拉刷新触发点onRcListScrollToUpper回顶后刷新
外部控制滚动rcListScroller + Scroller 实例按钮控制回顶

4.2 禁用状态的注意事项

  1. 视觉透明度:禁用时整体 opacity: 0.6,不需要单独修改子元素样式
  2. 事件彻底阻断onRcListItemClickhandleRcListItemClick 中被拦截,不穿透
  3. 开关同步禁用rcListItemDisabled 会自动传递给内部 RcSwitch,无需额外设置
  4. 仅禁用视觉:如果只想改变外观而不阻断事件,可以不用 rcListItemDisabled,直接通过 opacity 修改

总结

RcList 的交互系统经过精心分层:点击反馈事件回调独立控制,禁用保护穿透视觉与行为双层,开关状态通过 @Local 内部状态实现解耦,滚动事件分三粒度按需监听,外部 Scroller 支持命令式控制。每个交互能力都可以独立使用,也可以自由组合,构成了一套完整、健壮的列表交互体系。

掌握这些交互机制,你可以实现设置页面、消息列表、联系人等高频应用场景,而无需关心底层的事件冒泡和状态管理细节。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!

Read more

手写一个C++ TCP服务器实现自定义协议(顺便解决粘包问题)

手写一个C++ TCP服务器实现自定义协议(顺便解决粘包问题)

在之前的博客中,我们了解了关于UDP和TCP的网络编程,直观的感受了一下网络套接字是如何使用的,并且成功的完成了客户端与服务端的网络通信,但是其中还有一个小细节我们可能会忽略,就是UDP是基于数据报进行传输的,一下子就将所有我们要发送的信息传送给对方,但是我们的TCP可是基于字节流进行传输的,我们如何保证读取上来的数据,是一个完整的报文呢? 我们在进行TCP网络通信的时候,通过调用connec函数调用,使客户端可以和服务端保持链接之后,客户端将自己想要发送的数据通过write系统调用写进对应的socket函数调用给我们返回的文件描述符所对应的文件中。 现在有一个问题就是我们向文件中写入的时候,直接将其放入即可,但是想要往出拿的时候就有点困难了,想要往出拿的人如果不知道放的人是如何放的,就会造成一系列的错误,这就好比放数据时先放了一个整形,又放了一个浮点数,还放了一个字符串,然而拿的人按照字符串,整形,浮点数这样的方式进行获取,这就会导致数据不一致的现象,所以一旦我们要发送一些带有结构化的数据时,就必须再次制定——协议,这样才能满足我们想要返送一些结构化数据的需求。 TCP是传输控

By Ne0inhk
【C++笔记】STL知识铺垫

【C++笔记】STL知识铺垫

前言:          在前面的学习中,我们已经掌握了C++的基础语法和编程概念,本文将深入探讨C++标准库的使用,并详细介绍迭代器、auto关键字以及范围for循环等相关知识。          一、STL简介          1.1 什么是STL          STL(Standard Template Library,标准模板库)是C++标准库的核心组成部分,它不仅提供了可复用的组件库,更是一个集成了高效数据结构与算法的软件框架。          1.2 STL的六大组件          由于历史原因,string 类型先于 STL 出现,STL 后来由惠普实验室开发并开源,因此人们通常不将 string 归入 STL 范畴。                   二、迭代器                  迭代器(Iterator)是 C++ STL 中最精妙的设计之一,如果把 STL 的容器比作各种不同类型的仓库(数组、链表、

By Ne0inhk
蓝桥杯手把手教你备战(C/C++ B组)(最全面!最贴心!适合小白!)

蓝桥杯手把手教你备战(C/C++ B组)(最全面!最贴心!适合小白!)

比赛环境:网盘资源分享 通过网盘分享的文件:蓝桥杯比赛环境 链接: https://pan.baidu.com/s/1eh85AW-y83ibCmEo8ByBwA?pwd=1234 提取码: 1234 1 常见问题答疑 1.1 蓝桥杯含金量高不高? 说起蓝桥杯,不得不提ACM。 ACM是国际大学生程序设计竞赛(ACM-ICPC),被誉为计算机领域的“奥运会”,是世界上,规模最大、水平最高、最具影响力的国际大学生程序设计竞赛。 ACM难度较高,当然含金量也更高, 那么蓝桥杯的含金量肯定比不过ACM,但是其具有独特的优势。 蓝桥杯难度更低,更易拿奖,同时在计算机行业具有较高认可度。 ACM适合那些智商高或者编程经验丰富(学习算法1年以上)的选手参赛。而蓝桥杯适合小白,适合期望快速获得编程领域一个认可证书而没有太多时间投入的参赛者。 1.2 获奖到底难不难? 蓝桥杯分为省赛和国赛。 省赛时: 与你竞争的是同省的人,所以获奖难度与你所在的省份有一定关系。 强省(

By Ne0inhk
C++ 多线程同步之原子操作(atomic)实战

C++ 多线程同步之原子操作(atomic)实战

C++ 多线程同步之原子操作(atomic)实战 💡 学习目标:掌握 C++ 标准库中原子操作的使用方法,理解原子操作与互斥锁的区别,能够在轻量级同步场景中高效解决数据竞争问题。 💡 学习重点:std::atomic 模板的常用接口、原子操作的特性、原子类型与普通类型的性能对比、原子操作的典型应用场景。 50.1 原子操作的引入背景 在 48 章我们学习了互斥锁,它通过阻塞线程的方式实现临界区保护。 但互斥锁存在上下文切换开销,在一些简单的同步场景中显得过于笨重。 比如对单个变量的自增、自减、赋值等操作,我们需要一种更轻量级的同步方案——原子操作。 ⚠️ 注意事项:原子操作仅适用于单个变量的简单同步,无法替代互斥锁实现复杂临界区的保护。 举个例子,使用互斥锁保护变量自增: #include<iostream>#include<thread>#include<mutex>usingnamespace std;

By Ne0inhk