HarmonyOS 应用开发《掌上英语》第53篇:分类选择组件——多级分类的通用解决方案
分类选择组件——多级分类的通用解决方案
引言
在英语学习类应用中,课程内容通常按照学科、年级、知识点等多维度进行分类组织。用户需要在一层层的分类中快速定位到目标内容,这就需要一个高效、易用的多级分类选择组件。本文将以项目中的select_category组件为例,深入剖析多级分类组件的设计思想与实现细节。
select_category是一个独立的 HAR 模块,位于components/select_category/目录下,通过标准的build-profile.json5配置和Index.ets统一导出,对外提供SelectCategory(二级分类)和ThirdCategory(三级分类)两个核心组件。这种模块化设计使得分类选择能力可以被项目中的任何页面按需引用,实现了组件的高度复用。
数据模型设计
分类的嵌套结构
分类数据的核心挑战在于其天然的嵌套特性——一级分类包含多个二级分类,二级分类下又有三级分类。为了清晰表达这种层级关系,select_category组件定义了一套完整的数据模型,所有模型类均使用@ObservedV2装饰器标记,确保属性的变化能够触发 UI 自动更新。
三级分类(最细粒度):
@ObservedV2classItemDetail{@Tracetitle:string=''// 返回对象的标题@Traceid:string=''// ID}ItemDetail是三级分类的最底层节点,代表一个具体可选的分类项。它包含title和id两个字段,足以满足大多数场景下"展示-选中-回调"的需求。
二级到三级的桥接:
@ObservedV2classThirdItemModel{@Traceid:string=''// 三级分类对象ID@Tracetitle:string=''// 标题@Tracelist:ItemDetail[]=[]// 包含的数组}ThirdItemModel是二级分类下包含的三级分类列表,每个三级分类又包含若干ItemDetail作为其叶子节点。这种"列表套列表"的结构,天然对应了 UI 上的树形展开。
最高层级:
@ObservedV2classSecondItemModel{@Traceid:string=''// 二级分类对象ID@Tracelist:ThirdItemModel[]=[]// 三级分类数组@Tracetitle:string=''// 对象的标题}数据传输对象
除了上述实体模型,组件还定义了用于跨页面传参的数据传输对象(DTO),即SecondParam和ThirdParam:
@ObservedV2classSecondParam{@Tracelist:string[]// 面包屑导航标题列表@TracesecondItem:SecondItemModel}@ObservedV2classThirdParam{@Tracelist:string[]// 面包屑导航标题列表@TracethirdItem:ThirdItemModel}list字段是一个字符串数组,承载了面包屑导航中每一级的标题文字。secondItem和thirdItem则分别携带对应层级的具体数据。将导航标题与业务数据分离的设计,使得面包屑的渲染逻辑与内容展示逻辑解耦,各自独立变化。
组件实现
SelectCategory——二级分类组件
SelectCategory组件位于MainPage.ets中,接收SecondParam作为数据源,提供二级分类的浏览和选择能力。
组件接口定义:
@ComponentV2exportstruct SelectCategory{@Param@RequiresecondParamObj:SecondParam;// 数据源@LocalsecondDataList:string[]=[];// 导航标题列表@LocalcontentData?:ThirdItemModel[];// 内容列表@ParamcurrentColor:ResourceStr='#4B5CC4';// 主题色@ParamcontentIcon:ResourceStr=$r('app.media.icon_right');// 列表项图标@EventgoPage:(x:string)=>void=()=>{};// 导航点击事件@EventgoThird:(x:ThirdItemModel)=>void=()=>{};// 进入三级}组件通过@Param接收外部传入的参数,通过@Event向外发射事件。currentColor和contentIcon分别控制选中高亮颜色和列表项箭头图标,使用者可以按需定制,赋予了组件灵活的样式扩展能力。
初始化流程:
aboutToAppear():void{this.initDataSource();}initDataSource(){this.secondDataList=this.secondParamObj.list;this.contentData=this.secondParamObj.secondItem.list;}在aboutToAppear生命周期中完成数据初始化,将传入的SecondParam拆解为导航数据(secondDataList)和内容数据(contentData)。
ThirdCategory——三级分类组件
ThirdCategory组件位于ThirdcatePage.ets中,结构与SelectCategory高度一致,但在数据深度上更进一步。
@ComponentV2exportstruct ThirdCategory{@Param@RequirethirdParamObj:ThirdParam@LocalthirdDataList:string[]=[];@LocalthirdContentsData:ItemDetail[]=[];@ParamcurrentColor:ResourceStr='#4B5CC4'@ParamcontentIcon:ResourceStr=$r('app.media.icon_right');@EventgoPage:(x:string)=>void=()=>{};@EventgoBack:(x:ItemDetail)=>void=()=>{};}与SelectCategory相比,ThirdCategory的goBack事件在用户点击三级分类叶子节点时触发,将选中的ItemDetail回传给调用方,完成整个分类选择流程的闭环。
选中的高亮展示
在分类选择场景中,用户需要清晰地感知"当前处在哪一级"以及"当前选中了哪个分类"。select_category组件通过面包屑导航栏实现了这一交互需求。
导航栏实现:
@BuildertitleBarBuilder(){List(){ForEach(this.secondDataList,(item:string)=>{ListItem(){Row(){Text(item).fontColor(this.secondDataList[this.secondDataList.length-1]===item?this.currentColor:$r('sys.color.font_primary')).fontWeight(this.secondDataList[this.secondDataList.length-1]===item?FontWeight.Medium:FontWeight.Regular)// ...if(this.secondDataList[this.secondDataList?.length-1]!==item){Image($r('app.media.icon_right'))}}}})}.listDirection(Axis.Horizontal).scrollBar(BarState.Off).edgeEffect(EdgeEffect.Spring,{alwaysEnabled:false})}核心判断逻辑非常简洁:面包屑路径中最后一个元素即当前选中的分类。当item等于数组最后一个元素时,文字颜色变为主题色(this.currentColor),字重加粗;其他非选中项则保持默认颜色和普通字重。非选中项右侧显示一个右箭头图标,直观地表明"此处可点击跳转",而选中项不显示箭头,暗示"当前位置"。
这种"最后一个即选中"的判断方式非常适合面包屑导航的线性路径——用户从一级到二级再到三级,路径从头到尾累积,永远只有末尾项是当前所在位置。ThirdCategory的thirdTitleBarBuilder采用了完全相同的逻辑,保证了二级和三级组件在视觉和行为上的一致性。
内容的列表渲染
内容区域同样采用ForEach循环渲染,二级组件渲染ThirdItemModel[],三级组件渲染ItemDetail[]。每个列表项是一个圆角卡片,包含标题文字和右侧箭头图标:
Row(){Text(item.title).fontColor($r('sys.color.font_primary')).fontSize($r('sys.float.Caption_L')).layoutWeight(1)// ...溢出省略Image(this.contentIcon).width($r('app.float.vp_16')).height($r('app.float.vp_16'))}.backgroundColor($r('sys.color.comp_background_tertiary')).borderRadius(10).padding($r('app.float.vp_12'))点击列表项时,二级组件触发goThird事件,三级组件触发goBack事件,分别对应"进入更深层级"和"选中返回"两个交互意图。
模块导出与复用
select_category模块通过Index.ets统一导出,这是 ArkTS 模块化开发的标准实践:
export{SelectCategory}from'./src/main/ets/components/MainPage';export{ThirdCategory}from'./src/main/ets/components/ThirdcatePage';export{ItemDetail,ThirdParam,ThirdItemModel,SecondParam}from'./src/main/ets/model/SelectCateModel';外部页面只需一条 import 语句即可引入完整的分类选择能力:
import{SelectCategory,ThirdCategory,SecondParam,ThirdParam,ItemDetail}from'select_category';总结
select_category组件通过清晰的数据模型设计、一致的面包屑导航逻辑、灵活的参数配置和规范的事件回调,提供了一个可复用的多级分类选择通用解决方案。其核心设计思想——“将层级关系表达为嵌套数据模型”、“用尾部元素判断法实现选中高亮”、“通过 @Param/@Event 实现组件通用化”——对于其他具有层级结构的选择场景(如地址选择、标签筛选、目录导航等)同样具有参考价值。
值得注意的是,组件使用了@ComponentV2装饰器体系(而非 V1 的@Component),这意味着项目已经迁移至 HarmonyOS 最新的组件开发模式,享受更优的响应式性能和更简洁的状态管理语法。