HarmonyOS开发实例:【分布式新闻客户端】

server/2024/10/21 9:18:29/

 

介绍

本篇Codelab基于栅格布局、设备管理和多端协同,实现一次开发,多端部署的分布式新闻客户端页面。主要包含以下功能:

  1. 展示新闻列表以及左右滑动切换新闻Tab。
  2. 点击新闻展示新闻详情页。
  3. 点击新闻详情页底部的分享按钮,发现周边处在同一无线网络下的设备并进行可信认证连接。
  4. 可信认证后,再次点击分享按钮,选择已连接的设备进行跨设备启动UIAbility。

最终效果图如下:

相关概念

  • [栅格布局]:一种通用的辅助定位工具,解决多尺寸多设备的动态布局问题。
  • [设备管理]:模块提供分布式设备管理能力。
  • [跨设备启动UIAbility]:多端上的不同UIAbility/ServiceExtensionAbility同时运行、或者交替运行实现完整的业务。
  • [Tabs组件]:通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。

相关权限

本篇Codelab使用了设备管理及跨设备实现多端协同能力,需要手动替换full-SDK,并在配置文件module.json5文件requestPermissions属性中添加如下权限:

  • [分布式设备认证组网权限]:ohos.permission.ACCESS_SERVICE_DM。
  • [设备间的数据交换权限]:ohos.permission.DISTRIBUTED_DATASYNC。

约束与限制

  1. 本篇Codelab部分能力依赖于系统API,需下载full-SDK并替换DevEco Studio自动下载的public-SDK。具体操作可参考指南[《如何替换full-SDK》]。
  2. 本篇Codelab使用的部分API仅系统应用可用,需要提升应用等级。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 4.0 Beta2。
  • OpenHarmony SDK版本:API version 10。
  • 鸿蒙开发>鸿蒙开发文档指导:qr23.cn/AKFP8k点击或复制转到。

硬件要求

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:4.0 Beta1。 -鸿蒙HarmonyOS与OpenHarmony开发籽料mau123789是v直接拿

搜狗高速浏览器截图20240326151450.png

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以4.0 Beta1版本为例:

  1. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  2. 搭建开发环境。

    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──entry/src/main/ets                   // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets         // 常量类
│  │  └──utils
│  │     └──Logger.ets                  // 日志工具类
│  ├──entryability
│  │  └──EntryAbility.ets               // 程序入口类
│  ├──model
│  │  └──RemoteDeviceModel.ets          // 设备管理类
│  ├──pages
│  │  ├──Index.ets                      // 新闻列表页
│  │  └──NewsDetail.ets                 // 新闻详情页
│  ├──view
│  │  ├──DetailFooter.ets               // 详情页页脚
│  │  ├──DetailHeadContent.ets          // 新闻详情
│  │  ├──DeviceListDialog.ets           // 设备列表弹窗
│  │  ├──NewsList.ets                   // 新闻列表
│  │  └──NewsTab.ets                    // 新闻页签
│  └──viewmodel
│     └──NewsDataModel.ets              // 新闻数据处理
└──entry/src/main/resources             // 资源文件目录

构建新闻列表页

新闻列表页由页签区域和新闻列表区域组成,页签区域为自定义布局TabBuilder,新闻列表区域为Tabs组件嵌套List组件,并适配不同尺寸设备对应的栅格。新闻列表页能够左右滑动或点击页签切换新闻Tab,并设置点击新闻跳转至新闻详情页。

 
// NewsTab.ets
@Component
export default struct NewsTab {@State currentIndex: number = 0;@State currentBreakpoint: string = CommonConstants.BREAKPOINT_SM;private newsItems: NewsData[] = [];// 自定义页签栏@Builder TabBuilder(title: Resource, index: number) {Row() {Text(title).fontSize(this.currentIndex === index ? $r('app.float.lager_font_size') : $r('app.float.middle_font_size')).fontWeight(this.currentIndex === index ? CommonConstants.FONT_WEIGHT_500 : FontWeight.Normal).fontColor(this.currentIndex === index ? $r('app.color.tab_font_select') : $r('app.color.font_color_gray'))}.layoutWeight(1).margin({right: $r('app.float.news_tab_margin_right'),left: (this.currentBreakpoint === CommonConstants.BREAKPOINT_SM && index === 0) ?$r('app.float.news_tab_margin_left') : 0}).height(this.currentIndex === index ? $r('app.float.news_tab_current_height') : $r('app.float.news_tab_height'))}build() {...Tabs() {ForEach(CommonConstants.ALL_TITLE, (title: string, index: number) => {TabContent() {// 新闻内容列表NewsList({ newsItems: NewsDataModel.getNewsByType(this.newsItems, title) })}.tabBar(this.TabBuilder(NewsDataModel.getTypeByStr(title), index))}, (title: string, index: number) => index + JSON.stringify(title))}.barHeight($r('app.float.news_tab_bar_height')).barWidth(CommonConstants.FULL_COMPONENT).barMode(this.currentBreakpoint === CommonConstants.BREAKPOINT_SM ? BarMode.Scrollable : BarMode.Fixed).onChange((index: number) => {this.currentIndex = index;})...}
}// NewsList.ets
@Component
export default struct NewsList {private newsItems: NewsData[] = [];build() {List() {ForEach(this.newsItems, (item: NewsData, index: number) => {ListItem() {// 栅格布局GridRow({columns: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.TWELVE_COLUMN},breakpoints: {value: [CommonConstants.SMALL_DEVICE_TYPE,CommonConstants.MIDDLE_DEVICE_TYPE,CommonConstants.LARGE_DEVICE_TYPE]},gutter: { x: $r('app.float.grid_row_gutter') }}) {GridCol({span: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.EIGHT_COLUMN},offset: {sm: CommonConstants.ZERO_COLUMN,md: CommonConstants.ZERO_COLUMN,lg: CommonConstants.TWO_COLUMN}}) {NewsItem({ newsItem: item, isLast: index === this.newsItems.length - 1 })}}}}, (item: NewsData, index: number) => index + JSON.stringify(item))}.height(CommonConstants.FULL_COMPONENT)}
}

构建新闻详情页

新闻详情页

新闻详情页由新闻内容区域和页脚区域组成,其中新闻内容区域为Scroll组件嵌套栅格组件展示新闻详情,页脚区域为栅格布局,包含TextInput组件和三个按钮图标。

 
// DetailHeadContent.ets
build() {Column() {...// 可滚动的容器组件Scroll() {// 栅格布局GridRow({columns: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.TWELVE_COLUMN},breakpoints: {value: [CommonConstants.SMALL_DEVICE_TYPE,CommonConstants.MIDDLE_DEVICE_TYPE,CommonConstants.LARGE_DEVICE_TYPE]},gutter: { x: $r('app.float.grid_row_gutter') }}) {GridCol({span: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.EIGHT_COLUMN},offset: {sm: CommonConstants.ZERO_COLUMN,md: CommonConstants.ZERO_COLUMN,lg: CommonConstants.TWO_COLUMN}}) {...}...}}.padding({bottom: $r('app.float.news_detail_padding_bottom')}).scrollBar(BarState.Off)}.margin({left: $r('app.float.news_detail_margin'),right: $r('app.float.news_detail_margin')}).height(CommonConstants.FULL_COMPONENT).alignItems(HorizontalAlign.Start)
}// DetailFooter.ets
build() {Column() {// 分割线Divider().color($r('app.color.detail_divider_color')).width(CommonConstants.FULL_COMPONENT)// 栅格布局GridRow({columns: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.TWELVE_COLUMN},breakpoints: {value: [CommonConstants.SMALL_DEVICE_TYPE,CommonConstants.MIDDLE_DEVICE_TYPE,CommonConstants.LARGE_DEVICE_TYPE]},gutter: { x: $r('app.float.grid_row_gutter') }}) {GridCol({span: {sm: CommonConstants.FOUR_COLUMN,md: CommonConstants.EIGHT_COLUMN,lg: CommonConstants.EIGHT_COLUMN},offset: {sm: CommonConstants.ZERO_COLUMN,md: CommonConstants.ZERO_COLUMN,lg: CommonConstants.TWO_COLUMN}}) {...}.margin({left: this.currentBreakpoint === CommonConstants.BREAKPOINT_SM ? $r('app.float.footer_margin_sm') :$r('app.float.footer_margin_other'),right: this.currentBreakpoint === CommonConstants.BREAKPOINT_SM ? $r('app.float.footer_margin_sm') :$r('app.float.footer_margin_other')})}.backgroundColor($r('app.color.bg_color_gray')).height($r('app.float.footer_height')).width(CommonConstants.FULL_COMPONENT).onBreakpointChange((breakpoints) => {...})}
}

分享按钮弹窗

页脚点击分享按钮,弹出自定义弹窗DeviceListDialog,用于多端协同拉起应用。DeviceListDialog由两个标题栏和两个List组件构成,其中List组件使用ForEach循环渲染设备数据。

 
// DeviceListDialog.ets
build() {Column() {Row() {...}.height($r('app.float.choose_device_row_height')).width(CommonConstants.FULL_COMPONENT).padding({left: $r('app.float.dialog_padding'),right: $r('app.float.dialog_padding')})// 信任设备列表List() {ForEach(this.trustedDeviceList, (item: deviceManager.DeviceInfo, index: number) => {ListItem() {...}}, (item: deviceManager.DeviceInfo) => JSON.stringify(item.deviceId))}Row() {...}.height($r('app.float.choose_device_row_height')).width(CommonConstants.FULL_COMPONENT).padding({left: $r('app.float.dialog_padding'),right: $r('app.float.dialog_padding')})// 发现设备列表List() {ForEach(this.discoverDeviceList, (item: deviceManager.DeviceInfo, index: number) => {ListItem() {...}}, (item: deviceManager.DeviceInfo) => JSON.stringify(item.deviceId))}Row() {...}.height($r('app.float.dialog_button_row_height')).padding({top: $r('app.float.dialog_button_padding_top'),bottom: $r('app.float.dialog_button_padding_bottom'),left: $r('app.float.dialog_padding'),right: $r('app.float.dialog_padding')}).width(CommonConstants.FULL_COMPONENT)}.borderRadius($r('app.float.dialog_border_radius')).backgroundColor($r('app.color.device_dialog_background')).width(CommonConstants.FULL_COMPONENT)
}

多端协同拉起应用

创建设备管理器

应用创建时创建一个设备管理器实例,注册设备状态监听和获取信任的设备列表。其中deviceManager类需使用full-SDK。

// EntryAbility.ets
onCreate(want: Want) {...// 创建设备管理器RemoteDeviceModel.createDeviceManager(this.context);
}// RemoteDeviceModel.ets
async createDeviceManager(context: common.UIAbilityContext): Promise<void> {if (this.deviceManager !== undefined) {return;}await new Promise((resolve: (value: Object | PromiseLike<Object>) => void, reject:((reason?: RejectError) => void)) => {deviceManager.createDeviceManager(context.abilityInfo.bundleName, (err, value) => {if (err) {reject(err);logger.error('createDeviceManager failed.');return;}this.deviceManager = value;// 注册设备状态监听this.registerDeviceStateListener();// 获取信任设备列表this.getTrustedDeviceList();resolve(value);})})
}

发现设备

用户点击新闻详情页底部的分享按钮,调用startDeviceDiscovery()方法,发现周边处在同一无线网络下的设备并添加设备至已发现的设备列表。

// RemoteDeviceModel.ets
startDeviceDiscovery(): void {if (this.deviceManager === undefined) {logger.error('deviceManager has not initialized');this.showToast($r('app.string.no_device_manager'));return;}this.deviceManager.on('deviceFound', (data) => {if (data === null) {return;}// 监听设备发现this.deviceFound(data);})this.deviceManager.on('discoverFail', (data) => {logger.error(`discoverFail data = ${JSON.stringify(data)}`);})this.deviceManager.on('serviceDie', () => {logger.error('serviceDie');})let info: deviceManager.SubscribeInfo = {subscribeId: SUBSCRIBE_ID,mode: CommonConstants.INFO_MODE,medium: 0,freq: CommonConstants.INFO_FREQ,isSameAccount: false,isWakeRemote: true,capability: 0};// 添加设备至发现列表this.discoverList = [];AppStorage.setOrCreate(CommonConstants.DISCOVER_DEVICE_LIST, this.discoverList);try {this.deviceManager.startDeviceDiscovery(info);} catch (err) {logger.error(`startDeviceDiscovery failed error = ${JSON.stringify(err)}`);}
}

进行可信认证连接

在已发现的设备列表中选择设备,调用authenticateDevice()方法进行可信认证,输入PIN码,连接设备,将设备改为信任状态,添加至已信任设备列表。

// RemoteDeviceModel.ets
authenticateDevice(device: deviceManager.DeviceInfo, context: common.UIAbilityContext): void {if (this.deviceManager === undefined) {logger.error('deviceManager has not initialized');this.showToast($r('app.string.no_device_manager'));return;}for (let i: number = 0; i < this.discoverList.length; i++) {if (this.discoverList[i].deviceId !== device.deviceId) {continue;}let extraInfo: AuthExtraInfoInterface = {targetPkgName: context.abilityInfo.bundleName,appName: context.applicationInfo.name,appDescription: context.applicationInfo.description,business: CommonConstants.ZERO};let authParam: deviceManager.AuthParam = {'authType': CommonConstants.ONE,'extraInfo': extraInfo};try {// 可信认证this.deviceManager.authenticateDevice(device, authParam, (err) => {if (err) {logger.error(`authenticateDevice error. Code is ${err.code}, message is ${err.message}`);return;}})} catch (err) {logger.error(`authenticateDevice failed error = ${JSON.stringify(err)}`);}}
}

跨设备启动UIAbility

可信认证后,用户再次点击分享按钮,选择已信任设备列表中的设备,调用startAbilityContinuation()方法进行拉起应用,在另一设备中触发aboutToAppear()方法渲染当前的新闻详情页,实现跨设备启动UIAbility。

// DeviceListDialog.ets
function startAbilityContinuation(deviceId: string, newsId: string, context: common.UIAbilityContext): void {let want: Want = {deviceId: deviceId,bundleName: context.abilityInfo.bundleName,abilityName: CommonConstants.ABILITY_NAME,parameters: {newsId: newsId}};// 拉起应用context.startAbility(want).catch((err: Error) => {Logger.error(`startAbilityContinuation failed error = ${JSON.stringify(err)}`);prompt.showToast({message: $r('app.string.start_ability_continuation_error')});})
}// NewsDetail.ets
aboutToAppear() {let newsId: string | undefined = AppStorage.get<string>('wantNewsId');if (newsId === undefined) {this.newsData = (router.getParams() as Record<string, NewsData>)['newsItem'];return;}// 读取跨设备传递的参数信息this.newsData = this.newsItems.filter((item: NewsData) => (item.newsId === newsId))[0];
}

最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发>鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发>鸿蒙开发的学习路线图。

高清完整版请点击→鸿蒙NEXT星河版开发学习文档》

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙OpenHarmony )学习手册(共计1236页)与鸿蒙OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

鸿蒙 (OpenHarmony)开发学习视频》

图片

鸿蒙生态应用开发V2.0白皮书》

图片

鸿蒙 (OpenHarmony)开发基础到实战手册》

获取这份鸿蒙星河版学习资料,请点击→鸿蒙NEXT星河版开发学习文档》

OpenHarmony北向、南向开发环境搭建

图片

鸿蒙开发>鸿蒙开发基础》

  1. ArkTS语言

  2. 安装DevEco Studio

  3. 运用你的第一个ArkTS应用

  4. ArkUI声明式UI开发

  5. .……

图片

鸿蒙开发>鸿蒙开发进阶》

  1. Stage模型入门

  2. 网络管理

  3. 数据管理

  4. 电话服务

  5. 分布式应用开发

  6. 通知与窗口管理

  7. 多媒体技术

  8. 安全技能

  9. 任务管理

  10. WebGL

  11. 国际化开发

  12. 应用测试

  13. DFX面向未来设计

  14. 鸿蒙系统>鸿蒙系统移植和裁剪定制

  15. ……

图片

鸿蒙开发>鸿蒙开发实战》

  1. ArkTS实践

  2. UIAbility应用

  3. 网络案例

  4. ……

图片

 获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发>鸿蒙开发,未来将会支持 50 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发>鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!


http://www.ppmy.cn/server/4991.html

相关文章

Games104 现代游戏引擎3

Sprite Animation 序列帧动画 自由度&#xff08;degrees of freedom&#xff0c;DoF&#xff09;对于刚体而言描述它的运动需要3个位移3个旋转&#xff0c;一共6个自由度 顶点动画&#xff08;per-vertex animation&#xff09;利用网格的顶点来控制运动。此时网格上的每个顶…

客户端动态降级系统

本文字数&#xff1a;4576字 预计阅读时间&#xff1a;20分钟 01 背景 无论是iOS还是Android系统的设备&#xff0c;在线上运行时受硬件、网络环境、代码质量等多方面因素影响&#xff0c;可能会导致性能问题&#xff0c;这一类问题有些在开发阶段是发现不了的。如何在线上始终…

达梦数据库导入导出工具dmfldr

达梦数据库导入导出工具dmfldr 基础信息 OS版本&#xff1a; Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a; DM Database Server 64 V8 DB Version: 0x7000c 03134284132-20240115-215128-200811 dmfldr工具介绍 dmfldr&#xff08;DM Fast Loade…

设计模式代码实战-观察者模式

1、问题描述 小明所在的学校有一个时钟&#xff08;主题&#xff09;&#xff0c;每到整点时&#xff0c;它就会通知所有的学生&#xff08;观察者&#xff09;当前的时间&#xff0c;请你使用观察者模式实现这个时钟通知系统。 注意点&#xff1a;时间从 0 开始&#xff0c;并…

Jackson 2.x 系列【25】Spring Boot 集成之起步依赖、自动配置

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. 起步依赖3. 自动配置3.1 JacksonPrope…

Uni-app中实现数据选择并回传给上个页面的方法

当我们在Uni-app中进行页面间数据传递时&#xff0c;通常会涉及到数据的选择以及回传给上个页面的需求。为了达到这个目的&#xff0c;我们可以利用Uni-app提供的事件机制和页面导航方法来实现。以下是一种实现方式&#xff1a; 数据选择并回传给上个页面的方法 第一步&#…

Go语言中通过数据对齐降低内存消耗和提升性能

数据对齐是一种安排数据分配方式以加速 CPU 访问内存的方法。 不了解这个概念会导致额外的内存消耗甚至性能下降。 要了解数据对齐的工作原理,让我们首先讨论没有它会发生什么。假设我们分配两个变量,一个 int32 类型的 (32 B) 和一个 int64类型的(64 B): var i int32…

java学习笔记3

5. 多重循环和程序调试 5.1 多重循环 多重循环是指循环中嵌套循环结构 多重循环注意事项 各种循环可以互相嵌套一般不要超过三层嵌套外层循环变化一次,内层循环要全部执行完代码 **需求1:**使用循环嵌套输出10*10的矩形 public static void demo() {for (int i = 0; i <…