提交 8f9059c7 编写于 作者: W wangqingkaihong

检视修改

Signed-off-by: Nwangqingkaihong <wangqing@kaihong.com>
上级 0196fee3
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
## 场景说明 ## 场景说明
两台设备组网,实现修改文件时两个设备同时绘制的分布式画布场景有助于加快工作效率,减少工作中的冗余,本例将为大家介绍如何实现上述功能。 两台设备组网,当其中一个设备修改文件时,两个设备可以同步修改的结果。分布式场景可以在协同办公(如多人多设备编辑同一文件),设备文档更新(分布式设备更新文件内容,所有设备同步更新)中发挥重要作用,有助于加快工作效率,减少工作中的冗余。
本示例将为大家介绍如何实现上述功能。
## 效果呈现 ## 效果呈现
...@@ -40,358 +42,371 @@ ...@@ -40,358 +42,371 @@
## 开发步骤 ## 开发步骤
1.申请所需权限 1. 申请所需权限
​ 在model.json5中添加以下配置: 在model.json5中添加以下配置:
```json ```json
"requestPermissions": [ "requestPermissions": [
{ {
"name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换 "name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换
}, },
{ {
"name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力 "name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力
} }
] ]
``` ```
2.构建UI框架 2. 构建UI框架
indexCanvas页面: indexCanvas页面:
```typescript TitleBar组件呈现标题栏。通过数据懒加载的方式遍历绘制的图形。被划出可视区域外的资源会被回收。
build() {
Column() { 绘制ellipse图形、rect图形的按钮使用Button组件呈现。
TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog })
//自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备 返回按钮、删除按钮也通过Button组件呈现。
Row() {
Text($r('app.string.state')) ```typescript
.fontSize(30) build() {
Image(this.isOnline ? $r('app.media.green') : $r('app.media.red')) Column() {
.size({ width: 30, height: 30 }) TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog })
.objectFit(ImageFit.Contain) //自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备
} Row() {
.width('100%') Text($r('app.string.state'))
.padding(16) .fontSize(30)
//通过懒加载模式遍历绘制的图形,将每个图形绘制在画布上 Image(this.isOnline ? $r('app.media.green') : $r('app.media.red'))
LazyForEach(this.canvasDataSource, (item: CanvasPath, index) => { .size({ width: 30, height: 30 })
Canvas(this.context) .objectFit(ImageFit.Contain)
.width('100%') }
.height(200) .width('100%')
.backgroundColor('#00ffff') .padding(16)
.onReady(() => { //通过懒加载模式遍历绘制的图形,将每个图形绘制在画布上
if (item.path === 'rect') { LazyForEach(this.canvasDataSource, (item: CanvasPath, index) => {
this.context.save(); Canvas(this.context)
this.path2Df.rect(80, 80, 100, 100); .width('100%')
this.context.stroke(this.path2Df); .height(200)
this.context.restore(); .backgroundColor('#00ffff')
} .onReady(() => {
if (item.path === 'ellipse') { if (item.path === 'rect') {
this.context.restore(); this.context.save();
this.path2De.ellipse(100, 100, 50, 100, Math.PI * 0.25, Math.PI * 0.5, Math.PI); this.path2Df.rect(80, 80, 100, 100);
this.context.stroke(this.path2De); this.context.stroke(this.path2Df);
this.context.save(); this.context.restore();
} }
}) if (item.path === 'ellipse') {
}, item => JSON.stringify(item)) this.context.restore();
this.path2De.ellipse(100, 100, 50, 100, Math.PI * 0.25, Math.PI * 0.5, Math.PI);
Row() { this.context.stroke(this.path2De);
Button('ellipse')//绘制ellipse图形的按钮 this.context.save();
.width(130) }
.height(45) })
.key('ellipse') }, item => JSON.stringify(item))
.onClick(() => {
if (this.globalObject.isContainString('ellipse') === -1) { Row() {
this.globalObject.add('ellipse'); //将绘制信息保存在持久全局数据中 Button('ellipse')//绘制ellipse图形的按钮
} .width(130)
this.onPageShow(); .height(45)
}) .key('ellipse')
Button('rect')//绘制rect图形的按钮 .onClick(() => {
.width(130) if (this.globalObject.isContainString('ellipse') === -1) {
.height(45) this.globalObject.add('ellipse'); //将绘制信息保存在持久全局数据中
.key('rect') }
.onClick(() => { this.onPageShow();
if (this.globalObject.isContainString('rect') === -1) { })
this.globalObject.add('rect'); Button('rect')//绘制rect图形的按钮
} .width(130)
this.onPageShow(); .height(45)
}) .key('rect')
}.margin({ top: 10 }) .onClick(() => {
.width('100%') if (this.globalObject.isContainString('rect') === -1) {
.justifyContent(FlexAlign.SpaceAround) this.globalObject.add('rect');
}
Row() { this.onPageShow();
Button('back') })
.width(130) }.margin({ top: 10 })
.height(45) .width('100%')
.key('back') .justifyContent(FlexAlign.SpaceAround)
.backgroundColor(Color.Orange)
.onClick(() => { Row() {
router.back() Button('back')
}) .width(130)
Button('delete')//删除图形 .height(45)
.width(130) .key('back')
.height(45) .backgroundColor(Color.Orange)
.key('delete') .onClick(() => {
.onClick(() => { router.back()
this.globalObject.clear(); })
this.canvasDataSource['pathArray'] = []; Button('delete')//删除图形
this.canvasDataSource.notifyDataReload(); .width(130)
this.context.clearRect(0, 0, 950, 950) .height(45)
}) .key('delete')
}.margin({ top: 10 }) .onClick(() => {
.width('100%') this.globalObject.clear();
.justifyContent(FlexAlign.SpaceAround) this.canvasDataSource['pathArray'] = [];
} this.canvasDataSource.notifyDataReload();
.width('100%') this.context.clearRect(0, 0, 950, 950)
.height('100%') })
.justifyContent(FlexAlign.Center) }.margin({ top: 10 })
.alignItems(HorizontalAlign.Center) .width('100%')
} .justifyContent(FlexAlign.SpaceAround)
} }
``` .width('100%')
.height('100%')
2.数据model .justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
``` }
//BasicDataSource.ets }
class BasicDataSource implements IDataSource { ```
private listeners: DataChangeListener[] = []
3. 数据model
public totalCount(): number {
return 0 通过registerDataChangeListener进行对数据变动的监听,数据发生变化时,调用notifyDataReload方法通知数据已经准备就绪。
}
```typescript
public getData(index: number): any { //BasicDataSource.ets
return undefined class BasicDataSource implements IDataSource {
} private listeners: DataChangeListener[] = []
//注册数据变动的监听 public totalCount(): number {
registerDataChangeListener(listener: DataChangeListener): void { return 0
if (this.listeners.indexOf(listener) < 0) { }
console.info('add listener')
this.listeners.push(listener) public getData(index: number): any {
} return undefined
} }
unregisterDataChangeListener(listener: DataChangeListener): void { //注册数据变动的监听
const pos = this.listeners.indexOf(listener); registerDataChangeListener(listener: DataChangeListener): void {
if (pos >= 0) { if (this.listeners.indexOf(listener) < 0) {
console.info('remove listener') console.info('add listener')
this.listeners.splice(pos, 1) this.listeners.push(listener)
} }
} }
//数据reloaded,分布式数据数值变化需要调用这个接口重载下 unregisterDataChangeListener(listener: DataChangeListener): void {
notifyDataReload(): void { const pos = this.listeners.indexOf(listener);
this.listeners.forEach(listener => { if (pos >= 0) {
listener.onDataReloaded() console.info('remove listener')
}) this.listeners.splice(pos, 1)
} }
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => { //数据reloaded,分布式数据数值变化需要调用这个接口重载下
listener.onDataAdd(index) notifyDataReload(): void {
}) this.listeners.forEach(listener => {
} listener.onDataReloaded()
})
.... }
export class CanvasDataSource extends BasicDataSource { notifyDataAdd(index: number): void {
//监听的数据类型 this.listeners.forEach(listener => {
private pathArray: Canvas[] = [] listener.onDataAdd(index)
})
//重载接口 }
public totalCount(): number {
return this.pathArray.length ....
}
export class CanvasDataSource extends BasicDataSource {
public getData(index: number): any { //监听的数据类型
return this.pathArray[index] private pathArray: Canvas[] = []
}
//重载接口
public addData(index: number, data: Canvas): void { public totalCount(): number {
this.pathArray.splice(index, 0, data) return this.pathArray.length
this.notifyDataAdd(index) }
}
public getData(index: number): any {
public pushData(data: Canvas): void { return this.pathArray[index]
this.pathArray.push(data) }
this.notifyDataAdd(this.pathArray.length - 1)
} public addData(index: number, data: Canvas): void {
} this.pathArray.splice(index, 0, data)
``` this.notifyDataAdd(index)
}
3.将两台设备组网
public pushData(data: Canvas): void {
```typescript this.pathArray.push(data)
showDialog = () => { this.notifyDataAdd(this.pathArray.length - 1)
//RemoteDeviceModel引入自model/RemoteDeviceModel.ts }
RemoteDeviceModel.registerDeviceListCallback(() => { }
//得到附近可信的设备列表 ```
Logger.info(TAG, 'registerDeviceListCallback, callback entered')
this.devices = [] 4. 将两台设备组网
this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices
if (this.dialogController) { 使用自RemoteDeviceModel.ts中引入的类RemoteDeviceModel以扫描获得附近可以连接的设备。
this.dialogController.close()
this.dialogController = undefined ```typescript
} showDialog = () => {
this.dialogController = new CustomDialogController({ //RemoteDeviceModel引入自model/RemoteDeviceModel.ts
builder: DeviceDialog({ RemoteDeviceModel.registerDeviceListCallback(() => {
devices: this.devices, //得到附近可信的设备列表
onSelectedIndexChange: this.onSelectedDevice Logger.info(TAG, 'registerDeviceListCallback, callback entered')
}), this.devices = []
autoCancel: true this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices
}) if (this.dialogController) {
this.dialogController.open() this.dialogController.close()
}) this.dialogController = undefined
} }
.................................... this.dialogController = new CustomDialogController({
//model/RemoteDeviceModel.ts builder: DeviceDialog({
import deviceManager from '@ohos.distributedHardware.deviceManager' devices: this.devices,
registerDeviceListCallback(stateChangeCallback: () => void) { onSelectedIndexChange: this.onSelectedDevice
if (typeof (this.deviceManager) !== 'undefined') { }),
this.registerDeviceListCallbackImplement(stateChangeCallback) autoCancel: true
return })
} this.dialogController.open()
Logger.info(TAG, 'deviceManager.createDeviceManager begin') })
try { }
deviceManager.createDeviceManager(BUNDLE, (error, value) => { ....................................
if (error) { //model/RemoteDeviceModel.ts
Logger.error(TAG, 'createDeviceManager failed.') import deviceManager from '@ohos.distributedHardware.deviceManager'
return registerDeviceListCallback(stateChangeCallback: () => void) {
} if (typeof (this.deviceManager) !== 'undefined') {
this.deviceManager = value this.registerDeviceListCallbackImplement(stateChangeCallback)
this.registerDeviceListCallbackImplement(stateChangeCallback) return
Logger.info(TAG, `createDeviceManager callback returned,value=${value}`) }
}) Logger.info(TAG, 'deviceManager.createDeviceManager begin')
} catch (error) { try {
Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`) deviceManager.createDeviceManager(BUNDLE, (error, value) => {
} if (error) {
Logger.error(TAG, 'createDeviceManager failed.')
Logger.info(TAG, 'deviceManager.createDeviceManager end') return
} }
registerDeviceListCallbackImplement(stateChangeCallback: () => void) { this.deviceManager = value
Logger.info(TAG, 'registerDeviceListCallback') this.registerDeviceListCallbackImplement(stateChangeCallback)
this.stateChangeCallback = stateChangeCallback Logger.info(TAG, `createDeviceManager callback returned,value=${value}`)
if (this.deviceManager === undefined) { })
Logger.error(TAG, 'deviceManager has not initialized') } catch (error) {
this.stateChangeCallback() Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`)
return }
}
Logger.info(TAG, 'getTrustedDeviceListSync begin') Logger.info(TAG, 'deviceManager.createDeviceManager end')
try { }
let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表 registerDeviceListCallbackImplement(stateChangeCallback: () => void) {
Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`) Logger.info(TAG, 'registerDeviceListCallback')
if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') { this.stateChangeCallback = stateChangeCallback
this.devices = list if (this.deviceManager === undefined) {
} Logger.error(TAG, 'deviceManager has not initialized')
} catch (error) { this.stateChangeCallback()
Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`) return
} }
this.stateChangeCallback() Logger.info(TAG, 'getTrustedDeviceListSync begin')
Logger.info(TAG, 'callback finished') try {
try { let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表
this.deviceManager.on('deviceStateChange', (data) => { Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`)
if (data === null) { if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
return this.devices = list
} }
Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`) } catch (error) {
switch (data.action) { Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`)
case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务 }
this.discoverDevices = [] this.stateChangeCallback()
this.devices.push(data.device) Logger.info(TAG, 'callback finished')
this.stateChangeCallback() try {
try { this.deviceManager.on('deviceStateChange', (data) => {
let list = this.deviceManager.getTrustedDeviceListSync() if (data === null) {
if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') { return
this.devices = list }
} Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`)
} catch (error) { switch (data.action) {
Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`) case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务
} this.discoverDevices = []
this.stateChangeCallback() this.devices.push(data.device)
break this.stateChangeCallback()
default: try {
break let list = this.deviceManager.getTrustedDeviceListSync()
} if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
}) this.devices = list
this.deviceManager.on('deviceFound', (data) => { }
if (data === null) { } catch (error) {
return Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`)
} }
Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`) this.stateChangeCallback()
this.onDeviceFound(data) break
}) default:
this.deviceManager.on('discoverFail', (data) => { break
Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`) }
}) })
this.deviceManager.on('serviceDie', () => { this.deviceManager.on('deviceFound', (data) => {
Logger.info(TAG, 'serviceDie') if (data === null) {
}) return
} catch (error) { }
Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`) Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`)
} this.onDeviceFound(data)
this.startDeviceDiscovery() })
} this.deviceManager.on('discoverFail', (data) => {
startDeviceDiscovery() { Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`)
SUBSCRIBE_ID = Math.floor(65536 * Math.random()) })
var info = { this.deviceManager.on('serviceDie', () => {
subscribeId: SUBSCRIBE_ID, Logger.info(TAG, 'serviceDie')
mode: 0xAA, })
medium: 2, } catch (error) {
freq: 2,//高频率 Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`)
isSameAccount: false, }
isWakeRemote: true, this.startDeviceDiscovery()
capability: 0 }
} startDeviceDiscovery() {
Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`) SUBSCRIBE_ID = Math.floor(65536 * Math.random())
try { var info = {
this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备 subscribeId: SUBSCRIBE_ID,
} catch (error) { mode: 0xAA,
Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`) medium: 2,
} freq: 2,//高频率
isSameAccount: false,
} isWakeRemote: true,
``` capability: 0
}
4.实现同步编辑 Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`)
try {
```typescript this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备
onPageShow() { } catch (error) {
//每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow() Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`)
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents }
this.noteDataSource.notifyDataReload()
Logger.info(TAG, `this.sessionId = ${this.sessionId}`) }
Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`) ```
if (this.sessionId !== this.globalSessionId) {
this.sessionId = this.globalSessionId 5. 实现同步编辑
this.share()
} 通过AppStorage设置持久性数据,然后实现IDataSource接口,通过注册数据监听接口监听数据的变化。
}
share() { ```typescript
//多个设备间的对象如果设置为同一个sessionId的笔记数据自动同步 onPageShow() {
Logger.info(TAG, `sessionId = ${this.sessionId}`) //每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow()
this.globalObject.setChangeCallback(() => { //noteDataSource获取globalObject保存的分布式的持久性数据,并进行Reload操作传递。
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
this.noteDataSource.notifyDataReload() this.noteDataSource.notifyDataReload()
}) Logger.info(TAG, `this.sessionId = ${this.sessionId}`)
this.globalObject.setStatusCallback((session, networkId, status) => { Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`)
Logger.info(TAG, `StatusCallback,${status}`) if (this.sessionId !== this.globalSessionId) {
if (status === 'online') { this.sessionId = this.globalSessionId
this.isOnline = true this.share()
} else { }
this.isOnline = false }
} share() {
}) //多个设备间的对象如果设置为同一个sessionId,数据自动同步
this.globalObject.distributedObject.setSessionId(this.sessionId) Logger.info(TAG, `sessionId = ${this.sessionId}`)
AppStorage.SetOrCreate('objectModel', this.globalObject) this.globalObject.setChangeCallback(() => {
} this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
``` this.noteDataSource.notifyDataReload()
})
this.globalObject.setStatusCallback((session, networkId, status) => {
Logger.info(TAG, `StatusCallback,${status}`)
if (status === 'online') {
this.isOnline = true
} else {
this.isOnline = false
}
})
this.globalObject.distributedObject.setSessionId(this.sessionId)
AppStorage.SetOrCreate('objectModel', this.globalObject)
}
```
## 全部代码 ## 全部代码
本例完整代码sample示例链接:[分布式文件](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote) 本例完整代码sample示例链接:[分布式对象](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote)
## 参考 ## 参考
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## 场景说明 ## 场景说明
两台设备组网,实现修改文件时两个设备同时修改的分布式文件场景有助于加快工作效率,减少工作中的冗余,本例将为大家介绍如何实现上述功能。 两台设备组网的分布式场景是工作中常常需要的。常见的如代码的同步编辑、文档的同步修改等。这样的分布式场景有助于加快工作效率,减少工作中的冗余,本例将为大家介绍如何实现上述功能。
## 效果呈现 ## 效果呈现
...@@ -40,402 +40,443 @@ ...@@ -40,402 +40,443 @@
## 开发步骤 ## 开发步骤
1.申请所需权限 1. 申请所需权限
在model.json5中添加以下配置: 在model.json5中添加以下配置:
```json ```json
"requestPermissions": [ "requestPermissions": [
{ {
"name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换 "name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换
}, },
{ {
"name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力 "name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力
} }
] ]
``` ```
2.构建UI框架 2. 构建UI框架
indexNote页面: index页面:
```typescript TitleBar组件呈现标题栏。使用List组件呈现文件列表,ListItem由一个呈现文件类型标志的Image组件,一个呈现文件标题的Text组件,一个呈现文件内容的Text组件组成。
build() { ```typescript
Column() { build() {
TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog }) Column() {
//自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备 TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog })
Row() { //自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备
Text($r('app.string.state')) Row() {
.fontSize(30) Text($r('app.string.state'))
Image(this.isOnline ? $r('app.media.green') : $r('app.media.red'))//两台设备组网成功后状态显示为绿色、否则为红色 .fontSize(30)
.size({ width: 30, height: 30 }) Image(this.isOnline ? $r('app.media.green') : $r('app.media.red'))//两台设备组网成功后状态显示为绿色、否则为红色
.objectFit(ImageFit.Contain) .size({ width: 30, height: 30 })
} .objectFit(ImageFit.Contain)
.width('100%') }
.padding(16) .width('100%')
//通过数据懒加载的方式从数据源中每次迭代一个文件进行展示,可用列表被放置在滚动容器中,被划出可视区域外的资源会被回收 .padding(16)
List({ space: 10 }) { //通过数据懒加载的方式从数据源中每次迭代一个文件进行展示,可用列表被放置在滚动容器中,被划出可视区域外的资源会被回收
LazyForEach(this.noteDataSource, (item: Note, index) => { List({ space: 10 }) {
ListItem() { LazyForEach(this.noteDataSource, (item: Note, index) => {
NoteItem({ note: item, index: index })//NoteItem引入自common/NoteItem.ets,负责主页文件信息的呈现 ListItem() {
.id(`${item.title}`) NoteItem({ note: item, index: index })//NoteItem引入自common/NoteItem.ets,负责主页文件信息的呈现
} .id(`${item.title}`)
}, item => JSON.stringify(item)) }
} }, item => JSON.stringify(item))
.width('95%') }
.margin(10) .width('95%')
.layoutWeight(1) .margin(10)
.layoutWeight(1)
Row() {
Column() { Row() {
Image($r('app.media.clear'))//清除按钮 Column() {
.size({ width: 40, height: 40 }) Image($r('app.media.clear'))//清除按钮
Text($r('app.string.clear')) .size({ width: 40, height: 40 })
.fontColor(Color.Red) Text($r('app.string.clear'))
.fontSize(20) .fontColor(Color.Red)
}.layoutWeight(1) .fontSize(20)
.id('clearNote') }.layoutWeight(1)
.onClick(() => { .id('clearNote')
//点击清除按钮清除所有文件 .onClick(() => {
Logger.info(TAG, 'clear notes') //点击清除按钮清除所有文件
this.noteDataSource['dataArray'] = [] Logger.info(TAG, 'clear notes')
this.noteDataSource.notifyDataReload() this.noteDataSource['dataArray'] = []
this.globalObject.clear() this.noteDataSource.notifyDataReload()
AppStorage.SetOrCreate('sessionId', this.sessionId) this.globalObject.clear()
}) AppStorage.SetOrCreate('sessionId', this.sessionId)
})
Column() {
Image($r('app.media.add'))//添加按钮 Column() {
.size({ width: 40, height: 40 }) Image($r('app.media.add'))//添加按钮
Text($r('app.string.add')) .size({ width: 40, height: 40 })
.fontColor(Color.Black) Text($r('app.string.add'))
.fontSize(20) .fontColor(Color.Black)
}.layoutWeight(1) .fontSize(20)
.id('addNote') }.layoutWeight(1)
.onClick(() => { .id('addNote')
//点击添加按钮跳转到编辑页面 .onClick(() => {
router.push({ //点击添加按钮跳转到编辑页面
url: 'pages/Edit', router.push({
params: { url: 'pages/Edit',
note: new Note('', '', -1), params: {
isAdd: true note: new Note('', '', -1),
} isAdd: true
}) }
}) })
} })
.width('100%') }
.padding(10) .width('100%')
.backgroundColor('#F0F0F0') .padding(10)
} .backgroundColor('#F0F0F0')
.width('100%') }
.height('100%') .width('100%')
.backgroundColor('#F5F5F5') .height('100%')
} .backgroundColor('#F5F5F5')
} }
.................................... }
//common/NoteItem.ets ...
import router from '@ohos.router' //common/NoteItem.ets
import { MARKS } from '../model/Const' import router from '@ohos.router'
import Note from '../model/Note' import { MARKS } from '../model/Const'
import Note from '../model/Note'
@Component
export default struct NoteItem { @Component
@State note: Note | undefined = undefined export default struct NoteItem {
private index: number = 0 @State note: Note | undefined = undefined
private index: number = 0
build() {
Row() { build() {
Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.note'))//文件标志图片 Row() {
.size({ width: 30, height: 30 }) Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.note'))//文件标志图片
.objectFit(ImageFit.Contain) .size({ width: 30, height: 30 })
Column() { .objectFit(ImageFit.Contain)
Text(this.note.title)//文件标题 Column() {
.fontColor(Color.Black) Text(this.note.title)//文件标题
.fontSize(30) .fontColor(Color.Black)
.maxLines(1) .fontSize(30)
.textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1)
Text(this.note.content)//文件内容 .textOverflow({ overflow: TextOverflow.Ellipsis })
.fontColor(Color.Gray) Text(this.note.content)//文件内容
.margin({ top: 10 }) .fontColor(Color.Gray)
.fontSize(25) .margin({ top: 10 })
.maxLines(1)//在列表中最多展示一行 .fontSize(25)
.textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1)//在列表中最多展示一行
} .textOverflow({ overflow: TextOverflow.Ellipsis })
.alignItems(HorizontalAlign.Start) }
.margin({ left: 20 }) .alignItems(HorizontalAlign.Start)
} .margin({ left: 20 })
.padding(16) }
.width('100%') .padding(16)
.borderRadius(16) .width('100%')
.backgroundColor(Color.White) .borderRadius(16)
.onClick(() => { .backgroundColor(Color.White)
//点击文件进入此文件编辑页面 .onClick(() => {
router.push({ //点击文件进入此文件编辑页面
url: 'pages/Edit', router.push({
params: { url: 'pages/Edit',
index: this.index, params: {
note: this.note, index: this.index,
isAdd: false note: this.note,
} isAdd: false
}) }
}) })
} })
} }
``` }
```
Edit页面:
Edit页面:
```typescript
build() { 使用TextInput组件呈现文件标题输入框,使用TextArea组件呈现文件内容的输入区域,使用Button组件呈现保存按钮并绑定点击事件以新建或更新文件内容。
Column() {
TitleBar({ title: this.note.title === '' ? $r('app.string.add_note') : this.note.title }) ```typescript
Column() { build() {
Row() { Column() {
Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.mark')) TitleBar({ title: this.note.title === '' ? $r('app.string.add_note') : this.note.title })
.width(30) Column() {
.aspectRatio(1) Row() {
.margin({ left: 16, top: 16 }) Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.mark'))
.objectFit(ImageFit.Contain) .width(30)
.alignSelf(ItemAlign.Start) .aspectRatio(1)
Select([{ value: ' ', icon: MARKS[0] }, .margin({ left: 16, top: 16 })
{ value: ' ', icon: MARKS[1] }, .objectFit(ImageFit.Contain)
{ value: ' ', icon: MARKS[2] }, .alignSelf(ItemAlign.Start)
{ value: ' ', icon: MARKS[3] }, Select([{ value: ' ', icon: MARKS[0] },
{ value: ' ', icon: MARKS[4] }]) { value: ' ', icon: MARKS[1] },
.selected(this.note.mark) { value: ' ', icon: MARKS[2] },
.margin({ top: 5 }) { value: ' ', icon: MARKS[3] },
.onSelect((index: number) => { { value: ' ', icon: MARKS[4] }])
this.note.mark = index .selected(this.note.mark)
}) .margin({ top: 5 })
} .onSelect((index: number) => {
.width('100%') this.note.mark = index
})
TextInput({ placeholder: 'input the title', text: this.note.title })//文件标题输入框 }
.id('titleInput') .width('100%')
.placeholderColor(Color.Gray)
.fontSize(30) TextInput({ placeholder: 'input the title', text: this.note.title })//文件标题输入框
.margin({ left: 15, right: 15, top: 15 }) .id('titleInput')
.height(60) .placeholderColor(Color.Gray)
.backgroundColor(Color.White) .fontSize(30)
.onChange((value: string) => { .margin({ left: 15, right: 15, top: 15 })
this.note.title = value .height(60)
}) .backgroundColor(Color.White)
TextArea({ placeholder: 'input the content', text: this.note.content })//文件内容输入区域 .onChange((value: string) => {
.id('contentInput') this.note.title = value
.placeholderColor(Color.Gray) })
.backgroundColor(Color.White) TextArea({ placeholder: 'input the content', text: this.note.content })//文件内容输入区域
.fontSize(30) .id('contentInput')
.height('35%') .placeholderColor(Color.Gray)
.margin({ left: 16, right: 16, top: 16 }) .backgroundColor(Color.White)
.textAlign(TextAlign.Start) .fontSize(30)
.onChange((value: string) => { .height('35%')
this.note.content = value .margin({ left: 16, right: 16, top: 16 })
}) .textAlign(TextAlign.Start)
.onChange((value: string) => {
Button() { this.note.content = value
//保存按钮 })
Text($r('app.string.save'))
.fontColor(Color.White) Button() {
.fontSize(17) //保存按钮
} Text($r('app.string.save'))
.id('saveNote') .fontColor(Color.White)
.backgroundColor('#0D9FFB') .fontSize(17)
.height(50) }
.width(200) .id('saveNote')
.margin({ top: 20 }) .backgroundColor('#0D9FFB')
.onClick(() => { .height(50)
//点击按钮时调用model/DistributedObjectModel.ts定义的类globalObject中的方法 .width(200)
if (!this.isAdd) { .margin({ top: 20 })
let index = router.getParams()['index'] .onClick(() => {
this.globalObject.update(index, this.note.title, this.note.content, this.note.mark)//编辑时更新内容 //点击按钮时调用model/DistributedObjectModel.ts定义的类globalObject中的方法
} else { if (!this.isAdd) {
this.globalObject.add(this.note.title, this.note.content, this.note.mark)//新建时添加内容 let index = router.getParams()['index']
} this.globalObject.update(index, this.note.title, this.note.content, this.note.mark)//编辑时更新内容
router.back()//返回主页 } else {
}) this.globalObject.add(this.note.title, this.note.content, this.note.mark)//新建时添加内容
} }
} router.back()//返回主页
.width('100%') })
.height('100%') }
.backgroundColor('#F5F5F5') }
} .width('100%')
} .height('100%')
``` .backgroundColor('#F5F5F5')
}
3.将两台设备组网 }
```
```typescript
showDialog = () => { 3. 将两台设备组网
//RemoteDeviceModel引入自model/RemoteDeviceModel.ts
RemoteDeviceModel.registerDeviceListCallback(() => { 使用自RemoteDeviceModel.ts中引入的类RemoteDeviceModel以扫描获得附近可以连接的设备。
//得到附近可信的设备列表
Logger.info(TAG, 'registerDeviceListCallback, callback entered') ```typescript
this.devices = [] showDialog = () => {
this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices //RemoteDeviceModel引入自model/RemoteDeviceModel.ts
if (this.dialogController) { RemoteDeviceModel.registerDeviceListCallback(() => {
this.dialogController.close() //得到附近可信的设备列表
this.dialogController = undefined Logger.info(TAG, 'registerDeviceListCallback, callback entered')
} this.devices = []
this.dialogController = new CustomDialogController({ this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices
builder: DeviceDialog({ if (this.dialogController) {
devices: this.devices, this.dialogController.close()
onSelectedIndexChange: this.onSelectedDevice this.dialogController = undefined
}), }
autoCancel: true this.dialogController = new CustomDialogController({
}) builder: DeviceDialog({
this.dialogController.open() devices: this.devices,
}) onSelectedIndexChange: this.onSelectedDevice
} }),
.................................... autoCancel: true
//model/RemoteDeviceModel.ts })
import deviceManager from '@ohos.distributedHardware.deviceManager' this.dialogController.open()
registerDeviceListCallback(stateChangeCallback: () => void) { })
if (typeof (this.deviceManager) !== 'undefined') { }
this.registerDeviceListCallbackImplement(stateChangeCallback) ...
return //model/RemoteDeviceModel.ts
} import deviceManager from '@ohos.distributedHardware.deviceManager'
Logger.info(TAG, 'deviceManager.createDeviceManager begin') registerDeviceListCallback(stateChangeCallback: () => void) {
try { if (typeof (this.deviceManager) !== 'undefined') {
deviceManager.createDeviceManager(BUNDLE, (error, value) => { this.registerDeviceListCallbackImplement(stateChangeCallback)
if (error) { return
Logger.error(TAG, 'createDeviceManager failed.') }
return Logger.info(TAG, 'deviceManager.createDeviceManager begin')
} try {
this.deviceManager = value deviceManager.createDeviceManager(BUNDLE, (error, value) => {
this.registerDeviceListCallbackImplement(stateChangeCallback) if (error) {
Logger.info(TAG, `createDeviceManager callback returned,value=${value}`) Logger.error(TAG, 'createDeviceManager failed.')
}) return
} catch (error) { }
Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`) this.deviceManager = value
} this.registerDeviceListCallbackImplement(stateChangeCallback)
Logger.info(TAG, `createDeviceManager callback returned,value=${value}`)
Logger.info(TAG, 'deviceManager.createDeviceManager end') })
} } catch (error) {
registerDeviceListCallbackImplement(stateChangeCallback: () => void) { Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`)
Logger.info(TAG, 'registerDeviceListCallback') }
this.stateChangeCallback = stateChangeCallback
if (this.deviceManager === undefined) { Logger.info(TAG, 'deviceManager.createDeviceManager end')
Logger.error(TAG, 'deviceManager has not initialized') }
this.stateChangeCallback() registerDeviceListCallbackImplement(stateChangeCallback: () => void) {
return Logger.info(TAG, 'registerDeviceListCallback')
} this.stateChangeCallback = stateChangeCallback
Logger.info(TAG, 'getTrustedDeviceListSync begin') if (this.deviceManager === undefined) {
try { Logger.error(TAG, 'deviceManager has not initialized')
let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表 this.stateChangeCallback()
Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`) return
if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') { }
this.devices = list Logger.info(TAG, 'getTrustedDeviceListSync begin')
} try {
} catch (error) { let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表
Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`) Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`)
} if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
this.stateChangeCallback() this.devices = list
Logger.info(TAG, 'callback finished') }
try { } catch (error) {
this.deviceManager.on('deviceStateChange', (data) => { Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`)
if (data === null) { }
return this.stateChangeCallback()
} Logger.info(TAG, 'callback finished')
Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`) try {
switch (data.action) { this.deviceManager.on('deviceStateChange', (data) => {
case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务 if (data === null) {
this.discoverDevices = [] return
this.devices.push(data.device) }
this.stateChangeCallback() Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`)
try { switch (data.action) {
let list = this.deviceManager.getTrustedDeviceListSync() case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务
if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') { this.discoverDevices = []
this.devices = list this.devices.push(data.device)
} this.stateChangeCallback()
} catch (error) { try {
Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`) let list = this.deviceManager.getTrustedDeviceListSync()
} if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
this.stateChangeCallback() this.devices = list
break }
default: } catch (error) {
break Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`)
} }
}) this.stateChangeCallback()
this.deviceManager.on('deviceFound', (data) => { break
if (data === null) { default:
return break
} }
Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`) })
this.onDeviceFound(data) this.deviceManager.on('deviceFound', (data) => {
}) if (data === null) {
this.deviceManager.on('discoverFail', (data) => { return
Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`) }
}) Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`)
this.deviceManager.on('serviceDie', () => { this.onDeviceFound(data)
Logger.info(TAG, 'serviceDie') })
}) this.deviceManager.on('discoverFail', (data) => {
} catch (error) { Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`)
Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`) })
} this.deviceManager.on('serviceDie', () => {
this.startDeviceDiscovery() Logger.info(TAG, 'serviceDie')
} })
startDeviceDiscovery() { } catch (error) {
SUBSCRIBE_ID = Math.floor(65536 * Math.random()) Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`)
var info = { }
subscribeId: SUBSCRIBE_ID, this.startDeviceDiscovery()
mode: 0xAA, }
medium: 2, startDeviceDiscovery() {
freq: 2,//高频率 SUBSCRIBE_ID = Math.floor(65536 * Math.random())
isSameAccount: false, var info = {
isWakeRemote: true, subscribeId: SUBSCRIBE_ID,
capability: 0 mode: 0xAA,
} medium: 2,
Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`) freq: 2,//高频率
try { isSameAccount: false,
this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备 isWakeRemote: true,
} catch (error) { capability: 0
Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`) }
} Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`)
try {
} this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备
``` } catch (error) {
Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`)
4.实现同步编辑 }
```typescript }
onPageShow() { ```
//每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow()
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents 4. 实现同步编辑
this.noteDataSource.notifyDataReload()
Logger.info(TAG, `this.sessionId = ${this.sessionId}`) 通过AppStorage设置持久性数据,然后实现IDataSource接口,通过注册数据监听接口监听数据的变化。
Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`)
if (this.sessionId !== this.globalSessionId) { ```typescript
this.sessionId = this.globalSessionId class BasicDataSource implements IDataSource {
this.share() private listeners: DataChangeListener[] = []
}
} public totalCount(): number {
share() { return 0
//多个设备间的对象如果设置为同一个sessionId的笔记数据自动同步 }
Logger.info(TAG, `sessionId = ${this.sessionId}`)
this.globalObject.setChangeCallback(() => { public getData(index: number): any {
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents return undefined
this.noteDataSource.notifyDataReload() }
})
this.globalObject.setStatusCallback((session, networkId, status) => { registerDataChangeListener(listener: DataChangeListener): void {
Logger.info(TAG, `StatusCallback,${status}`) if (this.listeners.indexOf(listener) < 0) {
if (status === 'online') { console.info('add listener')
this.isOnline = true this.listeners.push(listener)
} else { }
this.isOnline = false }
}
}) unregisterDataChangeListener(listener: DataChangeListener): void {
this.globalObject.distributedObject.setSessionId(this.sessionId) const pos = this.listeners.indexOf(listener);
AppStorage.SetOrCreate('objectModel', this.globalObject) if (pos >= 0) {
} console.info('remove listener')
``` this.listeners.splice(pos, 1)
}
}
//数据准备好了
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
...
}
onPageShow() {
//每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow()
//noteDataSource获取globalObject保存的分布式的持久性数据,并进行Reload操作传递。
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
this.noteDataSource.notifyDataReload()
Logger.info(TAG, `this.sessionId = ${this.sessionId}`)
Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`)
if (this.sessionId !== this.globalSessionId) {
this.sessionId = this.globalSessionId
this.share()
}
}
share() {
//多个设备间的对象如果设置为同一个sessionId的笔记数据自动同步
Logger.info(TAG, `sessionId = ${this.sessionId}`)
this.globalObject.setChangeCallback(() => {
this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
this.noteDataSource.notifyDataReload()
})
this.globalObject.setStatusCallback((session, networkId, status) => {
Logger.info(TAG, `StatusCallback,${status}`)
if (status === 'online') {
this.isOnline = true
} else {
this.isOnline = false
}
})
this.globalObject.distributedObject.setSessionId(this.sessionId)
AppStorage.SetOrCreate('objectModel', this.globalObject)
}
```
## 全部代码 ## 全部代码
本例完整代码sample示例链接:[分布式文件](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote) 本例完整代码sample示例链接:[分布式对象](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote)
## 参考 ## 参考
[权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync) - [权限列表](../application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
- [分布式数据对象](../application-dev/reference/apis/js-apis-data-distributedobject.md)
[分布式数据对象](../application-dev/reference/apis/js-apis-data-distributedobject.md)
...@@ -8,10 +8,16 @@ ...@@ -8,10 +8,16 @@
本例效果如下: 本例效果如下:
![contactlist](figures/camera.png) | 拍照 | 预览 |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| <img src="figures/camera.png" alt="contactlist" style="zoom: 45%;" /> | <img src="figures/camerapreview.gif" alt="contactlist" style="zoom: 50%;" /> |
## 运行环境 ## 运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
- IDE:DevEco Studio 4.0.0.201 Beta1 - IDE:DevEco Studio 4.0.0.201 Beta1
- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1) - SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
...@@ -19,222 +25,224 @@ ...@@ -19,222 +25,224 @@
本例使用@ohos.multimedia.camera接口实现相机示例的主要功能:拍照、预览; 本例使用@ohos.multimedia.camera接口实现相机示例的主要功能:拍照、预览;
使用@ohos.multimedia.image接口实现图片接收; - 拍照:XComponent组件负责绘制摄像头画面呈现的窗口,其onload事件调用cameraModel.ts的initCamera方法初始化相机功能输出画面信息。拍照动作使用Image组件实现,其onclick事件调用cameraModel.ts的takepicture方法开始拍照。
使用@ohos.multimedia.mediaLibrary接口实现对媒体文件的存储。
- XComponent组件负责绘制摄像头画面呈现的窗口,其onload事件调用cameraModel.ts的initCamera方法初始化相机功能输出预览信息。 - 预览:返回相机界面点击底部左侧预览图可进入相册应用,可以在其中查看照片和录制的视频。
- 拍照按钮:使用Image组件实现,其onclick事件调用cameraModel.ts的takepicture方法开始拍照。
- 照片存储:MediaModel.ts中的createAndGetUri方法通过引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest类创建媒体资源,saveImage方法将拍摄的照片写入到Mediamodel传回的资源中去。
## 开发步骤 ## 开发步骤
1.申请所需权限 1. 申请所需权限
​ 在model.json5中添加以下配置: 在model.json5中添加以下配置:
```json ```json
"requestPermissions": [ "requestPermissions": [
{ {
"name": "ohos.permission.CAMERA"//允许应用使用相机拍摄照片和录制视频 "name": "ohos.permission.CAMERA"//允许应用使用相机拍摄照片和录制视频
}, },
{ {
"name": "ohos.permission.MICROPHONE"//允许应用使用麦克风 "name": "ohos.permission.MICROPHONE"//允许应用使用麦克风
}, },
{ {
"name": "ohos.permission.MEDIA_LOCATION"//允许应用访问用户媒体文件中的地理位置信息 "name": "ohos.permission.MEDIA_LOCATION"//允许应用访问用户媒体文件中的地理位置信息
}, },
{ {
"name": "ohos.permission.WRITE_MEDIA"//允许应用读写用户外部存储中的媒体文件信息 "name": "ohos.permission.WRITE_MEDIA"//允许应用读写用户外部存储中的媒体文件信息
}, },
{ {
"name": "ohos.permission.READ_MEDIA"//允许应用读取用户外部存储中的媒体文件信息 "name": "ohos.permission.READ_MEDIA"//允许应用读取用户外部存储中的媒体文件信息
} }
] ]
``` ```
2.创建绘制组件XComponent 2. 创建绘制组件XComponent以输出摄像头获取的画面,其绑定的onload方法中设定了画幅的大小。
```typescript ```typescript
build() { build() {
Column() { Column() {
Title() Title()
.visibility(this.isTitleShow ? Visibility.Visible : Visibility.None) .visibility(this.isTitleShow ? Visibility.Visible : Visibility.None)
Stack({ alignContent: Alignment.Bottom }) { Stack({ alignContent: Alignment.Bottom }) {
Stack({ alignContent: Alignment.TopStart }) { Stack({ alignContent: Alignment.TopStart }) {
XComponent({ XComponent({
id: 'componentId', id: 'componentId',
type: 'surface', type: 'surface',
controller: this.mXComponentController //将控制器绑定至XComponent组件 controller: this.mXComponentController //将控制器绑定至XComponent组件
}) })
.onLoad(() => { .onLoad(() => {
this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 });//设置surface大小 this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 });//设置surface大小
this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
this.currentModel = CameraMode.modePhoto; this.currentModel = CameraMode.modePhoto;
this.cameraModel.initCamera(this.surfaceId); //调用model/cameraModel.ts初始化相机功能 this.cameraModel.initCamera(this.surfaceId); //调用model/cameraModel.ts初始化相机功能
}) })
.width('100%') .width('100%')
.height('100%') .height('100%')
.margin({ bottom: 152 }) .margin({ bottom: 152 })
Column() { Column() {
} }
.width('97%') .width('97%')
.height('100%') .height('100%')
``` ```
3.初始化相机功能 3. 初始化相机功能
```typescript initCamera方法通过创建相机管理器实例cameraMgr来创建画面输出对象previewOutput。cameraMgr再通过创建CaptureSession实例来配置会话,完成相机功能的准备工作。
import image from '@ohos.multimedia.image';//自@ohos.multimedia.image引入image,提供图片处理效果
.................. ```typescript
private receiver: image.ImageReceiver = undefined;//图像接收类,用于获取组件surface id,接收最新的图片和读取下一张图片 import image from '@ohos.multimedia.image';//自@ohos.multimedia.image引入image,提供图片处理效果
.................. ...
constructor() { private receiver: image.ImageReceiver = undefined;//图像接收类,用于获取组件surface id,接收最新的图片和读取下一张图片
this.mediaModel = MediaModel.getMediaInstance();//通过调用model/MediaModel.ets中的方法创建mediaInstance类mediaModel ...
//创建ImageReceiver实例receiver constructor() {
this.receiver = image.createImageReceiver( this.mediaModel = MediaModel.getMediaInstance();//通过调用model/MediaModel.ets中的方法创建mediaInstance类mediaModel
cameraWH.width, //创建ImageReceiver实例receiver
cameraWH.height, this.receiver = image.createImageReceiver(
FOUR, cameraWH.width,
EIGHT cameraWH.height,
); FOUR,
//接收图片时注册回调 EIGHT
this.receiver.on('imageArrival', () => { );
//从ImageReceiver读取下一张图片 //接收图片时注册回调
this.receiver.readNextImage((err, image) => { this.receiver.on('imageArrival', () => {
if (err || image === undefined) { //从ImageReceiver读取下一张图片
return; this.receiver.readNextImage((err, image) => {
} if (err || image === undefined) {
//根据图像的组件类型从图像中获取组件缓存 return;
image.getComponent(FOUR, (errMsg, img) => { }
if (errMsg || img === undefined) { //根据图像的组件类型从图像中获取组件缓存
return; image.getComponent(FOUR, (errMsg, img) => {
} if (errMsg || img === undefined) {
let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE); return;
if (img.byteBuffer) { }
buffer = img.byteBuffer; let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE);
} if (img.byteBuffer) {
this.saveImage(buffer, image); buffer = img.byteBuffer;
}); }
}); this.saveImage(buffer, image);
}); });
} });
});
}
async initCamera(surfaceId: string): Promise<void> {
..................
try { async initCamera(surfaceId: string): Promise<void> {
this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);//获取相机管理器实例 ...
} try {
this.camerasArray = this.cameraMgr.getSupportedCameras();//获取支持指定的相机设备对象 this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);//获取相机管理器实例
if (this.camerasArray.length === 0) { }
return; this.camerasArray = this.cameraMgr.getSupportedCameras();//获取支持指定的相机设备对象
} if (this.camerasArray.length === 0) {
let mCamera = this.camerasArray[0]; return;
this.cameraInput = this.cameraMgr.createCameraInput(mCamera); }
this.cameraInput.open(); let mCamera = this.camerasArray[0];
this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);//查询相机设备支持的输出能力 this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
let previewProfile = this.capability.previewProfiles[0]; this.cameraInput.open();
//通过相机管理器创建预览输出对象 this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);//查询相机设备支持的输出能力
this.previewOutput = this.cameraMgr.createPreviewOutput( let previewProfile = this.capability.previewProfiles[0];
previewProfile, //通过相机管理器创建预览输出对象
surfaceId //surfaceId从XComponent组件获取 this.previewOutput = this.cameraMgr.createPreviewOutput(
); previewProfile,
let rSurfaceId = await this.receiver.getReceivingSurfaceId();//获取一个surface id供其他组件使用 surfaceId //surfaceId从XComponent组件获取
let photoProfile = this.capability.photoProfiles[0]; );
//通过相机管理器创建照片输出对象 let rSurfaceId = await this.receiver.getReceivingSurfaceId();//获取一个surface id供其他组件使用
this.photoOutPut = this.cameraMgr.createPhotoOutput( let photoProfile = this.capability.photoProfiles[0];
photoProfile, //通过相机管理器创建照片输出对象
rSurfaceId //rSurfaceId通过构造函数中定义的图像接收类receiver获取 this.photoOutPut = this.cameraMgr.createPhotoOutput(
); photoProfile,
this.capSession = this.cameraMgr.createCaptureSession();//创建CaptureSession实例 rSurfaceId //rSurfaceId通过构造函数中定义的图像接收类receiver获取
this.capSession.beginConfig();//开始配置会话 );
this.capSession.addInput(this.cameraInput);//将cameraInput加入会话 this.capSession = this.cameraMgr.createCaptureSession();//创建CaptureSession实例
this.capSession.addOutput(this.previewOutput);//将预览输出加入会话 this.capSession.beginConfig();//开始配置会话
this.capSession.addOutput(this.photoOutPut);//将照片输出加入会话 this.capSession.addInput(this.cameraInput);//将cameraInput加入会话
await this.capSession.commitConfig();//提交配置信息 this.capSession.addOutput(this.previewOutput);//将预览输出加入会话
await this.capSession.start();//开始输出 this.capSession.addOutput(this.photoOutPut);//将照片输出加入会话
} await this.capSession.commitConfig();//提交配置信息
await this.capSession.start();//开始输出
``` }
4.点击按钮进行拍照 ```
```typescript 4. 点击按钮进行拍照
Image(this.getCameraIcon())
.size({ width: 64, height: 64 }) 拍照按钮通过Image组件呈现,其绑定的onClick方法调用takePicture方法开始拍照。
.margin({ left: 10 })
.id('camera') ```typescript
.onClick(() => { Image(this.getCameraIcon())
if (this.currentModel === CameraMode.modePhoto) { .size({ width: 64, height: 64 })
prompt.showToast({ message: '拍照中...', duration: 200 }); .margin({ left: 10 })
this.cameraModel.takePicture();//调用model/cameraModel.takePicture()开始拍照 .id('camera')
} .onClick(() => {
}) if (this.currentModel === CameraMode.modePhoto) {
``` prompt.showToast({ message: '拍照中...', duration: 200 });
this.cameraModel.takePicture();//调用model/cameraModel.takePicture()开始拍照
5.拍照功能具体实现 }
})
- 拍照 ```
```typescript 5. 拍照功能具体实现
async takePicture(): Promise<void> {
//设置拍照相关参数 - 拍照
let photoSettings = {
rotation: this.imageRotation, ```typescript
quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM, async takePicture(): Promise<void> {
location: { //设置拍照相关参数
// 位置信息,经纬度 let photoSettings = {
latitude: 12.9698, rotation: this.imageRotation,
longitude: 77.75, quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
altitude: 1000, location: {
}, // 位置信息,经纬度
mirror: false, latitude: 12.9698,
}; longitude: 77.75,
await this.photoOutPut.capture(photoSettings); altitude: 1000,
AppStorage.Set('isRefresh', true); },
} mirror: false,
``` };
await this.photoOutPut.capture(photoSettings);
- 保存图片 AppStorage.Set('isRefresh', true);
}
```typescript ```
..................//model/MediaModel.ts中定义的负责保存图片的相关方法
async createAndGetUri(mediaType: mediaLibrary.MediaType): Promise<mediaLibrary.FileAsset> { - 保存图片
let dateTimeUtil: DateTimeUtil = new DateTimeUtil();
let info: FileInfo = this.getInfoFromMediaType(mediaType); saveImage方法使用MediaModel中的createAndGetUri方法创建Image类型资源,将拍摄到的照片写入到这个资源中去。
let name: string = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;//获取当前时间
let displayName: string = `${info.prefix}${name}${info.suffix}`; ```typescript
//获取公共目录路径。 //model/MediaModel.ts中定义的负责保存图片的相关方法
let publicPath: string = await this.mediaLibraryTest.getPublicDirectory( async createAndGetUri(mediaType: mediaLibrary.MediaType): Promise<mediaLibrary.FileAsset> {
info.directory let dateTimeUtil: DateTimeUtil = new DateTimeUtil();
);//通过引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest类创建媒体资源,其中定义了媒体类型、名称、路径。 let info: FileInfo = this.getInfoFromMediaType(mediaType);
let fileAsset: mediaLibrary.FileAsset = await this.mediaLibraryTest.createAsset( let name: string = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;//获取当前时间
mediaType,//根据传入函数createAndGetUri的mediaType参数决定创建什么类型的媒体资源 let displayName: string = `${info.prefix}${name}${info.suffix}`;
displayName, //获取公共目录路径。
publicPath let publicPath: string = await this.mediaLibraryTest.getPublicDirectory(
); info.directory
return fileAsset; );//通过引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest类创建媒体资源,其中定义了媒体类型、名称、路径。
} let fileAsset: mediaLibrary.FileAsset = await this.mediaLibraryTest.createAsset(
async getFdPath(fileAsset: mediaLibrary.FileAsset): Promise<number> { mediaType,//根据传入函数createAndGetUri的mediaType参数决定创建什么类型的媒体资源
let fd: number = await fileAsset.open('Rw');//打开当前文件 displayName,
return fd; publicPath
} );
.................. return fileAsset;
}
async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> { async getFdPath(fileAsset: mediaLibrary.FileAsset): Promise<number> {
this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE); let fd: number = await fileAsset.open('Rw');//打开当前文件
//通过调用MediaModel中的方法创建Image类型资源 return fd;
this.photoPath = this.fileAsset.uri; }
this.fd = await this.mediaModel.getFdPath(this.fileAsset); ...
await fileIo.write(this.fd, buffer);//将拍摄的照片写入到Mediamodel传回的资源中去
await this.fileAsset.close(this.fd);//释放open函数 async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> {
await img.release(); this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE);
if (this.takePictureHandle) { //通过调用MediaModel中的方法创建Image类型资源
this.takePictureHandle(this.photoPath); this.photoPath = this.fileAsset.uri;
} this.fd = await this.mediaModel.getFdPath(this.fileAsset);
} await fileIo.write(this.fd, buffer);//将拍摄的照片写入到Mediamodel传回的资源中去
``` await this.fileAsset.close(this.fd);//释放open函数
await img.release();
if (this.takePictureHandle) {
this.takePictureHandle(this.photoPath);
}
}
```
## 全部代码 ## 全部代码
...@@ -242,7 +250,8 @@ Image(this.getCameraIcon()) ...@@ -242,7 +250,8 @@ Image(this.getCameraIcon())
## 参考 ## 参考
[权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync) - [权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
- [@ohos.multimedia.camera](../application-dev/reference/apis/js-apis-camera.md)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册