文章目录
项目源码地址:https://gitcode.com/HarmonyOS_Samples/DynamicComponent
这个项目叫动态组件,很多人就只盯着广告节点。
但列表流广告要成立,普通内容也得稳定显示。否则广告再动态,列表本身乱了也没意义。
这篇看 CardComponent.ets 和 TypeCasting.ets,讲普通卡片怎么显示,以及资源字符串怎么取出来。

普通卡片从哪里来
在 MainPage.ets 里,非广告数据走这段:
CardComponent({ cardData: item });
也就是说,只要 item.isAdCard() 是 false,页面就创建普通卡片组件。
广告和普通卡片是同一个列表里的两种渲染结果。
CardComponent 接收 CardData
CardComponent.ets 开头是这样:
@Component
export struct CardComponent {
cardData?: CardData;
@State private cardTitle: string = '';
@State private cardAbstract: string = '';
}
它接收一个 cardData,然后用两个状态保存标题和摘要。
为什么标题和摘要不是直接写死?
因为它们来自资源文件,需要通过工具函数转成字符串。

aboutToAppear 里准备标题和摘要
组件出现前,会执行:
aboutToAppear() {
resourceToString(this.getUIContext(), $r('app.string.text_card_title')).then(value => {
this.cardTitle = value + this.cardData!.getId();
})
resourceToString(this.getUIContext(), $r('app.string.text_card_abstract')).then(value => {
this.cardAbstract = value + this.cardData!.getId();
})
}
这段代码做了两件事:
先读取资源里的标题前缀和摘要前缀。
再拼上当前卡片的 id。
所以列表里每张普通卡片都有不同的标题和摘要,看起来像一批模拟内容。
resourceToString 是资源转换工具
工具函数在 entry/src/main/ets/common/TypeCasting.ets:
export async function resourceToString(uiContext: UIContext, source: Resource): Promise<string> {
return uiContext.getHostContext()!.resourceManager.getStringValue(source.id).catch((error: Error) => {
let err = error as BusinessError
if (err.code) {
hilog.error(0x0000, 'ImperativeView', 'Failed to get host context. Cause: %{public}s', JSON.stringify(err) ?? '');
}
return '';
});
}
小白可以先不纠结 BusinessError 和 hilog。
重点是这句:
uiContext.getHostContext()!.resourceManager.getStringValue(source.id)
它通过 UIContext 拿到宿主上下文,再通过 resourceManager 获取字符串资源。
为什么要异步 then
resourceToString() 返回的是 Promise<string>。
所以 CardComponent 里用:
resourceToString(...).then(value => {
this.cardTitle = value + this.cardData!.getId();
})
等资源字符串拿到了,再更新 @State。
@State 更新后,卡片文本会刷新。
这就是普通声明式组件常见的状态更新方式。
卡片 UI 是怎么搭出来的

build() 里核心结构是:
Column() {
Row() {
Image($r('app.media.SampleImage'))
.width($r('app.string.card_image_size'))
.height($r('app.string.card_image_size'))
Column() {
Text(this.cardTitle)
Text(this.cardAbstract)
}
.layoutWeight(1)
}
}
左边是缩略图,右边是标题和摘要。
layoutWeight(1) 表示右侧文本区域尽量占用剩余空间。
这是很常见的列表卡片布局。
文本溢出处理也值得学
标题和摘要都有这样的设置:
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
意思是最多显示一行,超出部分用省略号。
这在列表组件里非常常见。因为卡片宽度有限,真实标题可能很长,如果不处理,布局容易被撑乱。
普通卡片和动态广告的区别
现在可以对比一下:
普通卡片:
CardComponent({ cardData: item });
动态广告:
NodeContainer(getAdNodeController(this.getUIContext(), item.getId()))
普通卡片直接声明组件。
动态广告先创建 Controller,再由 Controller 返回节点。
这就是这个项目最核心的对比。
为什么这篇也重要
如果你只学动态广告,不看普通卡片,很容易误以为整个列表都应该动态化。
其实不是。
项目的设计很克制:普通内容用普通组件,只有广告这种运行时变化更强的模块才用动态节点。
这才是比较合理的工程选择。
收个尾
动态组件不是替代普通组件,而是补充普通组件。
这个项目里,CardComponent 负责稳定的普通内容,NodeContainer + AdController 负责运行时决定的广告内容。两者放在同一个 LazyForEach 里,才构成完整的列表流广告案例。
下一篇我们进入 JSON 动态页面。那一部分不再是局部广告,而是把整页 UI 都交给数据来描述。

240

被折叠的 条评论
为什么被折叠?



