鸿蒙 ArkTS 实战|渣男渣女语录分析表:可展开卡片列表 + 类型筛选 + 套路拆解
·



一、设计思路
"语录分析表"本质是一个结构化数据驱动的交互列表:每条语录是一条数据记录,包含类型、原文、套路标签、潜台词、拆解、应对策略六个字段;UI 负责按类型筛选、点击展开详情。代码层面只做两件事——用 interface 定义数据模型、用 @State + ForEach 渲染动态列表。本篇拆解实现。
二、数据模型:一条语录 = 一条记录
interface QuoteItem {
id: number;
type: string; // 渣男 / 渣女
quote: string; // 语录原文
tag: string; // 套路标签
subtext: string; // 潜台词
analysis: string; // 深度拆解
response: string; // 应对策略
}
数据驱动的好处:加一条语录只需在数组里追加一个对象,UI 零改动。"表"就是数组,字段就是列。
@State quotes: QuoteItem[] = [
{
id: 1, type: '渣男',
quote: '我只是把你当妹妹,你误会了。',
tag: '免责声明型',
subtext: '你的付出我照单全收,但关系我概不负责。',
analysis: '先用"妹妹"给关系降级定位,享受照顾与好感的同时提前划清责任边界。',
response: '既然只是兄妹,那关心、礼物、陪伴按兄妹标准来,超出部分免谈。'
},
// ……共 16 条,8 渣男 + 8 渣女
];
三、筛选逻辑:一行 filter
@State filterType: string = '全部';
// 按类型筛选:全部时直接返回,否则过滤
getFiltered(): QuoteItem[] {
if (this.filterType === '全部') {
return this.quotes;
}
return this.quotes.filter((q: QuoteItem) => q.type === this.filterType);
}
筛选 UI 是三个胶囊按钮,点击改 filterType,列表自动重渲染:
Row({ space: 10 }) {
ForEach(['全部', '渣男', '渣女'], (t: string) => {
Text(t)
.fontSize(14)
.fontColor(this.filterType === t ? Color.White : '#4C7DFF')
.backgroundColor(this.filterType === t ? '#4C7DFF' : '#EAF0FF')
.borderRadius(16)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.onClick(() => {
this.filterType = t; // 改状态 → 列表自动刷新
})
}, (t: string) => t)
}
四、展开/收起:一个 @State 控制
@State expandedId: number = -1; // -1 表示全部收起
// 点击同一卡片收起,点击其他卡片切换
toggleExpand(id: number): void {
this.expandedId = (this.expandedId === id) ? -1 : id;
}
卡片内用条件渲染控制展开区:
// 展开区:潜台词 + 套路拆解 + 应对策略
if (this.expandedId === item.id) {
Column({ space: 8 }) {
this.analysisRow('💬 潜台词', item.subtext, '#EF4444')
this.analysisRow('🔍 套路拆解', item.analysis, '#111827')
this.analysisRow('🛡️ 应对策略', item.response, '#10B981')
}
.width('100%')
.margin({ top: 10 })
.padding({ top: 10 })
.border({ width: { top: 1 }, color: '#EEF1F6' })
}
五、列表渲染:List + ForEach + @Builder
List({ space: 10 }) {
ForEach(this.getFiltered(), (item: QuoteItem) => {
ListItem() {
this.quoteCard(item) // 卡片复用 @Builder
}
}, (item: QuoteItem) => item.id.toString()) // 唯一 key
}
.layoutWeight(1).width('100%')
.padding({ left: 14, right: 14, top: 12, bottom: 12 })
.scrollBar(BarState.Off)
@Builder 把卡片封装成可复用组件,传参 item,内部用 .onClick(() => this.toggleExpand(item.id)) 绑定交互:
@Builder
quoteCard(item: QuoteItem) {
Column() {
// 卡片头:类型徽章 + 语录 + 展开箭头
Row({ space: 10 }) {
Text(item.type)
.fontSize(12).fontColor(Color.White)
.backgroundColor(item.type === '渣男' ? '#EF4444' : '#EC4899')
.borderRadius(8)
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
Text(item.quote)
.fontSize(15).fontWeight(FontWeight.Medium).fontColor('#111827')
.layoutWeight(1)
Text(this.expandedId === item.id ? '▲' : '▼')
.fontSize(12).fontColor('#9CA3AF')
}
.width('100%').alignItems(VerticalAlign.Center)
// ……标签行 + 展开区(见上)
}
.width('100%').padding(14)
.backgroundColor(Color.White)
.borderRadius(14)
.onClick(() => { this.toggleExpand(item.id); })
}
六、代码要点
| 技术点 | 实现 | 作用 |
|---|---|---|
| 数据模型 | interface QuoteItem |
一条语录六个字段,结构化 |
| 状态驱动 | @State quotes / filterType / expandedId |
数据一变 UI 自动刷新 |
| 筛选 | Array.filter |
一行代码完成类型过滤 |
| 展开控制 | expandedId 记录当前展开项 |
互斥展开:同时只开一条 |
| 复用 | @Builder quoteCard(item) |
卡片组件化,列表渲染复用 |
| 渲染 | List + ForEach |
长列表懒加载,滚动顺滑 |
七、16 条语录速查表
| # | 类型 | 语录 | 套路标签 |
|---|---|---|---|
| 1 | 渣男 | 我只是把你当妹妹,你误会了 | 免责声明型 |
| 2 | 渣男 | 我配不上你,你值得更好的 | 预防针型 |
| 3 | 渣男 | 我很忙,等有空再找你 | 冷处理型 |
| 4 | 渣男 | 我和她真的只是普通朋友 | 狡辩型 |
| 5 | 渣男 | 等我事业稳定了,就娶你 | 画饼型 |
| 6 | 渣男 | 你太敏感了,我不是那个意思 | 煤气灯型 |
| 7 | 渣男 | 你要是这么想,我也没办法 | 摆烂型 |
| 8 | 渣男 | 我前任总来找我,我也很烦 | 秀行情型 |
| 9 | 渣女 | 你是个好人,但我们不合适 | 好人卡型 |
| 10 | 渣女 | 我拿你当最好的朋友 | 备胎定位型 |
| 11 | 渣女 | 我现在只想搞事业,不想谈恋爱 | 养鱼型 |
| 12 | 渣女 | 你再等等我,等我调整好状态 | 吊着型 |
| 13 | 渣女 | 我们只是误会,你别乱想 | 模糊型 |
| 14 | 渣女 | 他很普通,我只是和他吃个饭 | 否认型 |
| 15 | 渣女 | 你要是再这样,我们就别联系了 | 拿捏型 |
| 16 | 渣女 | 这个礼物不贵重,就当朋友送的 | 试探型 |
八、识别套路的三原则
- 看行为不看解释:语录是话术,拆解看的是"他做了什么",行动永远比台词诚实。
- 警惕免责声明:凡是提前声明"我不配/是误会/别多想"的,都在为将来的行为铺退路。
- 及时止损:识别出套路后,最有效的反击不是争辩,而是降低投入、划清边界、体面离场。
技术结构上,这个页面的 List + @Builder + 筛选模式可以直接复用到任何"语录库 / 错题本 / 速查表"类应用——数据驱动,加内容不改代码。
更多推荐



所有评论(0)