本篇Codelab使用ArkTS语言实现了应用的一次开发、多端部署,效果预览如下:
分布式新闻客户端(ArkTS)应用包含两个页面:主页面和详情页面。两个页面都展示了丰富的组件,其中详情页还展示了跨设备拉起FA的功能。通过本篇Codelab我们将会一起完成这个应用,实现以下功能:
分布式拉起效果如下图所示:
我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
在本篇Codelab中我们只对核心代码进行讲解,您可以在后面12
参考章节中下载完整代码,首先来介绍下整个工程的代码结构:

export class NewsData {
newsId: string;
...
// 构造方法,用于初始化新闻对象
constructor(newsId: string, title: string, newsType: string, imgUrl: Resource, reads: string, likes: string, content: string) {
this.newsId = newsId;
...
}
}

// 新闻数据源
const NewsComposition: any[] = [
{
"newsId": "1",
"title": "Best Enterprise Wi-Fi Network Award of the Wireless Broadband Alliance 2020",
...
},
...
]
// 用于初始化NewsData的数组
export function getNewsDatas(): Array<NewsData> {
...
}
// 获取默认新闻(第一条新闻数据)
export function getDefaultData(): NewsData {
...
}
本节我们将完成主页面的开发,效果图如下:
从上面效果图可以看出,主页面主要由两部分构成:顶部的新闻分类页签和下方的新闻列表。我们可以使用Tabs和TabContent组件来实现。
@Entry
@Component
struct NewsMain {
build() {
Row() {
NewsTypeComponent()
}
}
}
import {NewsData} from '../model/NewsData'
import {getNewsDatas} from '../model/NewsDataModel'
@Component
struct NewsTypeComponent {
private newsItems: NewsData[] = getNewsDatas()
build() {
Tabs() {
TabContent() {
NewsListPage({ newsItems: this.newsItems })
}.tabBar('All')
...
}
}
@Component
struct NewsListItem {
private newsItem: NewsData
build() {
...
}
}
@Component
struct NewsListPage {
private newsItems: NewsData[]
build() {
Column() {
List() {
ForEach(this.newsItems, item => {
ListItem() {
NewsListItem({ newsItem: item })
}
}, item => item.newsId.toString())
}
}
}
}
本节我们将完成新闻详情页面的开发,效果图如下:
根据以上效果图,我们可以将详情页分为两个部分:新闻信息和底部操作栏。新闻信息包含新闻标题、阅读量和喜好数、新闻图片、新闻内容;底部状态栏包含1个文本框和4个功能按键。详情页实现步骤如下:
"pages": [
"pages/NewsMain",
"pages/page/NewsDetail"
]
在NewsDetail.ets中创建NewsDetail组件,在Column组件中添加新闻信息组件NewsInfo和底部操作栏组件NewsBottom。用Scroll组件包裹NewsInfo组件,这样当新闻内容文本较多时页面可以滚动。
@Component
export struct NewsDetail{
build() {
Column() {
Scroll() {
NewsInfo()
}
.flexGrow(1).width("100%")
NewsBottom()
}.height("100%").width("100%")
}
}
实现新闻列表页面和详情页面的布局完成以后,本节将实现页面跳转的功能。
import router from '@system.router';
@Component
struct NewsInfo {
private newsItem: NewsData;
build() {
Column() {
...
}
}
@Component
struct NewsBottom {
build() {
...
}
}
@Component
struct NewsListItem {
private newsItem: NewsData
build() {
Column() {
...
}
.height(101)
.width("100%")
.onClick(() => {
router.push(
{
uri: 'pages/NewsDetail',
params: {
newsItem: this.newsItem
}
})
})
}
}
@Component
struct NewsInfo {
private newsItem: NewsData = router.getParams().newsItem;
build() {
...
}
}
点击详情页底部操作栏最右侧的分享按钮,弹出当前组网下可供选择的设备列表。此时需要调用JS API设备管理接口的getTrustedDeviceListSync方法,获取网络中的设备信息列表。
"module": {
...
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
{
"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
}
]
}
public class MainAbility extends AceAbility {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
requestPermissions();
}
private void requestPermissions() {
if (verifySelfPermission(SystemPermission.DISTRIBUTED_DATASYNC) != 0) {
if (canRequestPermission(SystemPermission.DISTRIBUTED_DATASYNC)) {
requestPermissionsFromUser(new String[] {SystemPermission.DISTRIBUTED_DATASYNC}, 0);
}
}
}
}
import deviceManager from '@ohos.distributedHardware.deviceManager';
@CustomDialog
@Component
export struct DeviceListDialog {
// 获取新闻条目数据
@Consume("newsItem") newsData: NewsData;
...
}
}
import {NewsData} from '../model/NewsData'
import deviceManager from '@ohos.distributedHardware.deviceManager';
// 引入DeviceListDialog组件
import {DeviceListDialog} from '../component/DeviceListDialog'
...
@Component
struct NewsBottom {
@Consume("newsItem") newsData: NewsData;
@Provide deviceList: any[]= [];
private deviceMag;
dialogController: CustomDialogController = new CustomDialogController({
builder: DeviceListDialog(),
autoCancel: true,
alignment: DialogAlignment.Bottom
});
// 释放DeviceManager实例
aboutToDisappear() {
this.deviceMag.release();
}
// 获取设备列表
getDeviceList() {
...
deviceManager.createDeviceManager("com.huawei.codelab", (err, data) => {
if (err) {
console.error("createDeviceManager failed err: " + JSON.stringify(err));
return;
}
console.info('createDeviceManager successful. Data: ' + JSON.stringify(data))
this.deviceMag = data;
this.deviceList = this.deviceMag.getTrustedDeviceListSync();
});
this.dialogController.open()
}
build() {
...
Image($rawfile('icon_share.png')).width(25).height(21).margin({ right: 10 })
.onClick(() => {
this.getDeviceList()
})
}
设备列表弹窗组件效果图如下:
在上一章节,我们获取到了设备deviceId,本章节我们将实现在新闻详情页分布式拉起远程FA。
import featureAbility from '@ohos.ability.featureAbility';
import wantConstant from '@ohos.ability.wantConstant';
export function startRemoteAbilities(deviceIds, newsId) {
for (var i = 0; i < deviceIds.length; i++) {
var want = {
"want": {
// 远程设备的deviceId
"deviceId": deviceIds[i],
"bundleName": "com.huawei.codelab",
"abilityName": "com.huawei.codelab.MainAbility",
// 分布式任务flag
"flags": wantConstant.Flags.FLAG_ABILITYSLICE_MULTI_DEVICE,
"parameters": {
// 指定跳转的页面
"url": 'pages/NewsDetail',
// 跳转携带的参数
"newsId": newsId
},
}
};
featureAbility.startAbility(want, (err, data) => {
...
});
}
}
Button("确定")
...
.onClick(() => {
// 关闭弹窗
this.controller.close();
startRemoteAbilities(this.selectedDevices, this.newsData.newsId)
})
@Entry
@Component
struct NewsDetail {
// 使用@Provide注解,方便把数据传递给其子孙组件。
@Provide newsData: NewsData= getDefaultData()
private newsItems: NewsData[] = getNewsDatas()
aboutToAppear() {
if (router.getParams()) {
// 从本机获取数据
this.newsData = router.getParams().newsItem
} else {
// 从远端获取数据
featureAbility.getWant()
.then((want) => {
console.info('getWant successful. Data: ' + JSON.stringify(want));
// 获取从远端
let newsId = want.parameters.newsId
// 通过newsId获取新闻信息
this.newsData = this.newsItems.filter(item => (item.newsId === newsId))[0]
}).catch((error) => {
console.error('getWant failed. Cause: ' + JSON.stringify(error));
})
}
}
...
}
设备列表支持多选,可以拉起多台设备,最终分布式拉起效果如下图所示:
本篇Codelab使用方舟开发框架适配多种不同的屏幕形态,以达到一次开发,多端部署的目的。
工程部署在平板上,横屏状态下主界面分为左侧新闻列表和右侧新闻详情两部分。效果图如下所示:
上面效果图有新闻详情信息,和6 构建新闻详情页章节我们创建的新闻详情页内容是一样的,不同的是前面的详情页是一个单独的页面。这时候我们可以将详情页布局单独抽取为一个组件,在平板上复用。
...
@Component
export struct DetailComponent {
build() {
Column() {
Scroll() {
NewsInfo()
}
.flexGrow(1).width("100%")
NewsBottom()
}.height("100%").width("100%")
}
...
}
...
@Entry
@Component
struct NewsMain {
@Provide("landscape") isLandscape: boolean = false;
private newsItems: NewsData[] = getNewsDatas()
// 横屏时,详情页默认展示第一条数据
@Provide("newsItem") newsData: NewsData= this.newsItems[0];
// 监听设备横竖屏切换
orientationListener = mediaquery.matchMediaSync('screen and (1500 < width) and (orientation: landscape)')
build() {
Row() {
Column() {
NewsTypeComponent({ newsItems: this.newsItems })
}
.layoutWeight(2)
if (this.isLandscape) {
Column() {
DetailComponent()
}
.layoutWeight(3)
}
}
}
onPortrait(mediaQueryResult) {
console.log("isLandscape:" + mediaQueryResult.matches);
this.isLandscape = mediaQueryResult.matches;
}
...
}
...
目前你已经成功完成了Codelab并且学到了:
gitee:
github: