惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
K
Kaspersky official blog
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
U
Unit 42
D
Docker
I
InfoQ
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
C
Check Point Blog
The Cloudflare Blog
美团技术团队
V
Vulnerabilities – Threatpost
博客园_首页
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
A
Arctic Wolf
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Troy Hunt's Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
人人都是产品经理
人人都是产品经理
C
Cyber Attacks, Cyber Crime and Cyber Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
The Register - Security
The Register - Security
博客园 - 三生石上(FineUI控件)
Forbes - Security
Forbes - Security
NISL@THU
NISL@THU
Y
Y Combinator Blog
T
Threatpost
Microsoft Azure Blog
Microsoft Azure Blog
L
Lohrmann on Cybersecurity

人人都是产品经理

为什么你的产品找不到差异化?90%的失败都卡在第一步上(下) – 人人都是产品经理, 3年从30万到1300万用户、获2200万美元融资,这个AI教育产品用“抽卡”破解了获客难题 – 人人都是产品经理, 园区招商系统怎么做才能真正帮到去化?我加了这一个功能,推广链接转发400次阅读过万 – 人人都是产品经理, AI大事件:OpenAI发完网络安全模型又搞药物研发,小鹏汽车要抓”DeepSeek时刻” – 人人都是产品经理, 电商不是卖货,是一场更残酷的产品经理实战 – 人人都是产品经理, 没想到,活动营销又回来了! – 人人都是产品经理, 为何All-in海外KOC:一场关于AI时代窗口期的豪赌 – 人人都是产品经理, 重新理解企业的内部协作 – 人人都是产品经理, 苹果的 AI 战略到底是什么? – 人人都是产品经理, 医疗智能体·第2讲——合规护城河:等保、PIPL与HIPAA的架构实战 – 人人都是产品经理, 向量知识库五步法:从“答非所问”到“精准回复” – 人人都是产品经理, 鸿蒙PC三方库构建总指挥HPKBUILD(sha)库为例 – 人人都是产品经理, 何时该用LLM?AI产品经理的LLM设计指南 – 人人都是产品经理, 医疗信息领域的需求方、决策方、准入方以及关注点(二) – 人人都是产品经理, 即梦涨价:一场被误读的「傲慢」 – 人人都是产品经理, 面试AI PM必答题:Hermes和OpenClaw的区别,如何讲清楚业务价值 – 人人都是产品经理, AI的下一张船票:世界模型——AI产品经理必须理解的技术拐点 – 人人都是产品经理, 小红书做GEO,怎么让AI信你?记住这 3 个重要信息 – 人人都是产品经理, 5 家印度 AI 初创公司,看看印度 AI 再做什么 – 人人都是产品经理, AI项目跨团队协作:产品技术业务如何不打架 – 人人都是产品经理, Agentic Workflow(智能体工作流):让AI从”答案生成器”变成”数字员工” – 人人都是产品经理, lycium_plusplus 项目全景解读:OpenHarmony 三方库构建的“大管家” – 人人都是产品经理, 从爆单救火到前置履约:两套预采策略,把生鲜大促履约效率拉满 – 人人都是产品经理, 什么时候该补货?我用一轮数据做了一个决定 – 人人都是产品经理, 从“机械兜底”到“动态分流”:AI客服重复进线治理的4大底层逻辑 – 人人都是产品经理, 抖音拼效率,红书拼洞察 – 人人都是产品经理, 全民狂欢与退潮——为什么龙虾这波热潮冷却得如此之快? – 人人都是产品经理, Stripe押注!MPP重塑全球支付 – 人人都是产品经理, 小红书GEO:AI引用你的内容,不是因为你对,而是因为你看起来可信 – 人人都是产品经理, 前百度副总裁押注办公Agent,日韩付费爆发,Manus迎来强劲对手 – 人人都是产品经理, 企事业单位数字化的业务供需本质 – 人人都是产品经理, 医疗智能体·第1讲——医疗信息化重构:从“辅助软件”到“自主智能体”的范式转移 – 人人都是产品经理, 粉丝量就是空气!!! – 人人都是产品经理, 用户说“薯片碎了”,机器回“要买吗?”:意图识别的翻车与破局 – 人人都是产品经理, RAG召回准确率从75到90 我做对了这三件事 – 人人都是产品经理, AI大事件:Anthropic改收费、OpenAI发安全版、手术机器人纳入医保、阿里发布”秒悟” – 人人都是产品经理, Chrome 推出 Skills 新功能,Agent 重塑上网方式 – 人人都是产品经理, GitHub前创始人拿了a16z的1700万美元,做Agent时代的Git – 人人都是产品经理 拷贝或克隆其他 Flutter OH 项目到本地后无法运行 – 人人都是产品经理, 优惠券设计:优惠券创建 – 人人都是产品经理, 不用死磕文档!AI 助手 1 小时搞定飞书 CLI 安装 + 配置 + 知识库 – 人人都是产品经理, 用小龙虾做竞品分析报告:从2天到20分钟,我是怎么做到的 – 人人都是产品经理 用小龙虾做市场分析报告:搞懂这3个公式,市场规模不再靠猜 – 人人都是产品经理, 你早就在做 Harness 工程,只是不知道它叫这个名字 – 人人都是产品经理, Think Long就够?你可能想多了! – 人人都是产品经理, 货代SRM实战:供应商准入怎么做,才能让资源池不是通讯录而是可交付网络? – 人人都是产品经理, 如何做好用户调研?详解基本技巧 – 人人都是产品经理, 木鸟、途家、美团对打,平台春天行动开“卷” – 人人都是产品经理, 入职才发现公司不靠谱?小红书从业者求职避坑指南 – 人人都是产品经理, 美国 AI 三巨头联手封堵,中国 AI 突围之路在何方 – 人人都是产品经理, 小红书,放在需求对面的镜子 – 人人都是产品经理, AI 会带来大规模失业吗? – 人人都是产品经理, 从出单到补货前,我第一次犹豫:该不该放大? – 人人都是产品经理, Flutter 三方库鸿蒙化适配:5 种高效检查方式,快速判断是否需要适配 – 人人都是产品经理, 从做产品进阶拿结果:医美机构产品经理转岗科室运营经理 – 人人都是产品经理, 阿里HappyHorse,一场关于“Token经济”的阳谋 – 人人都是产品经理, To B AI:客户留存落地的观察与思考 – 人人都是产品经理, AI产品的“生命线”——数据采集、标注、清洗的产品化设计 – 人人都是产品经理, 谈谈AI Agent(二):当“孩子”能自己“体验世界”时,你该学什么? – 人人都是产品经理, UI/UX设计师的3层能力进阶,前两层让你活下来,第三层…才是真正的分水岭 – 人人都是产品经理, 2分钟 → 30秒,效率提升75%:B端产品经理如何用「规则枷锁」驯服AI幻觉? – 人人都是产品经理, 还没来得及学OpenClaw,来了个更猛的:Hermes Agent – 人人都是产品经理, AI日报:宇树机器人跑出10m/s刷新世界纪录 – 人人都是产品经理, 一文说透基金互金如何用情绪价值引导用户决策做转化 – 人人都是产品经理, 当浏览器开始替你”看”网页:AI 浏览器正在亲手拆掉它脚下的那张网 – 人人都是产品经理, 0代码,一天时间我Vibe Coding了个网站 – 人人都是产品经理, Hermes 和 OpenClaw 之争,Agent 的能力应该“装上去”还是“长出来”? – 人人都是产品经理 视频生成的“桌子”,字节Seedance 2掀完,阿里快乐马掀 – 人人都是产品经理, 从听不懂到完全信任:我的 Codex 深度产品体验 – 人人都是产品经理, 当虚拟偶像有了北京户口,与真人偶像还有什么区别? – 人人都是产品经理, 会说,远远比会做更重要 —— 对 SBTI 爆火现象的五层观察 – 人人都是产品经理, AI产品经理必看:当“搭环境”比“选模型”更重要,你的认知还在2024年吗? – 人人都是产品经理, 2026年AI产品商业化核心逻辑:从功能demo到规模化营收的3个必破卡点 – 人人都是产品经理, 京东围绕供应链,卷起裤腿下场的那些事儿 – 人人都是产品经理, SBTI一夜刷屏:它赢在了“太会说人话” – 人人都是产品经理, 折扣零售的真相:不是便宜,而是价值感! – 人人都是产品经理, 和甲方吵了一架,最后加钱做了——我学到的ToB产品经理生存法则 – 人人都是产品经理, 和几位小红书操盘手聊了8小时,干货全在这 – 人人都是产品经理, 智谱GLM-5.1登场,开源模型首超Opus4.6!!! – 人人都是产品经理 Anthropic收入凭什么反超OpenAI,终于有人把这事说清楚了 – 人人都是产品经理, 史上最有故事感的技术报告——Claude最强模型Mythos 7个极其精彩的细节 – 人人都是产品经理, 模型不是壁垒,Harness 也不是 – 人人都是产品经理, 抖音本地生活业务思考21 – 人人都是产品经理, Superpowers:145k Star的AI编码框架,到底是什么来头? Superpowers:145k Star的AI编码框架,到底是什么来头? – 人人都是产品经理, OpenAI 的路走错了,Anthropic Harness 解法启示:模型需要实践专科生 – 人人都是产品经理, 画原型图的前一步:设计站点地图 – 人人都是产品经理, 给 DeepSeek 的最后一封催更信 – 人人都是产品经理, 手把手教你用 Claude Code 搭建 AI 营销团队:5 个 Agent、12 项技能,独立完成研究、写作、设计全流程 – 人人都是产品经理, 你以为大模型在学语言?不,它在重新发明语言学 – 人人都是产品经理 所谓Skill,不过是AI时代的工业垃圾 – 人人都是产品经理, 聊一聊内容传播的几个方法 – 人人都是产品经理, 当平台开始吃掉生态:从 OpenClaw 被封杀,读懂 Anthropic 的这盘棋 – 人人都是产品经理, 你装了 10 个 AI 插件,Obsidian 还是一个文件夹 – 人人都是产品经理 关于AI智能体架构演进的系统性思考:从单体试水到多体协同的重构 – 人人都是产品经理, 当“人”变成Skill,我们又该何去何从? – 人人都是产品经理 Mythos 事件:前沿 AI 治理的意外实验 – 人人都是产品经理, 货代CRM:信用与风险管理怎么做,才能把坏账风险拦在放货之前? – 人人都是产品经理, 从HR收集自拍照到员工自助录入——我见证了园区人脸识别从”不可用”到”真好用”的全过程 – 人人都是产品经理 千问闯关AI混沌期:阿里画靶,吴嘉张弓,马云射箭? – 人人都是产品经理,
【上新啦】HarmonyOS官方模板优秀案例(第2期:新闻行业 · 综合新闻)
华为开发者联盟 · 2025-08-12 · via 人人都是产品经理

💡 鸿蒙生态为开发者提供海量的HarmonyOS模板/组件,助力开发效率原地起飞 💡

★ 一键直达生态市场组件&模板市场 , 快速应用DevEco Studio插件市场集成组件&模板 ★

HarmonyOS官方模板优秀案例第1期-便捷生活行业收到大家点赞

第2期-新闻行业小编加急上新啦!

本期案例内容更丰富,增加“预加载”、“AI朗读”等行业创新特性介绍

👉 覆盖20+行业,本帖以汇总形式持续更新中,点击收藏!一键三连!常看常新!

【第2期】新闻行业 · 综合新闻

概述

行业洞察

行业诉求:

    • 新闻类应用中,核心关键是内容产出及内容分发效率,当前行业主要痛点是如何提升接收内容效率以及广告分发效率;
    • 跨端场景使用是新闻类应用的重要场景诉求,尤其是PC、PAD、及车机端等全场景的使用及流转分发;
    • 如何借助新技术实现精准内容分发,做到精准生产、精准传播、精准服务,是新闻类应用开发者在思考的问题;
    • 新闻类应用需要为用户带来,自主可控、坚持贴近群众服务群众、易用、流畅、即时的应用使用体验。

行业常用三方SDK

SDK链接:支付宝SDK微信支付SDK银联SDK腾讯QQ SDK新浪微博SDK极光PUSH SDK友盟移动统计SDK腾讯微信SDK高德地图SDK个推BuglyShareSDK听云SDK

说明:“以上三方库及链接仅为示例,三方库由三方开发者独立提供,以其官方内容为准”

  1. 优秀案例概览下载模板

综合新闻行业模板是基于以上行业分析实现的参考,为综合新闻类应用提供了常用功能的开发样例,涵盖新闻分类、列表、详情,精选视频,互动评论,内容分享等多个实用场景。

  • Stage开发模型 + 声明式UI开发范式
  • 分层架构设计 + 组件化拆分,支持开发者在开发时既可以选择完整使用模板,也可以根据需求单独选用其中的业务组件。
  • 集成华为账号、分享、扫码等一方服务和微信登录等三方SDK,开发者仅需做少量配置和定制即可快速体验相关能力。
  • 集成预加载、推送服务、广告服务、AI朗读等创新特性,构建更丰富更全面的模板体验。

本模板主要页面及核心功能如下所示:

二、应用架构设计

  1. 分层模块化设计

产品定制层:专注于满足不同设备或使用场景的个性化需求,作为应用的入口,是用户直接互动的界面。

本实践暂时只支持直板机,为单HAP包形式,包含路由根节点、底部导航栏等。

基础特性层:用于存放相对独立的功能UI和业务逻辑实现。

本实践的基础特性层将应用功能拆分成6个相对独立的业务功能模块。

每个功能模块都具备高内聚、低耦合、可定制的特点,支持产品的灵活部署。

公共能力层:存放公共能力,包括公共UI组件、数据管理、外部交互和工具库等共享功能。

本实践的公共能力层分为公共基础能力和行业组件,均打包为HAR包被基础特性层的业务模块引用。

公共基础能力包含账号管理、动态布局等工具,公共类型定义,网络库,以及弹窗、加载等公共组件。

行业组件将包含行业特点、可完全自闭环的能力抽出独立的组件模块,支持开发者在开发中单独集成使用,详见业务组件设计章节

本模板整体工程分包及介绍如下所示

  • 详细工程结构可见工程结构章节。
​ComprehensiveNews

├──commons                                   // 公共能力层-公共基础能力

│  ├──lib_account                            // 账号登录模块             

│  ├──lib_common                             // 基础模块             

│  ├──lib_flex_layout                        // 动态布局模块             

│  ├──lib_native_components                  // 动态布局-原生模块                  

│  ├──lib_news_api                           // 服务端api模块             

│  ├──lib_news_feed_details                  // 新闻详情模块               

│  └──lib_widget                             // 通用UI模块             

├──components                                // 公共能力层-行业组件

│  ├──module_advertisement                   // 广告组件                     

│  ├──module_channeledit                     // 频道编辑组件

│  ├──module_feedback                        // 意见反馈组件

│  ├──module_feedcomment                     // 评论组件

│  ├──module_highlight                       // 高亮组件

│  ├──module_imagepreview                    // 图片预览组件

│  ├──module_newsfeed                        // 动态卡片组件

│  ├──module_post                            // 发帖组件

│  ├──module_setfontsize                     // 字体大小调节组件

│  ├──module_share                           // 分享组件

│  ├──module_swipeplayer                     // 视频组件

│  └──module_text_reader                     // 朗读组件            

├──features                                  // 基础特性层

│  ├──business_home                          // 首页模块             

│  ├──business_interaction                   // 互动模块                           

│  ├──business_mine                          // 我的模块                        

│  ├──business_profile                       // 个人主页模块             

│  ├──business_setting                       // 设置模块             

│  └──business_video                         // 视频模块             

└──products                                  // 产品定制层

└──phone                                  // 手机设备的主入口模块

2. 业务组件设计

为支持开发者单独获取特定场景的页面和功能,本模板将功能完全自闭环的部分能力抽离出独立的行业组件模块,不依赖公共基础能力包,开发者可以单独集成,开箱即用,降低使用难度。

例如开发者已搭建了一个自己的新闻工程,只想单独取用本模板中的广告功能,可根据本模板中module_advertisement模块的README文件中的快速入门步骤,将该模块单独集成在自己的工程中。

三、行业场景技术方案

  1. 账号管理

场景说明

支持华为账号一键登录及其他方式(账号密码登录、微信登录)。

用户登录后展示昵称和头像,点击用户信息栏可进入用户主页,查看并编辑个人信息和历史动态。

技术方案

华为账号一键登录

通过Account Kit实现华为账号一键登录,并获取用户手机号,关联应用已有用户。

微信登录

根据鸿蒙接入指南接入微信SDK,可通过跳转微信并获取微信用户授权进行登录。

头像修改

通过Scenario Fusion Kit提供的选择头像Button快速拉起头像选择页面,供用户完成华为账号头像或其他头像的选择与展示。

代码参考

部分核心代码参见账号管理实现章节。

  1. 内容分享

场景说明

用户可在首页-新闻详情,视频,互动等多个页面点击分享按钮,拉起分享面板。

用户可通过以下方式实现内容分享。

  • 点击微信、朋友圈、QQ按钮拉起对应应用进行分享操作;
  • 点击生成海报按钮将对应内容生成为携带二维码的海报,分享给上述应用或保存在本地;
  • 点击复制链接将url存入剪切板;
  • 点击系统分享拉起系统分享,支持华为分享,传入小艺智能空间等多种分享方式。

技术方案

微信分享

根据微信鸿蒙SDK接入指南接入微信SDK后,可通过分享功能开发手册调用指定API拉起微信并实现内容分享。

根据QQ鸿蒙SDK接入指南接入QQ SDK后,可通过调用分享接口拉起QQ并实现内容分享。

生成二维码海报并保存

通过系统组件QRCode将分享内容转换为字符串并写入,直接生成对应二维码。

通过弹窗授权保存媒体库资源的方式获取用户授权,并将海报存入相册,无需申请相册管理模块权限ohos.permission.WRITE_IMAGEVIDEO。

复制链接

通过Basic Services Kit中的pasteboard进行内容复制。

系统分享

通过Share Kit接入系统分享面板并发起分享。

代码参考

部分核心代码参见内容分享实现章节。

  1. 扫码识别

场景说明

用户可通过首页右上角的扫码按钮拉起扫码界面,通过摄像头或相册图片完成扫码操作。

应用识别出有效二维码信息可跳转并访问对应内容。

技术方案

    • 使用Scan Kit提供的默认界面扫码能力,可实现系统级体验一致的扫码界面以及相册扫码入口。
    • 开发者也可以通过自定义界面扫码实现更定制化的界面样式和功能。

代码参考

    • 部分核心代码参见扫码识别实现章节。
  1. 视频播放

场景说明

支持竖屏、全屏、长按倍速视频播放,支持当前视频播放完毕后自动续播下一个。

支持用户上下滑动查看上一个或下一个视频。

技术方案

    • 使用Swiper组件+LazyForEach懒加载实现上下滑动视频翻页的效果,并最大限度的降低应用内存占用,提升滑动帧率。
    • 使用AVPlayer进行视频播放,实现播放事件的监听,及全屏、倍速等播放形式的设置。
    • 使用gesture手势响应实现长按倍速等手势事件

代码参考

    • 部分核心代码参见视频播放实现章节。
  1. 应用设置

场景说明

支持应用通用设置。

  • 隐私设置:支持打开或关闭个性化推荐,关闭后首页顶部导航栏不展示推荐栏。
  • 通知开关:支持打开或关闭推送通知,关闭后不接收云端推送消息。
  • 播放与网络设置:支持修改非WLAN网络下的图片和视频播放设置,支持修改视频完播操作和自动播放设置。
  • 清理缓存:支持一键清理应用内缓存。
  • 夜间模式:支持手动开启或关闭应用内夜间模式。
  • 字体大小:支持手动调节应用内字体大小。
  • 检测版本:检测当前安装的应用版本是否为最新的应用市场在架版本。

技术方案

播放与网络设置

使用Network Kit管理网络连接,获取并监控设备的网络状态,进行全局存储。

在图片展示或视频播放页面获取网络状态变量,在流量状态下进行提示或省流处理。

清理缓存

通过Core File Kit查询应用缓存大小并展示。

使用基础文件操作接口获取缓存目录后遍历目录内的文件并删除。

夜间模式和字体大小

通过Ability Kit中环境变量的查询与设置实现应用深浅色模式和字体大小的切换。

检测版本

通过AppGallery Kit主动检查应用是否存在新版本,实现版本检测、显示更新提醒功能。

代码参考

部分核心代码参见应用设置实现章节。

四、行业场景创新特性

  1. 预加载

在应用启动前或初始化阶段,为避免出现首页内容加载慢、白屏等情况,可以通过接入预加载提升应用首开速度,提升用户体验。

本模板使用将首页推荐的新闻资源,在安装应用时通过预加载提前缓存到本地。用户首次访问应用时,直接从缓存中获取数据。减少了从服务器重新下载资源的时间,大大提升了应用首开速度。

同时集成了H5预加载组件FastWeb,对【我的-消息】中的私信对话H5界面进行预渲染、预编译,有效提升应用内H5的加载性能。

部分核心代码参见预加载实现章节。

  1. 广告

为实现流量变现,应用可以通过接入Ads Kit的流量变现服务实现横幅广告、开屏广告、贴片广告等多种形式的广告服务,向用户提供个性化的营销活动或商业广告。

本模板构建了开屏广告原生广告,多样化呈现广告信息。

部分核心代码参见广告实现章节。

  1. 内容推送

应用可以通过接入Push Kit实现从云端到终端的消息推送通道,向应用实时推送消息,提升用户的感知度和活跃度。

本模板集成推送通知消息能力,向用户推送指定新闻内容,可在终端设备的通知中心、锁屏、横幅等展示,用户点击后拉起应用。

部分核心代码参见内容推送实现章节。

  1. AI朗读

用户在接收新闻资讯时,除了常规的文本浏览,还可以通过AI朗读来获取信息。

本模板通过接入Speech Kit的朗读控件,提供了一键播放新闻朗读,并实现朗读内容滚动展示的功能,在用户不方便查看屏幕文字或想通过音频收听内容的时候获取新闻信息。

部分核心代码参见AI朗读实现章节。

五、模板代码

  1. 工程结构下载模板

详细代码结构如下所示:

ComprehensiveNews

├──commons

│  ├──lib_account/src/main/ets                            // 账号登录模块             

│  │    ├──components

│  │    │   └──AgreePrivacyBox.ets                        // 隐私同意勾选                  

│  │    ├──pages

│  │    │   ├──HuaweiLoginPage.ets                        // 华为账号登录页面

│  │    │   ├──OtherLoginPage.ets                         // 其他方式登录页面

│  │    │   └──ProtocolWebView.ets                        // 协议H5                  

│  │    └──utils

│  │        ├──HuaweiAuthUtils.ets                        // 华为认证工具类

│  │        ├──LoginSheetUtils.ets                        // 统一登录半模态弹窗

│  │        └──WXApiUtils.ets                             // 微信登录事件处理类

│  │

│  ├──lib_common/src/main/ets                             // 基础模块             

│  │    ├──constants                                      // 通用常量

│  │    ├──datasource                                     // 懒加载数据模型

│  │    ├──dialogs                                        // 通用弹窗

│  │    ├──models                                         // 状态观测模型

│  │    ├──push                                           // 推送

│  │    └──utils                                          // 通用方法     

│  │

│  ├──lib_flex_layout/src/main/ets                        // 动态布局模块             

│  │    ├──components

│  │    │   ├──AddPlateComp.ets                           // 添加车牌组件

│  │    │   └──GuideListComp.ets                          // 指南列表组件                  

│  │    ├──sdk                                            // 动态布局核心sdk        

│  │    └──views

│  │        └──FlexLayout.ets                             // 动态布局列表页

│  │

│  ├──lib_native_components/src/main/ets                  // 动态布局-原生模块             

│  │    ├──components

│  │    │   ├──AdvertisementCard.ets                      // 广告卡片

│  │    │   ├──FeedDetailsCard.ets                        // 动态卡片

│  │    │   ├──HotListServiceSwitchCard.ets               // 热榜切换组件

│  │    │   ├──HotNewsServiceCard.ets                     // 热榜新闻

│  │    │   ├──LeftTextRightImageCard.ets                 // 左文右图卡片

│  │    │   ├──TopTextBottomBigImageCard.ets              // 上文下图大卡

│  │    │   ├──TopTextBottomImageCard.ets                 // 上文下图卡片

│  │    │   ├──TopTextBottomVideoCard.ets                 // 上文下视频卡片

│  │    │   └──VerticalBigImageCard.ets                   // 上下布局大图

│  │    └─utils

│  │        ├──Modifier.ets                               // 样式modifier

│  │        ├──NodeBuilderConfig.ets                      // node配置类

│  │        └──Utils.ets                                  // 工具方法             

│  │

│  ├──lib_news_api/src/main/ets                           // 服务端api模块             

│  │    ├──constants                                      // 常量文件    

│  │    ├──database                                       // 数据库

│  │    ├──observedmodels                                 // 状态模型  

│  │    ├──params                                         // 请求响应参数

│  │    ├──services                                       // 服务api  

│  │    └──utils                                          // 工具utils

│  │

│  ├──lib_news_feed_details/src/main/ets                  // 新闻详情模块             

│  │    ├──components

│  │    │   ├──ArticleDetailsFooter.ets                   // 文章底部区域

│  │    │   ├──NewsContent.ets                            // 新闻主体内容

│  │    │   └──RecommendArea.ets                          // 相关推荐                  

│  │    └──views

│  │        └──ArticleFeedDetails.ets                     // 新闻详情页      

│  │

│  └──lib_widget/src/main/ets                             // 通用UI模块             

│       └──components

│           ├──ButtonGroup.ets                            // 组合按钮

│           ├──CustomBadge.ets                            // 自定义信息标记组件

│           ├──EmptyBuilder.ets                           // 空白组件

│           └──NavHeaderBar.ets                           // 自定义标题栏

├──components

│  ├──module_advertisement                                // 广告组件                     

│  ├──module_channeledit                                  // 频道编辑组件

│  ├──module_feedback                                     // 意见反馈组件

│  ├──module_feedcomment                                  // 评论组件

│  ├──module_highlight                                    // 高亮组件

│  ├──module_imagepreview                                 // 图片预览组件

│  ├──module_newsfeed                                     // 动态卡片组件

│  ├──module_post                                         // 发帖组件

│  ├──module_setfontsize                                  // 字体大小调节组件

│  ├──module_share                                        // 分享组件

│  ├──module_swipeplayer                                  // 视频组件

│  └──module_text_reader                                  // 朗读组件            

├──features

│  ├──business_home/src/main/ets                          // 首页模块             

│  │    ├──components

│  │    │   └──NewsSearch.ets                             // 搜索页面                  

│  │    └──pages

│  │        └──HomePage.ets                               // 首页页面

│  │

│  ├──business_interaction/src/main/ets                   // 互动模块             

│  │    ├─components

│  │    │   ├──InteractionFeedCard.ets                    // 动态卡片

│  │    │   ├──InterActionTabContent.ets                  // 动态列表

│  │    │   ├──NoWatcher.ets                              // 暂无关注

│  │    │   └──TopBar.ets                                 // 顶部Tab                  

│  │    └──pages

│  │        ├──InteractionPage.ets                        // 互动主页面

│  │        └──PublishPostPage.ets                        // 发帖页面                  

│  │

│  ├──business_mine/src/main/ets                          // 我的模块             

│  │    ├──components

│  │    │   ├──BaseMarkLikePage.ets                       // 收藏点赞基础页面

│  │    │   ├──CancelDialogBuilder.ets                    // 取消收藏点赞弹窗

│  │    │   ├──CommentRoot.ets                            // 主评论

│  │    │   ├──CommentSub.ets                             // 从属评论

│  │    │   ├──FanItem.ets                                // 粉丝单元

│  │    │   ├──IMItem.ets                                 // 私信单元

│  │    │   ├──MessageItem.ets                            // 消息单元

│  │    │   ├──SetReadIcon.ets                            // 标记已读

│  │    │   └──UniformNewsCard.ets                        // 统一新闻卡片                

│  │    └──pages

│  │        ├──CommentPage.ets                            // 评论页面

│  │        ├──HistoryPage.ets                            // 我的历史

│  │        ├──LikePage.ets                               // 我的点赞

│  │        ├──MarkPage.ets                               // 我的收藏

│  │        ├──MessageCommentReplyPage.ets                // 评论与回复

│  │        ├──MessageFansPage.ets                        // 新增粉丝

│  │        ├──MessageIMChatPage.ets                      // 聊天页面

│  │        ├──MessageIMListPage.ets                      // 私信列表

│  │        ├──MessagePage.ets                            // 消息页面

│  │        ├──MessageSingleCommentList.ets               // 全部回复页面

│  │        ├──MessageSystemPage.ets                      // 系统消息

│  │        └──MinePage.ets                               // 我的页面              

│  │

│  ├──business_profile/src/main/ets                       // 个人主页模块             

│  │    ├──components

│  │    │   ├──AuthorItem.ets                             // 作者单元

│  │    │   ├──BaseFollowWatchPage.ets                    // 关注粉丝基础页面

│  │    │   ├──DialogLikeNum.ets                          // 获赞弹窗

│  │    │   ├──TabBar.ets                                 // 顶部Tab

│  │    │   ├──UniformNews.ets                            // 统一新闻卡片

│  │    │   ├──UserIntro.ets                              // 用户信息

│  │    │   └──WatchButton.ets                            // 关注按钮                

│  │    └──pages

│  │        ├──FollowerPage.ets                           // 粉丝页面

│  │        ├──PersonalHomePage.ets                       // 个人主页

│  │        └──WatchPage.ets                              // 关注页面  

│  │

│  ├──business_setting/src/main/ets                       // 设置模块             

│  │    ├──components

│  │    │   ├──SettingCard.ets                            // 设置卡片

│  │    │   └──SettingSelectDialog.ets                    // 设置选项弹窗               

│  │    └──pages

│  │        ├──SettingAbout.ets                           // 关于页面

│  │        ├──SettingFont.ets                            // 字体大小设置页面

│  │        ├──SettingH5.ets                              // H5页面

│  │        ├──SettingNetwork.ets                         // 播放与网络设置页面

│  │        ├──SettingPage.ets                            // 设置页面

│  │        ├──SettingPersonal.ets                        // 编辑个人信息页面

│  │        └──SettingPrivacy.ets                         // 隐私设置页面   

│  │

│  └──business_video/src/main/ets                         // 视频模块             

│       ├──components

│       │   ├──CommentView.ets                            // 评论视图

│       │   ├──Sidebar.ets                                // 侧边栏视图

│       │   ├──TabHeaderView.ets                          // 顶部Tab视图

│       │   └──VideoLayerView.ets                         // 视频外层操作层视图

│       ├──pages

│       │   ├──CommentViewPage.ets                        // 评论页面

│       │   ├──VideoDetailPage.ets                        // 视频详情页

│       │   └──VideoPage.ets                              // 视频首页

│       └──views

│           ├──FeaturedPage.ets                           // 精选页面               

│           ├──FollowPage.ets                             // 关注页面

│           ├──RecommendPage.ets                          // 推荐页面

│           └──VideoSwiperPage.ets                        // 短视频轮播页面

└──products

└──phone/src/main/ets                                  // phone模块

├──common

│   ├──AppTheme.ets                               // 应用主题色

│   ├──Constants.ets                              // 业务常量

│   ├──FormUtils.ets                              // 卡片Utils

│   └──Types.ets                                  // 数据模型

├──components

│   └──CustomTabBar.ets                           // 应用底部Tab

├──pages

│   ├──AgreeDialogPage.ets                        // 隐私同意弹窗

│   ├──Index.ets                                  // 入口页面

│   ├──IndexPage.ets                              // 应用主页面

│   ├──PrivacyPage.ets                            // 查看隐私协议页面

│   ├──SafePage.ets                               // 隐私同意页面

│   ├──SplashPage.ets                             // 开屏广告页面

│   └──StartPage.ets                              // 应用启动页面

└──widget                                         // 服务卡片   

  1. 关键代码解读

本篇代码非应用的全量代码,只包括应用的部分能力的关键代码。

若需获取全量代码,请查看模板集成章节。

账号管理实现

华为账号一键登录

// ComprehensiveNews/commons/lib_account/src/main/ets/viewmodels/LoginVM.ets

@ObservedV2

export class LoginVM extends BaseViewModel {

// 封装登录按钮controller

controller: loginComponentManager.LoginWithHuaweiIDButtonController =

new loginComponentManager.LoginWithHuaweiIDButtonController()

.setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED)

.onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {

if (response) {

Logger.info(TAG, ‘onClickLoginWithHuaweiIDButton success, response: ‘ + JSON.stringify(response));

}

if (error) {

this.handleHuaweiLoginFail(error);

return;

}

this.huaweiLogin();

})

// …

}

// ComprehensiveNews/commons/lib_account/src/main/ets/pages/HuaweiLoginPage.ets

@ComponentV2

struct HuaweiLoginPage {

@Local vm: LoginVM = LoginVM.getInstance(false);

build() {

NavDestination() {

// …

Column() {

// 集成华为账号一键登录按钮

LoginWithHuaweiIDButton({

params: {

style: loginComponentManager.Style.BUTTON_CUSTOM,

extraStyle: {

buttonStyle: new loginComponentManager.ButtonStyle().loadingStyle({

show: true

}).backgroundColor($r(‘app.color.app_theme’))

},

loginType: loginComponentManager.LoginType.QUICK_LOGIN,

supportDarkMode: false,

},

controller: this.vm.controller,

}).id(‘login_with_huaweiId_button’)

}

}

}

}

微信登录

// ComprehensiveNews/commons/lib_account/src/main/ets/viewmodels/LoginVM.ets

@ObservedV2

export class LoginVM extends BaseViewModel {

// 封装微信登录请求

async jumpWX() {

const req = new wxopensdk.SendAuthReq;

/**

     * 填写实际的请求信息

     */

req.isOption1 = false;

req.nonAutomatic = true;

req.scope = ‘snsapi_userinfo’;

req.state = ‘none’;

req.transaction = ‘test123’;

const isJumpSuccess = await wxApi.sendReq(getContext() as common.UIAbilityContext, req);

Logger.info(TAG, ‘jump wechat result: ‘ + isJumpSuccess);

if (!isJumpSuccess) {

promptAction.showToast({ message: ‘未安装微信’ });

}

}

// …

}

内容分享实现

微信分享

// ComprehensiveNews/components/module_share/src/main/ets/viewModel/WxShareViewModel.ets

import * as wXOpenSdk from ‘@tencent/wechat_open_sdk’;

// …

export class WxShareViewModel {

// 此处换成真实appid

wXApi = wXOpenSdk.WXAPIFactory.createWXAPI(‘wxc92d9d6570127a32’)

private static _instance: WxShareViewModel

isWXApp: boolean = this.wXApi.isWXAppInstalled()

isWXAPPCallback(callback: Function) {

if (!this.isWXApp) {

AlertDialog.show({ message: JSON.stringify(‘请先安装微信’) })

} else {

callback()

}

}

/**

   * 分享网页

   * @param text 文本内容

   */

newsWebShare(qrCodeInfo: ShareOptions) {

this.isWXAPPCallback(async () => {

const webpageObject = new wXOpenSdk.WXWebpageObject()

/*

       * 填写真实web地址

       * */

webpageObject.webpageUrl = ”

const mediaMessage = new wXOpenSdk.WXMediaMessage()

mediaMessage.mediaObject = webpageObject

mediaMessage.title = qrCodeInfo.title

mediaMessage.description = `${qrCodeInfo.createTime}`

const thumbData = await getContext(this).resourceManager.getMediaContent($r(‘app.media.startIcon’))

const thumbPixel = image.createImageSource(thumbData.buffer).createPixelMapSync()

const thumbBuffer = await image.createImagePacker().packToData(thumbPixel, { format: ‘image/png’, quality: 100 })

mediaMessage.thumbData = new Uint8Array(thumbBuffer)

const req = new wXOpenSdk.SendMessageToWXReq()

req.scene = wXOpenSdk.SendMessageToWXReq.WXSceneSession

req.message = mediaMessage

this.wXApi.sendReq(getContext(this) as common.UIAbilityContext, req)

})

}

// …

}

QQ分享

// ComprehensiveNews/components/module_share/src/main/ets/viewModel/QqShareViewModel.ets

import { IQQOpenApi, OpenApiConfig, QQOpenApiFactory, ShareResultType } from ‘@tencent/qq-open-sdk’;

// …

/**

 * QQ分享

 * 实际业务请填写实际appid

 */

export class QqShareViewModel {

static appId = 102803516

private static _instance: QqShareViewModel

private static qqOpenApi: IQQOpenApi

shareData: QqShareData = new QqShareData()

public initQqShare(): IQQOpenApi {

if (!QqShareViewModel.qqOpenApi) {

let openApiOption: OpenApiConfig = {

forceEnableWeb: false,

autoHandleAuthResult: true,

}

QqShareViewModel.qqOpenApi =

QQOpenApiFactory.createApi(QqShareViewModel.appId, openApiOption)

}

return QqShareViewModel.qqOpenApi

}

/*

   * 需要根据业务获取实际签名

   * */

public genSign(appId: string | number, shareDataStr: string, timestamp: number, nonce: number) {

let totalStr = `POSTconnect.qq.com/share?appid=${appId}&nonce=${nonce}&ts=${timestamp}&${shareDataStr}`

return ‘业务签名’

}

public handleShareInfo(qrCodeInfo:ShareOptions) {

/*

     * 生成时间戳并计算签名

     * */

let timeStamp = Date.parse(new Date().toString()) / 1000

let nonce = Math.floor(Math.random() * 100000000 + 100)

let shareContent: QqShareContent = {

msg_style: 0,

title: qrCodeInfo.title,

url: ”,

picture_url: qrCodeInfo.coverUrl as string,

}

let shareDataSign = this.genSign(QqShareViewModel.appId, qrCodeInfo.title, timeStamp, nonce)

this.shareData.timestamp = timeStamp

this.shareData.shareJson = JSON.stringify(shareContent)

this.shareData.nonce = nonce

this.shareData.shareJsonSign = shareDataSign

}

public async share(qrCodeInfo:ShareOptions) {

this.handleShareInfo(qrCodeInfo)

let result = await QqShareViewModel.qqOpenApi.share(2, this.shareData);

// …

}

}

系统分享

// ComprehensiveNews/components/module_share/src/main/ets/utils/SystemShare.ets

export class PosterShare {

// …

public async share(qrCodeInfo:ShareOptions) {

let shareData: systemShare.SharedData = new systemShare.SharedData({

utd: utd.UniformDataType.TEXT,

content: ‘www.vmall.com/index.html?cid=128688’,

title: qrCodeInfo.title,

description: `${qrCodeInfo.createTime}`,

});

let controller: systemShare.ShareController = new systemShare.ShareController(shareData);

let context = getContext(this) as common.UIAbilityContext;

controller.show(context, {

selectionMode: systemShare.SelectionMode.SINGLE,

previewMode: systemShare.SharePreviewMode.DETAIL,

}).then(() => {

console.info(‘ShareController show success.’);

}).catch((error: BusinessError) => {

console.error(`ShareController show error. code: ${error.code}, message: ${error.message}`);

});

controller.on(‘dismiss’,() => {

ShareEventDispatcher.dispatchToClose()

})

}

}

扫码识别实现

二维码信息识别

// ComprehensiveNews/commons/lib_common/src/main/ets/utils/NewsScan.ets

export class NewsScan {

static TAG: string = ‘NewsScan’

public scan() {

// 定义扫码参数options

let options: scanBarcode.ScanOptions = {

scanTypes: [scanCore.ScanType.ALL],

enableMultiMode: true,

enableAlbum: true,

};

try {

// 可调用getContext接口获取当前页面关联的UIAbilityContext

scanBarcode.startScanForResult(getContext(this), options).then((result: scanBarcode.ScanResult) => {

// 解析码值结果跳转应用服务页

let resultParams: Record<string, string | NewsEnum> = JSON.parse(result.originalValue)

if(!resultParams?.articleId) {

promptAction.showToast({ message: ‘扫码错误,仅支持模板内文章海报生成的二维码’ })

return

}

let routerParams = {

‘id’: resultParams?.articleId,

} as Record<string, string>

RouterToNews.routerToNewsById(routerParams.id)

}).catch((error: BusinessError) => {

promptAction.showToast({ message: ‘扫码错误’ })

Logger.error(NewsScan.TAG, ‘[Scan CPSample]’,

`Failed to get ScanResult by promise with options. Code:${error.code}, message: ${error.message}`);

});

} catch (error) {

promptAction.showToast({ message: ‘扫码错误’ })

Logger.error(NewsScan.TAG, ‘[Scan CPSample]’,

`Failed to start the scanning service. Code:${error.code}, message: ${error.message}`);

}

}

}

视频播放实现

视频跟手上下滑动效果实现

// ComprehensiveNews/components/module_swipeplayer/src/main/ets/views/VideoSwipePlayer.ets

@ComponentV2({ freezeWhenInactive: true })

export struct VideoSwipePlayer {

// …

build() {

Stack({ alignContent: Alignment.TopStart }) {

Swiper(this.swiperController) {

LazyForEach(this.datasource, (item: VideoPlayerData, index: number) => {

VideoPlayerView({

videoData: item,

index: index,

autoPlay: this.options?.autoPlay,

currentIndex: this.currentIndex,

totalCount: this.options?.totalCount,

swipePlayerController: this.swipePlayerController,

videoItemPlayerSession: this.videoItemPlayerSession,

videoNetwork: this.videoNetwork,

videoNetworkSetting: this.videoNetworkSetting,

videoLayerBuilder: this.videoLayerBuilder,

fullBtnBuilder: this.fullBtnBuilder,

pathStack: this.pathStack,

})

}, (item: VideoPlayerData, index: number) => item.getVideoId() + index);

}

.cachedCount(this.options?.cachedCount ?? 2)

.width(‘100%’)

.height(‘100%’)

.vertical(true)

.loop(false)

.curve(Curve.Ease)

.duration(300)

.indicator(false)

.backgroundColor(Color.Black)

.effectMode(this.currentIndex === 0 ? EdgeEffect.None : EdgeEffect.Spring)

.onChange((index: number) => {

this.currentIndex = index;

if (this.options?.swiperCallback?.onChange) {

this.options.swiperCallback?.onChange(index);

}

})

.onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => {

this.currentIndex = targetIndex;

if (this.options?.swiperCallback?.onAnimationStart) {

this.options.swiperCallback?.onAnimationStart(index, targetIndex, extraInfo);

}

})

}

.width(‘100%’)

.height(‘100%’)

.id(‘player’)

}

}

应用设置实现

清理缓存

// ComprehensiveNews/commons/lib_common/src/main/ets/utils/FileUtils.ets

export class FileUtils {

/**

   * 读取缓存大小,单位Byte

   */

static getCache() {

return storageStatistics.getCurrentBundleStats().then((bundleStats: storageStatistics.BundleStats) => {

Logger.info(TAG, ‘getCurrentBundleStats successfully:’ + JSON.stringify(bundleStats));

Logger.info(TAG, ‘appSize :’ + bundleStats.appSize);

Logger.info(TAG, ‘cacheSize :’ + bundleStats.cacheSize);

Logger.info(TAG, ‘dataSize :’ + bundleStats.dataSize);

return bundleStats.cacheSize;

});

}

/**

   * 清除缓存

   * @param cacheDir

   */

static clearCache() {

const cacheDir = getContext().cacheDir;

Logger.info(TAG, ‘clear cache dir:’ + cacheDir);

fs.listFile(cacheDir).then((filenames) => {

for (let i = 0; i < filenames.length; i++) {

let dirPath = cacheDir + ‘/’ + filenames[i];

Logger.info(TAG, ‘dir path:’ + dirPath);

let isDirectory: boolean = false;

try {

isDirectory = fs.statSync(dirPath).isDirectory();

} catch (e) {

Logger.error(TAG, ‘statSync error:’ + JSON.stringify(e));

}

if (isDirectory) {

fs.rmdirSync(dirPath);

} else {

fs.unlink(dirPath).then(() => {

Logger.info(TAG, ‘remove file succeed’);

}).catch((err: Error) => {

Logger.error(TAG, ‘remove file failed with error message: ‘ + err.message);

});

}

}

})

}

// …

}

检测版本

export class AppGalleryUtils {

/**

   * 是否有更新版本

   * @param context

   * @returns

   */

public static checkAppUpdate(context: common.UIAbilityContext): Promise<boolean> {

try {

return updateManager.checkAppUpdate(context)

.then((checkResult: updateManager.CheckUpdateResult) => {

Logger.info(TAG, ‘Succeeded in checking Result updateAvailable:’ + checkResult.updateAvailable);

return checkResult.updateAvailable === updateManager.UpdateAvailableCode.LATER_VERSION_EXIST;

}).catch((error: BusinessError) => {

Logger.error(TAG, `checkAppUpdate onError.code is ${error.code}, message is ${error.message}`);

return false;

});

} catch (error) {

Logger.error(TAG, `checkAppUpdate onError.code is ${error.code}, message is ${error.message}`);

return Promise.resolve(false);

}

}

/**

   * 显示升级弹窗

   * @param context

   */

public static showUpdateDialog(context: common.UIAbilityContext) {

try {

updateManager.showUpdateDialog(context)

.then((resultCode: updateManager.ShowUpdateResultCode) => {

Logger.info(TAG, ‘Succeeded in showing UpdateDialog resultCode:’ + resultCode);

})

.catch((error: BusinessError) => {

Logger.error(TAG, `showUpdateDialog onError.code is ${error.code}, message is ${error.message}`);

});

} catch (error) {

Logger.error(TAG, `showUpdateDialog onError.code is ${error.code}, message is ${error.message}`);

}

}

}

预加载实现

推荐新闻列表云函数

// ComprehensiveNews/preload/handler.js

let myHandler = async function (event, context, callback, logger) {

logger.info(`Input event: ${JSON.stringify(event)}`);

let hmSystem = {

status: 1,

msg: “success”,

};

logger.info(`hmSystem: ${hmSystem}`);

let recommendList = [

// …

];

logger.info(`tableTitleArr: ${recommendList}`);

let result = { hmSystem, recommendList };

let res = new context.HTTPResponse(

result,

{

“faas-content-type”: “json”,

},

“application/json”,

“200”

);

callback(res);

};

module.exports.myHandler = myHandler;

端侧集成安装预加载

// ComprehensiveNews/products/phone/src/main/ets/phoneability/PhoneAbility.ets

export default class PhoneAbility extends UIAbility {

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {

// …

this.functionPreload();

}

private functionPreload() {

// 获取安装预加载数据

try {

cloudResPrefetch.getPrefetchResult(cloudResPrefetch.PrefetchMode.INSTALL_PREFETCH)

.then((data: cloudResPrefetch.PrefetchResult) => {

let result: RequestListData[] =

(data.result as CloudRecommendListResult<RequestListData[]>).recommendList as RequestListData[]

AppStorage.setOrCreate(‘recommendList’, result)

Logger.info(‘preloadTag’,

`Succeeded in getting install prefetch data, result: ${JSON.stringify(data.result)}`);

}).catch(async (err: BusinessError) => {

Logger.error(‘preloadTag’, `Failed to get install prefetch data, code: ${err.code}, message: ${err.message}`);

// 使用原有方式获取应用数据

let recommendList = await HomeServiceApi.queryHomeRecommendList(‘recommend’);

AppStorage.setOrCreate(‘recommendList’, recommendList)

})

} catch (err) {

Logger.error(‘preloadTag’, `Failed to get install prefetch data, code: ${err.code}, message: ${err.message}`);

}

}

// …

}

广告实现

请求广告

// ComprehensiveNews/components/module_advertisement/src/main/ets/components/Advertisement.ets

@ComponentV2

export struct AdvertisementComponent {

// 请求广告

private async loadAd(adId: string): Promise<void> {

// 1为全屏广告,8为横幅广告

if (this.adType === 1) {

// 广告请求参数

this.adRequestParams = {

// 广告位ID

adId: adId,

// 开屏广告类型

adType: this.adType,

// 请求的广告数量

adCount: 1,

// 开放匿名设备标识符

oaid: this.oaId,

};

this.adDisplayOptions = {

mute: true,

};

this.adOptions = {

allowMobileTraffic: 0,

adContentClassification: ‘A’,

tagForUnderAgeOfPromise: -1,

tagForChildProtection: -1,

};

// 广告请求回调监听

const adLoadListener: advertising.AdLoadListener = {

// 广告请求失败回调

onAdLoadFailure: (errorCode: number, errorMsg: string) => {

hilog.error(0x0000, ‘testTag’, `Failed to load ad. Code is ${errorCode}, message is ${errorMsg}`);

},

// 广告请求成功回调

onAdLoadSuccess: (ads: Array<advertising.Advertisement>) => {

clearTimeout(this.timeOutIndex);

if (this.isTimeOut) {

return;

}

hilog.info(0x0000, ‘testTag’, ‘Succeeded in loading ad’);

this.ad = ads[0];

},

};

// 创建AdLoader广告对象

const adLoader: advertising.AdLoader = new advertising.AdLoader(this.context);

this.timeOutHandler();

// 调用广告请求接口

adLoader.loadAd(this.adRequestParams, this.adOptions, adLoadListener);

} else {

this.adRequestParams = {

adId: adId,

adType: this.adType,

// 宽和高当前只支持360*57和360*144两种尺寸

adWidth: 360,

adHeight: 57,

oaid: this.oaId,

};

this.adDisplayOptions = {

refreshTime: 30000,

};

this.adOptions = {

allowMobileTraffic: 0,

tagForChildProtection: -1,

tagForUnderAgeOfPromise: -1,

adContentClassification: ‘A’,

};

}

}

}

内容推送实现

获取云端推送

// ComprehensiveNews/commons/lib_common/src/main/ets/push/PushUtils.ets

export class PushUtils {

/**

   * 获取push推送token

   **/

public static async getTokenSyn(): Promise<string> {

try {

const pushToken = await pushService.getToken();

return pushToken;

} catch (err) {

let e: BusinessError = err as BusinessError;

Logger.error(‘get token’, ‘Get push token catch error: %{public}d %{public}s’);

return ”;

}

}

/**

   * 本地使用push rest api mock云端推送

   **/

public static async pushMessage(articleList: ESObject) {

/*

     * 这里的Authorization需要开发者自行获取,详情见readme

     * */

axios.post(‘https://push-api.cloud.huawei.com/v3/388421841222475582/messages:send’,

PushUtils.pushMessageParams(articleList), {

headers: {

‘Content-Type’: ‘application/json’,

‘push-type’: 0,

‘Authorization’: ‘Bearer ‘,

},

})

.then((resp: AxiosResponse) => {

Logger.info(‘push success’, JSON.stringify(resp))

})

.catch((err: AxiosError) => {

Logger.info(‘push err’ + err)

})

}

}

AI朗读实现

朗读控件

// ComprehensiveNews/components/module_text_reader/src/main/ets/views/ReadNewsComponent.ets

// 当前受部分API接入限制,仅支持状态管理V1版本

@Component

struct ReadIcon {

/**

   * 包名

   */

@Prop bundleName: string = ”

/**

   * 播放状态

   */

@State readState: ReadStateCode = ReadStateCode.WAITING;

/**

   * 用于显示当前页的按钮状态

   */

@State isInit: boolean = false;

/**

   * 播放加载数据,传入的格式id,title的text,author的text,date的text,bodyInfo内容,封面的PixelMap

   */

@State readInfo: TextReader.ReadInfo | undefined = undefined

/**

   * 用于显示播放的id

   */

@Prop readInfoListString: string

@Prop localCoverImage:PixelMap

@State readInfoList: TextReader.ReadInfo[] | undefined = undefined

/**

   * 用于显示播放的id

   */

@Prop currentId: string

/**

   * 初始化

   */

async init() {

const readerParam: TextReader.ReaderParam = {

isVoiceBrandVisible: true,

businessBrandInfo: {

panelName: this.bundleName,

panelIcon: $r(‘app.media.startIcon’),

},

}

try {

let context: Context | undefined = this.getUIContext().getHostContext()

if (context) {

await TextReader.init(context, readerParam);

this.isInit = true;

}

} catch (err) {

console.error(`TextReader failed to init. Code: ${err.code}, message: ${err.message}`);

}

}

async aboutToAppear() {

this.readInfoList = JSON.parse(this.readInfoListString) as TextReader.ReadInfo[]

this.readInfoList.forEach((item) => {

if (item.id === this.currentId) {

this.readInfo = item

}

})

this.init();

}

onStateChanged = (state: TextReader.ReadState) => {

if (this.readInfo?.id === state.id) {

this.readState = state.state;

} else {

this.readState = ReadStateCode.WAITING;

}

}

// 设置操作监听

setActionListener() {

TextReader.on(‘stateChange’, (state: TextReader.ReadState) => {

this.onStateChanged(state)

});

TextReader.on(‘requestMore’, () => this.onStateChanged);

TextReader.on(‘eventPanel’, (pe: TextReader.PanelEvent) => {

// 监听点击上一条或者下一条音频

if (pe.click === ‘BPC_03’ || pe.click === ‘BPC_04’) {

let event: emitter.InnerEvent = {

eventId: 1,

priority: emitter.EventPriority.IMMEDIATE,

};

let eventData: emitter.EventData = {

data: {

state: pe.id,

},

};

// Send an event with eventId 1 and the event content is eventData

emitter.emit(event, eventData);

}

});

}

build() {

Column() {

TextReaderIcon({ readState: this.readState })

.margin({ right: 20 })

.width(32)

.height(32)

.onClick(async () => {

try {

this.setActionListener();

if (this.readInfoList !== undefined) {

await TextReader.start(this.readInfoList, this.readInfo?.id);

}

} catch (err) {

console.error(`TextReader failed to start. Code: ${err.code}, message: ${err.message}`);

}

})

}

.justifyContent(FlexAlign.Center)

.height(‘100%’)

.width(‘70%’)

.alignItems(HorizontalAlign.End)

}

}

  1. 模板集成

本模板提供了两种代码集成方式,供开发者自由选用。

开发者可以选择直接基于模板工程开发自己的应用工程。

  • 模板代码获取:
  • 通过IDE插件创建模板工程,开发指导
  • 通过生态市场下载源码,下载模板
  • 通过开源仓访问源码,仓库地址
    • 打开模板工程,根据README说明中的快速入门章节,将自己的应用信息配置在模板工程内,即可运行并查看模板效果。

  • 对接开发者自己的服务器接口,转换数据结构,展示真实的云侧数据。
  • ComprehensiveNews/commons/lib_news_api/src/main/ets/services文件中的接口当前为本地mock数据,开发者可根据业务需要替换为真实的服务器接口,并进行云侧数据结构与端侧数据结构的对接转换。

根据自己的业务内容修改模板,进行定制化开发。

  • 按需集成

若开发者已搭建好自己的应用工程,但暂未实现其中的部分场景能力,可以选择取用其中的业务组件,集成在自己的工程中。

  • 组件代码获取:
  • 通过IDE插件下载组件源码。开发指导
  • 通过生态市场下载组件源码。下载地址
    • 下载组件源码,根据README中的说明,将组件包配置在自己的工程中。

  • 根据API参考和示例代码,将组件集成在自己的对应场景中。

以上是第二期“新闻行业-综合新闻”行业优秀案例的内容,更多行业敬请期待~

欢迎下载使用行业模板“点击下载”,若您有体验和开发问题,或者迫不及待想了解XX行业的优秀案例,欢迎在评论区留言,小编会快马加鞭为您解答~

同时诚邀您添加下方二维码加入“组件模板活动社群”,精彩上新&活动不错过!

👉 系列持续更新,欢迎收藏本帖!

第1期:HarmonyOS官方模板优秀案例 | 便捷生活行业 · 购物中心

👉 HarmonyOS组件模板相关推荐

  • 【活动ing】HarmonyOS组件/模板集成创新活动,报名时间截止2025年8月30日,点击查看
  • 鸿蒙应用开发者激励计划2025,点击查看