鸿蒙应用开发实战【13】— AppListItem 应用列表行组件完整实现
鸿蒙应用开发实战【13】— AppListItem 应用列表行组件完整实现前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netAppListItem是号码助手中使用频率最高的组件出现在首页应用列表、状态筛选页、搜索结果页、卡号详情页等多处。它组合了AvatarBadge头像和StatusBadge状态标签并处理了子组件中安全导航这个 ArkTS 的关键难题。核心难点子组件非 Entry中使用router.pushUrl时需要处理 NavigationStack 不可用的问题。AppListItem 采用AppStoragetry/catch双保险方案解决。本篇完整讲解 AppListItem 的设计与实现包括三列弹性布局、布局权重分配、条件渲染、毛玻璃卡片样式等关键技术点。一、AppListItem 组件预览图1AppListItem Row 弹性布局左-中-右三列结构解析layoutWeight 自适应二、功能分析2.1 视觉结构AppListItem 采用经典的三列布局┌─────────────────────────────────────────────────────┐ │ ┌──────┐ 应用名称 ┌──────────┐ │ │ │ 头像 │ ────────── │ 使用中 │ │ │ │ 图标 │ APP · 主卡 └──────────┘ │ │ └──────┘ │ └─────────────────────────────────────────────────────┘三列布局分工如下左列AvatarBadge固定 42vp中列应用名 分类标签 卡号标签弹性占用剩余空间右列StatusBadge自适应内容宽度2.2 Prop 参数设计参数类型默认值说明appNamestring‘’应用名称显示首字作为头像iconColorstring‘#4F7CFF’头像背景颜色hexcategorystring‘APP’应用分类APP/网站/小程序cardLabelstring‘’关联卡号标签可为空statusstring‘使用中’绑定状态bindingIdnumber0绑定记录 ID用于跳转详情三、完整实现代码3.1 组件定义// common/components/AppListItem.etsimport{promptAction,router}fromkit.ArkUIimport{AppColors}from../theme/AppColorsimport{AppFonts}from../theme/AppFontsimport{AvatarBadge}from./AvatarBadgeimport{StatusBadge}from./StatusBadge/** * 应用绑定列表行 — 对应设计稿 app-item * 复用场景首页列表 / 状态筛选页 / 卡号详情 / 搜索页 * * 导航说明因子组件不能可靠地使用 getUIContext().getRouter() * 改用直接 import router try/catch AppStorage 双保险传参 */Componentexportstruct AppListItem{PropappName:stringPropiconColor:string#4F7CFFPropcategory:stringAPPPropcardLabel:string// 空字符串时不显示卡号标签Propstatus:string使用中PropbindingId:number0// ≤0 时表示数据异常build(){Row(){// ── 左列头像图标 ──AvatarBadge({text:this.appName.slice(0,1),// 应用名首字colorStart:this.iconColor,colorEnd:this.iconColor,// 单色不渐变badgeSize:42,radius:12,fontSize:14})// ── 中列应用名 元数据标签 ──Column(){// 应用名称Text(this.appName).fontSize(AppFonts.SIZE_BODY).fontWeight(AppFonts.WEIGHT_MEDIUM).fontColor(AppColors.TEXT).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})// 分类标签 卡号标签水平排列Row({space:6}){// 分类标签始终显示Text(this.category).fontSize(AppFonts.SIZE_MINI).fontColor(AppColors.TEXT_2).border({width:1,color:AppColors.LINE,radius:6}).padding({left:6,right:6,top:1,bottom:1})// 卡号标签只在有卡号时显示if(this.cardLabel.length0){Text(this.cardLabel).fontSize(AppFonts.SIZE_MINI).fontColor(AppColors.TEXT_2).border({width:1,color:AppColors.LINE,radius:6}).padding({left:6,right:6,top:1,bottom:1})}}.margin({top:5})}.alignItems(HorizontalAlign.Start)// 左对齐.layoutWeight(1)// 弹性拉伸占用剩余空间.margin({left:10})// 与头像的间距// ── 右列状态徽标 ──StatusBadge({status:this.status})}.width(100%).padding({top:10,bottom:10,left:12,right:12}).backgroundColor(AppColors.CARD)// 毛玻璃卡片背景.border({width:1,color:AppColors.LINE,radius:16}).margin({bottom:10})// 列表项间距.onClick((){if(this.bindingId0){promptAction.showToast({message:数据异常请刷新后重试})return}try{// 双保险传参AppStorage 是主通道router params 是备用AppStorage.setOrCreatenumber(current_binding_id,this.bindingId)router.pushUrl({url:pages/AppDetailPage,params:{bindingId:this.bindingId}})}catch(err){console.error([AppListItem] 跳转失败:,JSON.stringify(err))promptAction.showToast({message:打开失败请重试})}})}}3.2 组件组合关系// 组件依赖链AppListItem ├── AvatarBadge// 左侧头像│ ├──Proptext │ ├──PropcolorStart/colorEnd │ ├──PropbadgeSize/radius/fontSize │ └── linearGradientText ├── Column中间文本区 │ ├── Text应用名 │ ├── Row │ │ ├── Text分类标签 │ │ └── Text卡号标签条件渲染 │ └──layoutWeight(1)└── StatusBadge// 右侧状态徽标└──Propstatus四、layoutWeight 弹性布局详解4.1 什么是 layoutWeight// layoutWeight 的作用// 在 Row 中将剩余空间按比例分配给设置了 layoutWeight 的子组件Row(){// 左列固定宽度 42vpAvatarBadge 内部 .width(badgeSize)AvatarBadge({badgeSize:42})// 中列layoutWeight(1) → 占用 Row 剩余宽度的全部总宽 - 42 - StatusBadge宽Column(){...}.layoutWeight(1)// 右列StatusBadge 自适应内容宽度Text 的宽度StatusBadge({...})}4.2 多 layoutWeight 按比例分配Row(){Column().layoutWeight(1)// 占剩余空间的 1/3Column().layoutWeight(2)// 占剩余空间的 2/3}// 如果 Row 总宽 300vp则第一个 Column 100vp第二个 200vp4.3 与 flexGrow 的区别属性适用场景语法layoutWeightArkUI Row/Column.layoutWeight(1)flexGrowFlex 容器.flexGrow(1)width(‘100%’)铺满父容器.width(100%)推荐在 ArkUI 的 Row 中使用layoutWeight在 Flex 容器中使用flexGrow。前者更简洁直观。五、条件渲染卡号标签的显示逻辑5.1 if 条件渲染// 只在有卡号时显示卡号标签if(this.cardLabel.length0){Text(this.cardLabel).fontSize(AppFonts.SIZE_MINI)// ...}ArkUI 的if语句在build()中有特殊语义条件为 true → 创建并渲染该节点条件变为 false → 销毁该节点不是隐藏是移除条件再变为 true → 重新创建节点5.2 visibility vs if 的选择// 方案Aif节点不存在—— AppListItem 采用if(this.cardLabel.length0){Text(this.cardLabel)// 无卡号时DOM 中没有这个节点}// 方案Bvisibility节点存在但不可见Text(this.cardLabel).visibility(this.cardLabel.length0?Visibility.Visible:Visibility.None)// 节点始终存在只是隐藏// 选择原则// - 内容频繁切换 → Visibility避免频繁创建销毁// - 初始值确定后很少变化 → if节省内存// - AppListItem 的 Prop 不变 → 选 if六、毛玻璃卡片样式6.1 卡片样式参数// AppListItem 的卡片效果.backgroundColor(AppColors.CARD)// #B8FFFFFF72% 透明白色.border({width:1,color:AppColors.LINE,radius:16})// LINE #14141E3C8% 透明深色边框.margin({bottom:10})// 列表项间距 10vp6.2 毛玻璃效果原理卡片背景 rgba(255,255,255,0.72) 半透明白色 ↓ 叠加在页面背景#F4F6FB 蓝灰色之上 ↓ 显示效果 略带蓝灰色调的半透明白色毛玻璃 加上 1px 淡色边框增加卡片的轮廓感 加上 16vp 圆角增加柔和感七、在 ForEach 中使用 AppListItem7.1 首页的完整用法// HomePage.ets 中的 ForEach AppListItemScroll(){Column(){ForEach(this.getFilteredRows(),(row:AppRow){AppListItem({appName:row.binding.app_name,iconColor:row.binding.icon_key,category:row.binding.category,cardLabel:row.cardLabel,// cardLabelMap 中查找的卡号标签status:row.binding.status,bindingId:row.binding.id??0// ?? 0 确保不传 undefined})},(row:AppRow)binding_${row.binding.id??0}_${row.binding.status})// ↑ key 函数id status 组合精确识别需要更新的项Text().height(90)// 底部留空避免 FAB 遮挡最后一项}.padding({left:16,right:16})}.layoutWeight(1)7.2 key 函数设计思路// key 函数决定 ForEach 如何复用已有节点// ❌ 只用 id状态改变时不重渲染无法感知 status 变化(row:AppRow)${row.binding.id??0}// ❌ 只用 index删除中间项时后续全部重渲染(row:AppRow,index:number)${index}// ✅ id 关键状态精确且高效(row:AppRow)binding_${row.binding.id??0}_${row.binding.status}// 当 status 变化如从使用中改为待换绑该 item 重新渲染// 当 id 不变 status 不变复用已有节点八、本篇小结知识点核心要点Row 三列布局固定宽 layoutWeight(1) 自适应宽layoutWeight弹性分配 Row/Column 剩余空间if 条件渲染节点创建/销毁适合静态条件AppStorage 传参子组件导航的主通道比 router.params 更可靠ForEach keyid status 组合精确渲染控制毛玻璃卡片#B8FFFFFF 1px 淡边框 16vp 圆角参考资料鸿蒙应用开发实战【08】— AppStorage 全局状态与跨页面通信鸿蒙应用开发实战【11】— AvatarBadge 组件鸿蒙应用开发实战【12】— StatusBadge 组件鸿蒙应用开发实战【14】— 毛玻璃卡片颜色格式深度解析Row 容器layoutWeight 属性ForEach 渲染控制visibility 属性textOverflow 属性如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netRow 布局参考https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-rowForEach 使用指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-foreachComponent 开发指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-componentsBorderRadius APIhttps://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-border