-
【HarmonyOS】SaveButton组件把图片显示到相册中的方法demo,支持组件截图、url网络图片、base64格式图片。注意事项:1、不支持自定义SaveButton样式。2、下载按钮被遮挡一部分,也无法保存到相册。import photoAccessHelper from '@ohos.file.photoAccessHelper';import fs from '@ohos.file.fs';import { common } from '@kit.AbilityKit';import { componentSnapshot, promptAction } from '@kit.ArkUI';import { image } from '@kit.ImageKit';import { BusinessError } from '@kit.BasicServicesKit';import { http } from '@kit.NetworkKit';import { util } from '@kit.ArkTS';@Entry@Componentstruct Page09 { inviteQrCodeID: string = "inviteQrCodeID" @State imageUrl: string = "https://img1.baidu.com/it/u=1268271089,1175168242&fm=253&fmt=auto&app=120&f=JPEG?w=506&h=500" @State base64Str: string = 'data:image/png;base64,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' build() { Scroll() { Column({ space: 10 }) { Column({ space: 10 }) { Text('下载组件截图图片到相册') Column() { Column() { Text('标题测试').fontSize('36lpx').height('120lpx').fontColor("#2E2E2E") QRCode('https://www.huawei.com/') .width('300lpx') .height('300lpx') .margin({ top: '25lpx' }) .draggable(false) .width(140) .height(140) }.padding({ left: '42lpx', right: '42lpx', bottom: '20lpx' }) Text('点按下载保存至相册') .textAlign(TextAlign.Center) .padding({ left: '125lpx', right: '125lpx' }) .fontColor("#4B4B4B") .fontSize('32lpx') .margin({ bottom: '70lpx' }) } .id(this.inviteQrCodeID) .padding('20lpx') .margin({ top: '30lpx', bottom: '30lpx' }) .backgroundColor(Color.White) .borderRadius('30lpx') .alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片 let helper = photoAccessHelper.getPhotoAccessHelper(context); try { // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。 let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制 let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); componentSnapshot.get(this.inviteQrCodeID).then((pixelMap) => { let packOpts: image.PackingOption = { format: 'image/png', quality: 100 } const imagePacker: image.ImagePacker = image.createImagePacker(); return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => { imagePacker.release(); //释放 fs.close(file.fd); promptAction.showToast({ message: '图片已保存至相册', duration: 2000 }); }); }) } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`); } } else { promptAction.showToast({ message: '设置权限失败!', duration: 2000 }); } }) }.borderWidth(1).borderStyle(BorderStyle.Dotted).backgroundColor(Color.Pink).padding('50lpx') Column({ space: 10 }) { Text('下载网络图片到相册') /** * 需要在 src/main/module.json5 * 添加网络权限 * { "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, ], */ Image(this.imageUrl).width('100lpx') SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片 // savePhotoToGallery(context); let helper = photoAccessHelper.getPhotoAccessHelper(context); try { http.createHttp().request( this.imageUrl, { expectDataType: http.HttpDataType.ARRAY_BUFFER } ).then(async (res) => { console.info('res', JSON.stringify(res)) // 将图片资源转为像素图(PixelMap) let pixelMap = await image.createImageSource(res.result as ArrayBuffer).createPixelMap() // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。 let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制 let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); let packOpts: image.PackingOption = { format: 'image/png', quality: 100 } const imagePacker: image.ImagePacker = image.createImagePacker(); return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => { imagePacker.release(); //释放 fs.close(file.fd); promptAction.showToast({ message: '图片已保存至相册', duration: 2000 }); }); }).catch(() => { console.info('catch') }) } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`); } } else { promptAction.showToast({ message: '设置权限失败!', duration: 2000 }); } }) }.borderWidth(1).borderStyle(BorderStyle.Dotted).backgroundColor(Color.Pink).padding('50lpx') Column({ space: 10 }) { Text('下载base64图片到相册') Text('注意1:有些base64的格式图片显示不出来,\n是因为前缀没加data:image/png;base64,').textAlign(TextAlign.Center) Text("注意2:下载到相册的base64字符串不能有'data:image/jpeg;base64,'这样的前缀。所以我这里用正则去掉了前缀").textAlign(TextAlign.Center) SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片 // savePhotoToGallery(context); let helper = photoAccessHelper.getPhotoAccessHelper(context); try { // 正则表达式用于匹配 "data:image/*;base64," 这样的前缀 const prefixRegex = /^data:image\/[a-zA-Z]+;base64,/; // 使用 replace 方法去除匹配到的前缀 let base64String = this.base64Str.replace(prefixRegex, '') let buffer: ArrayBuffer = new util.Base64Helper().decodeSync(base64String, util.Type.MIME).buffer as ArrayBuffer; let imageSource = image.createImageSource(buffer); let pixelMap = await imageSource.createPixelMap({ editable: true }); let opts: image.PackingOption = { format: "image/jpeg", quality: 100 }; // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。 let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制 let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); let packOpts: image.PackingOption = { format: 'image/png', quality: 100 } const imagePacker: image.ImagePacker = image.createImagePacker(); return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => { imagePacker.release(); //释放 fs.close(file.fd); promptAction.showToast({ message: '图片已保存至相册', duration: 2000 }); }); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`); } } else { console.info(`result:${JSON.stringify(result)}`) promptAction.showToast({ message: '设置权限失败!', duration: 2000 }); } }) } .borderWidth(1).borderStyle(BorderStyle.Dotted).backgroundColor(Color.Pink).padding('50lpx') }.width('100%') }.width('100%') }}注意事项:1、样式不支持自定义图标,试过用opacity修改透明度,然后添加背景来实现自定义样式,结果也失败了,opacity(1)点击生效,opacity(0.9)后点击按钮就不生效了。参考:cid:link_12、如果带Scroll时,下载按钮被遮挡一部分,也无法保存到相册。比如下面这样遮挡一点点下载按钮后,点击就没办法保存到相册了。【参考方案】1、官方文档:cid:link_02、PixelMap和base64的相互转换参考文档:cid:link_2转载自https://www.cnblogs.com/zhongcx/articles/18433304
-
【HarmonyOS】给组件添加下划线或者画虚线边框可以使用.borderStyle和.borderWidth@Entry@Componentstruct Page51 { build() { Column() { Text('虚线边框测试') .width(200) .height(150) .borderStyle(BorderStyle.Dashed) .borderWidth(1) .backgroundColor('#F5F5F5') Row() { Text('利用底部边框实现添加下划线效果') .height('48lpx') .margin({ top: '66lpx' }) .fontSize('28lpx') .fontColor("#FF1919") .textAlign(TextAlign.Center) .borderWidth({ bottom: 1 }) .borderStyle(BorderStyle.Solid) .borderColor("#FF1919") }.width('100%').justifyContent(FlexAlign.Center) } .width('100%') .height('100%') }}转载自https://www.cnblogs.com/zhongcx/articles/18433301
-
web组件的h5页面调用鸿蒙app图库和拍照示例 1、添加权限:entry/src/main/module.json5 2、测试文件:src/main/resources/rawfile/page107.html<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>更换头像示例</title> <style> body { /* 确保图片不会超出屏幕宽度 */ margin: 0; padding: 0; overflow-x: hidden; } #avatarPreview { max-width: 50vw; /* 设置图片最大宽度为屏幕宽度的50% */ display: block; /* 确保图片作为块级元素显示 */ margin: 10px auto; /* 居中图片 */ } #avatarPreview img { width: 100%; /* 图片宽度自动适应其容器宽度 */ height: auto; /* 高度自适应保持图片比例 */ } </style></head><body><h2>点击选择新头像</h2><input type="file" id="avatarInput" accept="image/*" style="margin-bottom:10px;"><button onclick="uploadAvatar()">上传头像至OSS</button><button onclick="changeAvatar()">显示头像</button><div id="avatarPreview"></div><script> async function uploadAvatar() { const input = document.getElementById('avatarInput'); if (input.files && input.files[0]) { // 模拟异步上传图片至OSS的过程 try { const formData = new FormData(); formData.append('file', input.files[0]); // 这里使用fetch API模拟上传请求,实际应用中需要替换为真实的服务端API地址 const response = await fetch('https://your-fake-oss-api.com/upload', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); alert('图片上传成功!服务器响应:' + JSON.stringify(result)); } catch (error) { alert('图片上传失败:' + error); } } } function changeAvatar() { const input = document.getElementById('avatarInput'); if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function(e) { document.getElementById('avatarPreview').innerHTML = `<img src="${e.target.result}" alt="头像预览">`; }; reader.readAsDataURL(input.files[0]); } }</script></body></html>3、示例代码:src/main/ets/pages/Page107.etsimport web_webview from '@ohos.web.webview';import picker from '@ohos.file.picker';import fs from '@ohos.file.fs';import { common } from '@kit.AbilityKit';interface MyEvent { result: FileSelectorResult, fileSelector: FileSelectorParam}@Entry@Componentstruct Page107 { controller: web_webview.WebviewController = new web_webview.WebviewController(); handleFileSelection(event: MyEvent) { const PhotoSelectOptions = new picker.PhotoSelectOptions(); PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; PhotoSelectOptions.maxSelectNumber = 1; const photoPicker = new picker.PhotoViewPicker(); photoPicker.select(PhotoSelectOptions) .then((PhotoSelectResult) => { if (PhotoSelectResult.photoUris.length === 0) { console.warn('No image selected.'); return; } const srcUri = PhotoSelectResult.photoUris[0]; const context = getContext(this) as common.UIAbilityContext; const destPath = `${context.filesDir}/test${new Date().getTime()}.jpg`; try { let file = fs.openSync(srcUri, fs.OpenMode.READ_ONLY); fs.copyFileSync(file.fd, destPath); event?.result.handleFileList([destPath]); } catch (copyError) { console.error('Copying the file failed:', JSON.stringify(copyError)); } }) .catch((selectError: object) => { console.error('Failed to invoke photo picker:', JSON.stringify(selectError)); }); return true; } build() { Column() { Web({ src: $rawfile('page107.html'), // src: 'https://xxx', controller: this.controller }) .width('100%') .height('100%') .domStorageAccess(true)//设置是否开启文档对象模型存储接口(DOM Storage API)权限。 .javaScriptAccess(true)//设置是否允许执行JavaScript脚本,默认允许执行。 .databaseAccess(true)//设置是否开启数据库存储API权限,默认不开启。 .mixedMode(MixedMode.All)//HTTP和HTTPS混合 .fileAccess(true)//设置是否开启应用中文件系统的访问,默认启用。 .imageAccess(true)//设置是否允许自动加载图片资源,默认允许。 .geolocationAccess(true)//设置是否开启获取地理位置权限,默认开启。 .onlineImageAccess(true)//设置是否允许从网络加载图片资源(通过HTTP和HTTPS访问的资源),默认允许访问。 .mediaPlayGestureAccess(true)//设置有声视频播放是否需要用户手动点击,静音视频播放不受该接口管控,默认需要。 .onShowFileSelector(this.handleFileSelection.bind(this)) } .width('100%') .height('100%') }}转载自https://www.cnblogs.com/zhongcx/articles/18433297
-
实现一个自定义带圆点的进度条效果【起因】论坛里有人问进度条如何带圆点,参考:https://developer.huawei.com/consumer/cn/forum/topic/0208157230875079489?fid=0101587866109860105&pid=0301157237673947937方案就是做一个圆角组件,然后利用rotate旋转,至于动画效果,我查了一下文档,只要设置enableSmoothEffect:false,就可以关闭动画,然后自己开个定时器,判断实际进度与动画进度的差值每隔10毫秒执行一次就行了。上面的gif图比较卡是因为录屏转gif掉帧了哈,实际代码执行很流畅。【代码】@Entry@Componentstruct Page03 { @State value: number = 70 //实际进度,单位% @State valueAnim: number = 10 //动画进度,单位% progressId: number = 0 aboutToAppear(): void { this.progressId = setInterval(() => { if (this.value > this.valueAnim) { this.valueAnim += 1 } else if (this.value < this.valueAnim) { this.valueAnim -= 1 } }, 10) } aboutToDisappear(): void { clearInterval(this.progressId) } build() { Column() { Button('设置为0%').onClick(() => { this.value = 0 }) Button('设置为50%').onClick(() => { this.value = 50 }) Button('设置为68%').onClick(() => { this.value = 68 }) Button('设置为100%').onClick(() => { this.value = 100 }) Stack() { // Image() //这里展示向内渐变的圆形图片做北京 Text(`${this.valueAnim}%`) //这里展示进度 Text('本月任务进度').fontSize('15lpx').margin({ top: '100lpx' }) Progress({ value: this.valueAnim, total: 100, type: ProgressType.Ring }) .width('200lpx').color(Color.Orange) .style({ strokeWidth: 5, shadow: false ,enableSmoothEffect:false}) //这里系统进度条,可以实现无圆点进度 Text().width('50lpx') //重点来了,这里的图片是一个png,但只有正上方有一个白芯的圆点,然后根据进度计算角度把图片进行旋转,也就是Image().rotate(根据进度计算角度哈) Stack() { Text() .backgroundColor(Color.White) .borderColor(Color.Orange) .borderWidth(5) .width('27lpx') .height('27lpx') .borderRadius('50lpx') }.width('220lpx').height('220lpx').align(Alignment.Top).rotate({angle:this.valueAnim / 100 * 360}) }.width('300lpx').height('300lpx').backgroundColor(Color.Pink) } .height('100%') .width('100%') }}转载自https://www.cnblogs.com/zhongcx/articles/18433293
-
实现Text组件中文本两端对齐方案@Entry@Componentstruct Page29 { @State str_1 :string = '文本内容' @State str_2 :string = '文本内容文本内容abcd1234也对也不对,八荣八耻。' build() { Column() { Text('单行效果') Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) { ForEach(this.str_1.split(''), (item: string, index: number) => { Text(item) .fontSize(14) }) }.width(100) .backgroundColor(Color.Pink) Text('多行效果') Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) { ForEach(this.str_2.split(''), (item: string, index: number) => { Text(item) .fontSize(14) }) }.width(100) .backgroundColor(Color.Pink) } .height('100%') .width('100%') }}转载自https://www.cnblogs.com/zhongcx/articles/18433285
-
实现分页滚动文本组件:为何选择 Scroll + Text 而非 textOverflowimport { promptAction } from '@kit.ArkUI'@Entry@Componentstruct Page37 { @State lineHeight: number = 0 // 单行文本的高度 @State pageHeight: number = 0 // 每页的最大高度 @State totalContentHeight: number = 0 // 整个文本内容的高度 @State textContent: string = " " // 文本内容,默认一个空格是为了计算单行文本的高度 @State scrollOffset: number = 0 // 当前滚动偏移量 @State totalPages: number = 1 // 总页数 @State currentPage: number = 1 // 当前页数 scroller: Scroller = new Scroller() // 滚动条实例 resetMaxLineHeight() { if (this.lineHeight > 0 && this.pageHeight > 0 && this.totalContentHeight > 0) { this.pageHeight = (Math.floor(this.pageHeight / this.lineHeight)) * this.lineHeight this.totalPages = Math.ceil(this.totalContentHeight / this.pageHeight) //向上取整得到总页数 } } build() { Column() { Text('第一章') .margin({ top: 10, bottom: 10 }) .backgroundColor(Color.Pink) .width('100%') .textAlign(TextAlign.Center) Column() { Scroll(this.scroller) { Column() { Text(this.textContent) .backgroundColor(Color.Orange) .fontSize(20) .lineHeight(40) .fontColor(Color.Black)// .textOverflow({ overflow: TextOverflow.Clip }) .margin({ top: this.scrollOffset }) .onAreaChange((oldArea: Area, newArea: Area) => { if (this.lineHeight == 0 && newArea.height > 0) { this.lineHeight = newArea.height as number this.resetMaxLineHeight() //添加数据测试 let str = "" for (let i = 1; i <= 20; i++) { str += ` ${i}、荣誉和耻辱,是荣辱观中的一对基本范畴,是指社会对人们行为褒贬评价以及人们对这种评价的自我感受。知荣辱,是人性的标志,是人区别于动物、人之为人的重要标准。` } this.textContent = str return } if (this.totalContentHeight != newArea.height) { console.info(`newArea.height:${newArea.height}`) this.totalContentHeight = newArea.height as number this.resetMaxLineHeight() } }) }.hitTestBehavior(HitTestMode.Block) //禁止滑动 }.scrollBar(BarState.Off) .constraintSize({ maxHeight: this.pageHeight == 0 ? 1000 : this.pageHeight }) } .width('100%') .layoutWeight(1) .onAreaChange((oldArea: Area, newArea: Area) => { if (this.pageHeight == 0 && newArea.height > 0) { this.pageHeight = newArea.height as number this.resetMaxLineHeight() } }) Row() { Button('上一页').onClick(() => { if (this.currentPage == 1) { promptAction.showToast({ message: "没有上一页了" }) return; } this.scrollOffset += this.pageHeight this.currentPage--; }) Text(`${this.currentPage}/${this.totalPages}`) Button('下一页').onClick(() => { if (this.currentPage == this.totalPages) { promptAction.showToast({ message: "没有下一页了" }) return; } this.scrollOffset -= this.pageHeight this.currentPage++; }) }.margin({ top: 10, bottom: 10 }).backgroundColor(Color.Pink).width('100%').justifyContent(FlexAlign.SpaceAround) } .width('100%') .height('100%') .backgroundColor(Color.Gray) }}【实现思路】目标是实现在HarmonyOS应用中的分页滚动文本效果,使得用户能够通过“上一页”和“下一页”按钮来浏览不同的页面。我们选择使用 Scroll 组件结合 Text 组件来实现这一功能,而不是采用 textOverflow 的方式,原因在于 textOverflow 无法直接获取到文本控件被截断后的内容。具体实现过程如下:初始化状态:利用 @State 装饰器定义状态变量来存储单行文本的高度 (lineHeight)、每页的最大高度 (pageHeight)、文本内容的总高度 (totalContentHeight)、文本内容 (textContent)、滚动偏移量 (scrollOffset)、总页数 (totalPages) 和当前页数 (currentPage)。计算单行高度:通过监听 Text 组件的 onAreaChange 事件,当首次获取到文本元素的高度时,将其赋值给 lineHeight 并调用 resetMaxLineHeight 方法来计算每页的最大高度。生成内容:初始时,textContent 中包含一个空格,以便能够计算出单行文本的高度。一旦单行高度计算完成,通过循环生成多个段落填充文本内容。分页逻辑:① 当 totalContentHeight 发生变化时,调用 resetMaxLineHeight 方法更新总页数。② “上一页”和“下一页”按钮通过修改 scrollOffset 和 currentPage 来实现翻页效果。UI 布局与滚动控制:① 使用 Column 和 Row 布局来组织界面元素。② Scroll 组件用于创建滚动区域,而 Text 组件则用于显示文本内容。③ 通过设置 hitTestBehavior 为 HitTestMode.Block 来阻止文本区域的滑动行为,确保滚动仅发生在父级滚动区域中。适配不同屏幕尺寸:① 为了确保组件在不同设备上的表现一致,可以考虑使用百分比布局或者动态计算容器尺寸的方法来适应不同屏幕尺寸。② 通过设置 Scroll 组件的 constraintSize 属性,限制其最大高度为 pageHeight 或默认值 1000,以确保内容不会超出当前页面的高度。动态计算内容高度:① 通过监听 Scroll 组件的 onAreaChange 事件,当容器高度发生变化时,重新计算 pageHeight 和 totalPages。② 这样可以确保组件能够动态地适应不同的屏幕尺寸和内容长度,避免内容溢出或遮挡问题。【为何不使用 textOverflow?】① 无法直接获取截断后的内容: textOverflow 主要用于处理文本过长时的显示问题,但不能直接获取到文本被截断后的内容。这使得在分页时难以准确判断当前页面显示的是文本的哪一部分。② 难以实现分页逻辑: 由于 textOverflow 不提供获取截断文本内容的API,因此难以实现精确的分页逻辑,比如计算每页显示的内容范围。③ 用户体验: 使用 Scroll 和 Text 的组合可以更好地控制文本的显示和分页,从而提供更平滑的阅读体验。【总结】通过上述步骤,构建了一个简单但功能完备的分页滚动文本组件,可用于展示长文本内容,适用于多种场景。用户可以方便地通过“上一页”和“下一页”按钮浏览不同页面,而无需担心内容的显示问题。转载自https://www.cnblogs.com/zhongcx/articles/18433276
-
【HarmonyOS】普通组件与web组件长截屏方案:原则是利用Scroll内的组件可以使用componentSnapshot完整的截屏【普通组件长截屏】import { componentSnapshot, promptAction } from '@kit.ArkUI'import { common } from '@kit.AbilityKit'import { photoAccessHelper } from '@kit.MediaLibraryKit'import fs from '@ohos.file.fs';import { image } from '@kit.ImageKit';import { BusinessError } from '@kit.BasicServicesKit';@Entry@Componentstruct Page37 { @State lineHeight: number = 0 // 单行文本的高度 @State pageHeight: number = 0 // 每页的最大高度 @State totalContentHeight: number = 0 // 整个文本内容的高度 @State textContent: string = " " // 文本内容,默认一个空格是为了计算单行文本的高度 @State scrollOffset: number = 0 // 当前滚动偏移量 @State totalPages: number = 1 // 总页数 @State currentPage: number = 1 // 当前页数 scroller: Scroller = new Scroller() // 滚动条实例 resetMaxLineHeight() { if (this.lineHeight > 0 && this.pageHeight > 0 && this.totalContentHeight > 0) { this.pageHeight = (Math.floor(this.pageHeight / this.lineHeight)) * this.lineHeight this.totalPages = Math.ceil(this.totalContentHeight / this.pageHeight) //向上取整得到总页数 } } build() { Column() { SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片 let helper = photoAccessHelper.getPhotoAccessHelper(context); try { // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。 let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制 let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); componentSnapshot.get("aaaa").then((pixelMap) => { let packOpts: image.PackingOption = { format: 'image/png', quality: 100 } const imagePacker: image.ImagePacker = image.createImagePacker(); return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => { imagePacker.release(); //释放 fs.close(file.fd); promptAction.showToast({ message: '图片已保存至相册', duration: 2000 }); }); }) } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`); } } else { promptAction.showToast({ message: '设置权限失败!', duration: 2000 }); } }) Text('第一章') .margin({ top: 10, bottom: 10 }) .backgroundColor(Color.Pink) .width('100%') .textAlign(TextAlign.Center) Column() { Scroll(this.scroller) { Column() { Text(this.textContent) .id('aaaa') .backgroundColor(Color.Orange) .fontSize(20) .lineHeight(40) .fontColor(Color.Black)// .textOverflow({ overflow: TextOverflow.Clip }) .margin({ top: this.scrollOffset }) .onAreaChange((oldArea: Area, newArea: Area) => { if (this.lineHeight == 0 && newArea.height > 0) { this.lineHeight = newArea.height as number this.resetMaxLineHeight() //添加数据测试 let str = "" for (let i = 1; i <= 20; i++) { str += ` ${i}、荣誉和耻辱,是荣辱观中的一对基本范畴,是指社会对人们行为褒贬评价以及人们对这种评价的自我感受。知荣辱,是人性的标志,是人区别于动物、人之为人的重要标准。` } this.textContent = str return } if (this.totalContentHeight != newArea.height) { console.info(`newArea.height:${newArea.height}`) this.totalContentHeight = newArea.height as number this.resetMaxLineHeight() } }) }.hitTestBehavior(HitTestMode.Block) //禁止滑动 }.scrollBar(BarState.Off) .constraintSize({ maxHeight: this.pageHeight == 0 ? 1000 : this.pageHeight }) } .width('100%') .layoutWeight(1) .onAreaChange((oldArea: Area, newArea: Area) => { if (this.pageHeight == 0 && newArea.height > 0) { this.pageHeight = newArea.height as number this.resetMaxLineHeight() } }) Row() { Button('上一页').onClick(() => { if (this.currentPage == 1) { promptAction.showToast({ message: "没有上一页了" }) return; } this.scrollOffset += this.pageHeight this.currentPage--; }) Text(`${this.currentPage}/${this.totalPages}`) Button('下一页').onClick(() => { if (this.currentPage == this.totalPages) { promptAction.showToast({ message: "没有下一页了" }) return; } this.scrollOffset -= this.pageHeight this.currentPage++; }) }.margin({ top: 10, bottom: 10 }).backgroundColor(Color.Pink).width('100%').justifyContent(FlexAlign.SpaceAround) } .width('100%') .height('100%') .backgroundColor(Color.Gray) }}【web组件长截屏】src/main/resources/rawfile/test.html<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <script> //用于根据浏览器对 CSS.supports 和 env/constant 的支持情况,动态地调整视口元标签的内容,以达到最佳的页面显示效果。 var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)')) document.write( '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />') </script> <title></title> <!--用于设置浏览器页签上显示的小图标 start--> <!-- <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" /> --> <link rel="stylesheet" href="mycss.css" /> <link rel="icon" href="./static/favicon.ico" /> <!--用于设置浏览器页签上显示的小图标 end--> <!--preload-links--> <!--app-context--></head><body><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>测试测试</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>哈哈哈哈</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>1111111</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>2222222</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>aaaaaaa</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>bbbbbbb</div><div>到底了</div><div id="webBottom"></div></body><script> //Android禁止微信调整字体大小 (function() { if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") { handleFontSize(); } else { if (document.addEventListener) { document.addEventListener("WeixinJSBridgeReady", handleFontSize, false); } else if (document.attachEvent) { document.attachEvent("WeixinJSBridgeReady", handleFontSize); document.attachEvent("onWeixinJSBridgeReady", handleFontSize); } } function handleFontSize() { WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize': 0 }); WeixinJSBridge.on('menu:setfont', function() { WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize': 0 }); }); } })(); function setWebHeight() { window.hm.setWebHeight(document.getElementById('webBottom').offsetTop); } // 在文档加载完成后执行 setWebHeight 函数 window.onload = function() { setWebHeight(); };</script></html>src/main/ets/pages/Page42.etsimport { webview } from '@kit.ArkWeb';import web_webview from '@ohos.web.webview';import dataPreferences from '@ohos.data.preferences';import { common } from '@kit.AbilityKit';import { photoAccessHelper } from '@kit.MediaLibraryKit';import { componentSnapshot, promptAction } from '@kit.ArkUI';import fs from '@ohos.file.fs';import { image } from '@kit.ImageKit';import { BusinessError } from '@kit.BasicServicesKit';class WebService { setWebHeight = (height: string) => { console.info('web高度:', height); getContext().eventHub.emit("设置web高度",height) }}@Entry@Componentstruct Page42 { controller: webview.WebviewController = new webview.WebviewController(); webService: WebService = new WebService( ); methodList: Array<string> = [] @State isShort: boolean = true @State webHeight: number | undefined = undefined aboutToAppear(): void { this.methodList.splice(0) //清空原数组 console.info('====this.testObjtest', JSON.stringify(this.webService)) Object.keys(this.webService).forEach((key) => { this.methodList.push(key) console.info('====key', key) }); getContext().eventHub.on("设置web高度",(height:number)=>{ this.webHeight = height }) } build() { Scroll() { Column() { SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片 let helper = photoAccessHelper.getPhotoAccessHelper(context); try { // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。 let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制 let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); componentSnapshot.get("aaaa").then((pixelMap) => { let packOpts: image.PackingOption = { format: 'image/png', quality: 100 } const imagePacker: image.ImagePacker = image.createImagePacker(); return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => { imagePacker.release(); //释放 fs.close(file.fd); promptAction.showToast({ message: '图片已保存至相册', duration: 2000 }); }); }) } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`); } } else { promptAction.showToast({ message: '设置权限失败!', duration: 2000 }); } }) Text('1234测试顶部').backgroundColor(Color.Red).width('100%').height('800lpx') Web({ src: $rawfile('test.html'), controller: this.controller, renderMode: RenderMode.SYNC_RENDER }) .width('100%') .height(this.webHeight) .layoutMode(WebLayoutMode.FIT_CONTENT) .javaScriptAccess(true)//设置是否允许执行JavaScript脚本,默认允许执行。 .mixedMode(MixedMode.All)//HTTP和HTTPS混合 .javaScriptProxy({ name: "hm", object: this.webService, methodList: this.methodList, controller: this.controller, }).id("aaaa") Text('测试底部').backgroundColor(Color.Blue).width('100%').height('800lpx') } }.width('100%').height('100%').align(Alignment.Top) }}转载自https://www.cnblogs.com/zhongcx/articles/18433271
-
华为开发者布道师技术沙龙西安站的活动中,来自西安交通大学的谢涛老师作了题为“产学合作背景下的西安交通大学鸿蒙实践”的课程改革经验分享。谢涛老师分别从产学合作背景、西安交通大学鸿蒙实践、成效与反馈、未来展望等方面阐述了产学合作背景下西安交通大学的鸿蒙实践及效果。
-
鸿蒙开发实战:分析服务(HarmonyOS-JavaScript)介绍鸿蒙操作系统(HarmonyOS)是由华为开发的一款分布式操作系统,旨在实现跨设备的无缝连接。其核心设计理念是“一个内核支持多种设备”,包括智能手机、可穿戴设备、智慧屏等。本文将重点介绍如何在HarmonyOS中使用JavaScript进行分析服务的开发。应用使用场景分析服务可以用于以下场景:用户行为分析:收集应用程序中的用户交互数据,以优化用户体验。性能监控:实时获取设备性能指标,提高应用程序响应速度和稳定性。故障诊断:捕获并分析异常事件,以快速定位和修复问题。原理解释分析服务的核心是数据的收集与处理。通过仪表化代码(Instrumentation Code)来收集特定的信息,并将这些信息上传到服务器进行进一步分析。算法原理流程图[Start] --> [Instrumentation Code] --> [Data Collection] --> [Data Transmission] --> [Server Analysis] --> [Result Feedback] --> [End]算法原理解释Instrumentation Code:在应用源代码中嵌入代码,以便记录用户交互、性能指标或异常事件。Data Collection:收集来自不同设备的数据,可能涉及日志、计数器或指标。Data Transmission:通过网络将收集的数据发送到后端服务器。Server Analysis:在服务器上对数据进行清洗和分析,生成有意义的报告或采取自动反应措施。Result Feedback:将分析结果反馈给应用开发者,帮助优化应用。实际详细应用代码示例实现以下是一个简单的JavaScript代码示例,用于在HarmonyOS应用中实现基本的用户行为分析。import analytics from '@system.analytics'; // 初始化分析服务 analytics.enableAnalytics(true); // 定义一个函数来记录点击事件 function logClickEvent(buttonName) { analytics.record({ event: 'button_click', params: { button_name: buttonName, timestamp: new Date().toISOString() } }); } // 绑定按钮点击事件 document.getElementById('myButton').addEventListener('click', function() { logClickEvent('myButton'); });测试代码测试代码应该确保点击事件被正确记录,并且数据被成功传输到服务器。// 模拟点击事件 document.getElementById('myButton').click(); // 检查控制台日志以验证记录 console.log('Check if button click event is recorded.');部署场景移动应用:分析用户在App上的行为和偏好。智慧家居:监测设备使用情况及性能,为用户提供优化建议。企业系统:追踪员工在内部系统中的操作,提升工作效率。材料链接HarmonyOS 官方文档JavaScript 开发指南总结分析服务在HarmonyOS的生态体系中扮演着重要角色,它不仅有助于理解用户需求,还能够提高系统的稳定性和响应速度。通过合理的分析策略,开发者可以持续优化应用的用户体验和性能。未来展望随着物联网设备的普及和技术的不断进步,分析服务的作用将愈发重要。未来,我们期待看到更智能化、更自适应的分析工具,帮助开发者更有效地利用数据,推动创新应用的实现。
-
HarmonyOS 鸿蒙开发测试:分布式UI测试框架介绍HarmonyOS 是由华为开发的操作系统,旨在提供跨设备的无缝体验。分布式 UI 测试框架是其中一个重要组件,它使开发者能够在不同设备上进行 UI 的自动化测试,以确保一致性和可靠性。应用使用场景多设备交互测试: 检测应用在手机、平板、智能电视等多个设备上的表现。UI 一致性验证: 确保用户界面在不同尺寸屏幕上的呈现一致。性能测试: 评估应用在分布式场景下的性能表现。原理解释分布式 UI 测试框架依赖于 HarmonyOS 的分布式特性,通过统一接口将测试案例分发到各个设备上执行。每个设备独立运行测试并返回结果,最终综合分析以确保应用的稳定性和一致性。算法原理流程图+------------------------+ | Start Test Framework | +-----------+------------+ | v +-----------v------------+ | Load Test Cases | +-----------+------------+ | v +-----------v------------+ | Distribute Test | | Cases to Devices | +-----------+------------+ | v +-----------v------------+ | Execute Tests on Each | | Device | +-----------+------------+ | v +-----------v------------+ | Collect Results from | | All Devices | +-----------+------------+ | v +-----------v------------+ | Analyze and Report | | Results | +-----------+------------+ | v +-----------v------------+ | End Test Framework | +------------------------+算法原理解释加载测试用例: 从配置文件或数据库中获取需要执行的测试用例。分发测试用例: 通过网络连接(如 Wi-Fi、Bluetooth)将测试用例分配到各个目标设备。执行测试: 各设备独立执行所分配的测试用例,并记录结果。收集结果: 测试结果上传至中央服务器进行汇总。分析与报告: 生成详细的测试报告,包括错误日志和性能指标。实际详细应用代码示例实现下面是一个简单的 Python 伪代码,演示如何实现一个基本的分布式 UI 测试框架:def load_test_cases(): # 假设从某个配置源加载 return ["test_case_1", "test_case_2", "test_case_3"] def distribute_tests_to_devices(test_cases, devices): for device in devices: # 将测试用例发送到设备 send_tests(device, test_cases) def execute_tests_on_device(device, test_cases): results = [] for test in test_cases: result = run_single_test(device, test) results.append(result) return results def collect_results_from_devices(devices): all_results = [] for device in devices: results = get_results_from_device(device) all_results.extend(results) return all_results def analyze_and_report(results): # 简单分析和报告生成 pass # 主程序入口 if __name__ == "__main__": devices = ["device_1", "device_2"] test_cases = load_test_cases() distribute_tests_to_devices(test_cases, devices) results = collect_results_from_devices(devices) analyze_and_report(results)测试代码、部署场景测试代码应在真实设备环境下进行,以模拟实际使用场景。可以使用 docker 或虚拟机来搭建测试环境,但最有效的是直接在物理设备上部署。材料链接HarmonyOS 官方文档鸿蒙开源项目 GitHub总结HarmonyOS 分布式 UI 测试框架是一个强大的工具,用于确保不同设备间应用的一致性和性能。其核心是利用 HarmonyOS 的分布式能力,实现跨设备自动化测试,从而提升开发效率和产品质量。未来展望随着 IoT 设备的普及,分布式 UI 测试的重要性将会越来越高。在未来,该框架可能会进一步扩展支持,更好地适应新设备和新的交互方式,同时随着 AI 技术的发展,可能引入更智能的分析和优化算法。
-
HarmonyOS 鸿蒙开发测试: 安全测试介绍HarmonyOS(鸿蒙操作系统)是由华为开发的分布式操作系统,旨在提供一种跨设备的统一体验。安全测试在HarmonyOS中至关重要,因为一个漏洞可能会影响多个连接设备。本次讨论将涉及HarmonyOS中的安全测试,包括应用使用场景、原理解释、算法流程及实现。应用使用场景智能家居设备:确保所有联网设备的通信安全。移动设备:保护用户数据和隐私。物联网设备:保证传感器与控制器的安全交互。汽车系统:维护车载信息娱乐系统的安全性。原理解释HarmonyOS的安全框架包括:权限管理:应用程序需要申请适当的权限才能访问敏感数据。数据加密:对数据进行加密以防止未经授权的访问。沙箱机制:每个应用运行在隔离的环境中,减少相互干扰风险。算法流程图以下是基本的安全测试算法流程图:┌───────────────────────┐ │ 开始 │ ├───────────────────────┤ │ 初始化测试环境 │ ├───────────────────────┤ │ 加载待测应用 │ ├───────────────────────┤ │ 检查权限设置 │ ├───────────────────────┤ │ 执行安全扫描 │ ├───────────────────────┤ │ 分析扫描结果 │ ├────────────┬──────────────┤ │ │ 无漏洞 │ 有漏洞 │ │ └──────────────┘ ┌───────────────┘ └──────────────▲──────────────┘ 修复并重测 │ │ │ └─────────────────────────────┘算法原理解释初始化:设定测试基础设施,包括网络模拟器、设备模拟器等。加载 & 权限检查:确保应用所宣称的权限合理,不超出实际需求。安全扫描:使用自动化工具检测已知的安全漏洞。分析和修复:根据扫描报告分析漏洞并进行修复,再次测试直至通过。实际详细应用代码示例实现// A simplified example of checking permissions in a HarmonyOS app public void checkPermissions(Context context) { if (context.verifySelfPermission("ohos.permission.INTERNET") != IBundleManager.PERMISSION_GRANTED) { throw new SecurityException("Internet permission is not granted"); } }测试代码@Test public void testCheckPermissions() { Context mockContext = Mockito.mock(Context.class); Mockito.when(mockContext.verifySelfPermission("ohos.permission.INTERNET")) .thenReturn(IBundleManager.PERMISSION_DENIED); try { checkPermissions(mockContext); fail("SecurityException expected but not thrown"); } catch (SecurityException e) { // Test passes } }部署场景生产部署:在发布应用之前,通过CI/CD流水线集成安全测试。开发阶段:作为开发者本地测试的一部分,以便及时发现并解决问题。材料链接HarmonyOS 官方文档OWASP 移动应用安全测试指南总结在HarmonyOS中的安全测试是确保应用和系统健壮性的关键步骤。通过良好的权限管理、数据加密、沙箱机制以及自动化工具的结合,可以有效降低安全风险。未来展望随着物联网设备数量的激增,安全问题将变得更加复杂。未来,HarmonyOS的安全测试可能会更多地引入人工智能技术,以提高漏洞检测的效率和精准度。同时,加强与开源社区和安全研究机构的合作也将有助于构建更为安全的生态系统。
-
HarmonyOS 鸿蒙开发测试: 游戏测试介绍HarmonyOS(鸿蒙)是由华为开发的分布式操作系统,旨在为全场景智慧生态提供统一的平台。它支持多设备协同工作,使得应用可以跨设备无缝运行。在游戏开发和测试方面,HarmonyOS 提供了丰富的接口和工具,使开发者能够创建出更为智能和高效的游戏体验。应用使用场景跨设备游戏体验: 在电视、手机、平板等设备间无缝切换游戏进程。增强现实游戏: 利用 HarmonyOS 的分布式能力,将游戏与现实世界相结合。多人在线游戏: 通过设备互联实现实时多人互动。原理解释HarmonyOS 的游戏测试主要依托于其分布式架构和轻量化虚拟机(Ark Compiler),这使得代码能够高效地在多个平台上执行。通过分布式数据管理和任务调度,确保游戏在不同设备之间流畅运行。算法原理流程图Start | v Initialize Game Environment | v Load Game Assets | v Initialize Network Components | v Enter Main Game Loop |---> Check Input Events ----| | | v v Update Game Logic Render Graphics | | |<------ Synchronize State -| | v End Game Loop | v Terminate Game Session算法原理解释初始化游戏环境: 设置屏幕大小、音频等配置信息。加载游戏资源: 包含纹理、声音、关卡数据等。初始化网络组件: 准备好用于多设备通信的网络模块。进入主游戏循环: 持续检测玩家输入、更新游戏状态和渲染画面。同步状态: 确保分布式环境下所有设备的游戏状态一致。实际详细应用代码示例实现// 简单的 JS 游戏元素移动示例 for HarmonyOS let playerPosition = { x: 0, y: 0 }; function movePlayer(direction) { switch (direction) { case 'left': playerPosition.x -= 1; break; case 'right': playerPosition.x += 1; break; case 'up': playerPosition.y -= 1; break; case 'down': playerPosition.y += 1; break; } renderPlayer(); } function renderPlayer() { console.log(`Player is at (${playerPosition.x}, ${playerPosition.y})`); } // 模拟游戏循环 setInterval(() => { let randomDirection = ['left', 'right', 'up', 'down'][Math.floor(Math.random() * 4)]; movePlayer(randomDirection); }, 1000);测试代码、部署场景在实验室环境中进行测试,使用多台设备模拟现实场景。需考虑网速、延迟等对分布式游戏的影响。材料链接HarmonyOS 官方文档Ark Compiler 介绍总结HarmonyOS 为游戏开发者提供了强大的工具和平台,简化了跨设备应用的开发难度,同时提升了用户体验。未来展望随着 HarmonyOS 生态的逐步完善,预计将看到更多基于该平台的创新游戏应用涌现。未来可能会有更多关于AI和AR技术的集成,进一步增强交互性和沉浸感。
-
HarmonyOS 鸿蒙开发测试:兼容性测试介绍HarmonyOS(鸿蒙)是华为开发的一个面向全场景的分布式操作系统。为了确保应用在不同设备上都能正常运行,兼容性测试成为开发过程中的重要一环。应用使用场景兼容性测试用于验证应用程序在各种设备和环境中是否能够正常工作。适用场景包括:手机、平板、智能手表等多种设备上运行不同版本的HarmonyOS环境不同网络条件下的应用表现原理解释兼容性测试主要是通过模拟不同的硬件配置、软件版本和网络环境来检测应用是否在这些环境下保持一致性和稳定性。这涉及设备间的通信协议、一致性接口调用,以及资源管理方式的验证。算法原理流程图+----------------+ | Start | +----------------+ | v +---------------+ | Select Device | | Profiles | +---------------+ | v +---------------------+ | Setup Test Scenarios| +---------------------+ | v +-----------------------+ | Execute Tests on Each | | Scenario | +-----------------------+ | v +------------------------+ | Collect and Analyze | | Results | +------------------------+ | v +----------------+ | Generate Report| +----------------+ | v +----------------+ | End | +----------------+算法原理解释选择设备配置:根据目标市场和用户,选择不同的设备配置进行测试。设置测试场景:准备各类测试场景,包括不同的操作系统版本、网络条件等。执行测试:在每种场景下运行应用,并记录其表现。结果分析:收集所有测试数据,对比异常结果,定位可能的问题。生成报告:整理并输出详细测试报告,为后续修正提供参考。实际详细应用代码示例实现// Sample code using JUnit for a simple compatibility test in Java import org.junit.Test; import static org.junit.Assert.*; public class CompatibilityTest { @Test public void testDeviceCompatibility() { // Simulate running an application on different device profiles String[] devices = {"Phone", "Tablet", "SmartWatch"}; boolean allCompatible = true; for (String device : devices) { // Mock function to check compatibility boolean isCompatible = checkCompatibilityOnDevice(device); if (!isCompatible) { allCompatible = false; System.out.println("Incompatibility found on: " + device); } } assertTrue("All devices should be compatible", allCompatible); } private boolean checkCompatibilityOnDevice(String device) { // This function would contain logic to actually test compatibility // For demonstration, we assume all are compatible return true; } }测试代码、部署场景测试代码:通常使用自动化测试框架如JUnit或Appium编写测试代码。部署场景:可以在本地虚拟机上测试,或使用云平台进行大规模测试。材料链接HarmonyOS 开发者官方文档JUnit 官方文档总结兼容性测试在HarmonyOS应用开发中至关重要,它帮助确保应用在多种设备和环境下的一致性与稳定性。通过相应的工具和方法,可以有效提高应用质量。未来展望随着HarmonyOS的普及,兼容性测试将需要支撑更多类型的 IoT 设备。未来可能会出现更智能的测试工具以及基于AI的分析技术,以进一步简化开发者的测试工作。
-
HarmonyOS 鸿蒙开发测试:性能测试HarmonyOS(鸿蒙操作系统)是华为开发的一款分布式操作系统,旨在为多种设备提供统一的操作系统体验。在开发和使用应用程序时,性能测试是确保应用高效稳定运行的关键步骤。性能测试介绍性能测试是指通过各种技术手段评估应用程序在特定负载下的响应时间、吞吐量、资源使用率等。它可以帮助开发者发现程序中的瓶颈,为优化提供数据支持。应用使用场景物联网设备:如智能家居设备,要求低延迟、高效率。移动设备:手机和平板需要在性能和电池寿命之间找到平衡。车载系统:需要快速响应用户和传感器输入。在实现物联网设备、移动设备和车载系统的代码示例时,我们需要分别考虑不同的性能需求和特性。以下是一些示例代码,展示如何编写针对这些设备的代码片段:1. 物联网设备:智能家居设备对于智能家居设备,我们通常使用轻量级协议(如MQTT)来确保低延迟和高效率。import paho.mqtt.client as mqtt # MQTT回调函数 def on_connect(client, userdata, flags, rc): print("Connected with result code " + str(rc)) client.subscribe("home/temperature") def on_message(client, userdata, msg): print(f"Temperature: {msg.payload.decode()} °C") client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message # 连接到MQTT Broker client.connect("mqtt.example.com", 1883, 60) # 进入循环,保持连接 client.loop_forever()2. 移动设备:平衡性能和电池寿命在移动设备上开发应用程序时,例如使用Python封装的Kivy库来开发一个简单的应用程序,以便在性能和电池寿命之间实现平衡。from kivy.app import App from kivy.uix.label import Label class BatteryFriendlyApp(App): def build(self): return Label(text='Hello, Mobile World!') if __name__ == '__main__': BatteryFriendlyApp().run()3. 车载系统:快速响应用户和传感器输入车载系统通常需要快速响应输入,这里我们使用多线程来处理用户输入和传感器数据。import threading import time def read_sensor(): while True: # 模拟读取传感器数据 sensor_data = "sensor_value" print(f"Sensor Data: {sensor_data}") time.sleep(1) # 模拟传感器读取周期 def handle_user_input(): while True: # 模拟用户输入(这里可以替换为实际输入) user_input = input("Enter command: ") print(f"User Command: {user_input}") # 分别启动两个线程以提高响应速度 sensor_thread = threading.Thread(target=read_sensor) input_thread = threading.Thread(target=handle_user_input) sensor_thread.start() input_thread.start() sensor_thread.join() input_thread.join()原理解释性能测试的核心是模拟不同的使用场景和负载,测量系统的响应能力,以识别系统的瓶颈部分。它通常包括以下几个步骤:性能需求定义:明确应用对性能的需求,比如最大响应时间。测试环境准备:搭建与实际使用环境相似的测试环境。负载设计与施加:设计合理的负载模型并施加到系统上。指标监控与记录:实时监控并记录系统的性能指标。结果分析与报告:分析测试结果,生成报告以指导优化。算法原理流程图+-----------------------------------+ | 开始性能测试 | +-----------------------------------+ | v +----------------------+ +------------------+ | 定义性能测试目标 | ----> | 准备测试环境 | +----------------------+ +------------------+ | | v v +----------------------+ +------------------+ | 设计负载模型 | ----> | 施加负载 | +----------------------+ +------------------+ | | v v +----------------------+ +------------------+ | 监控性能指标 | ----> | 分析测试结果 | +----------------------+ +------------------+ | | v v +----------------------+ +------------------+ | 生成测试报告 | | 结束性能测试 | +----------------------+ +------------------+算法原理解释该流程图描述了一个典型的性能测试过程。首先要明确具体的性能目标,根据这些目标设计测试负载,并在合适的环境中进行测试。在测试过程中,通过工具和方法监控性能指标,并在测试后分析数据,生成详细的测试报告。这些步骤帮助开发人员找出性能瓶颈,并针对性地进行优化。实际详细应用代码示例实现为了进行性能测试,可以使用一些常见的工具和库,如 JMeter、Locust 等。由于没有具体的应用场景,这里以 Python 的 Locust 为例,展示如何进行简单的 HTTP 请求性能测试。from locust import HttpUser, task, between class MyUser(HttpUser): wait_time = between(1, 2) @task def my_task(self): self.client.get("/") if __name__ == "__main__": import os os.system("locust -f <this_script_name>.py")运行上述代码后,在浏览器中访问 http://localhost:8089 可以配置并启动测试。测试代码、部署场景测试代码:根据应用的功能编写不同的测试代码,模拟真实用户行为。部署场景:选择合适的硬件及网络条件进行部署,以尽可能贴近真实环境。材料链接HarmonyOS开发文档Locust官方文档总结性能测试是确保应用在各种环境下顺利运行的重要环节。通过合理的测试计划和执行,我们可以识别出系统潜在的性能问题并加以改进,从而提升用户体验。未来展望随着物联网和边缘计算的发展,HarmonyOS 将迎来更广泛的应用,性能测试将变得更加复杂和重要。未来的发展方向包括自动化测试、智能监控,以及基于 AI 的性能预测和优化。
-
HarmonyOS 鸿蒙开发测试: 稳定性测试介绍HarmonyOS(鸿蒙)是华为自主研发的分布式操作系统,旨在无缝连接各种设备,包括智能手机、可穿戴设备、智能家居以及车载设备等。稳定性测试在鸿蒙开发中尤为重要,因为它确保系统能在不同环境下可靠运行。应用使用场景智能手机: 确保在长时间重负载条件下性能稳定。物联网设备: 确保分布式场景下多设备协同工作的稳定性。车载系统: 保证复杂运算和长时间运转的可靠性。以下是三个场景中,关于确保稳定性和可靠性的代码示例:智能手机:长时间重负载条件下性能稳定在智能手机应用中,为了确保在长时间重负载条件下的性能稳定,可以考虑使用线程池来管理并发任务,从而避免资源过度消耗。import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SmartphoneLoadBalancer { private ExecutorService executorService = Executors.newFixedThreadPool(10); public void executeHeavyTask(Runnable task) { executorService.execute(task); } public void shutdown() { executorService.shutdown(); } public static void main(String[] args) { SmartphoneLoadBalancer loadBalancer = new SmartphoneLoadBalancer(); Runnable heavyTask = () -> { System.out.println("Executing heavy task..."); // Simulate heavy computation try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed."); }; for (int i = 0; i < 20; i++) { loadBalancer.executeHeavyTask(heavyTask); } loadBalancer.shutdown(); } }物联网设备:分布式场景下多设备协同工作在物联网设备中,为了确保分布式场景下多设备协同工作的稳定性,可以使用消息队列(如MQTT)来实现设备之间的通信与协调。import paho.mqtt.client as mqtt # MQTT settings BROKER_ADDRESS = "mqtt.example.com" TOPIC = "devices/status" def on_connect(client, userdata, flags, rc): print(f"Connected with result code {rc}") client.subscribe(TOPIC) def on_message(client, userdata, msg): print(f"Message received: {msg.topic} {str(msg.payload)}") # Process message and perform coordination tasks here client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect(BROKER_ADDRESS, 1883, 60) client.loop_start() try: while True: # Main loop for device operation pass except KeyboardInterrupt: print("Exiting...") finally: client.loop_stop() client.disconnect()车载系统:保证复杂运算和长时间运转的可靠性对于车载系统,可以利用定期检查机制和容错处理来保证复杂运算和长时间运行的可靠性。#include <iostream> #include <thread> #include <chrono> void performComplexCalculation() { std::cout << "Performing complex calculation..." << std::endl; // Simulate complex computation std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "Calculation completed." << std::endl; } void faultToleranceMechanism() { std::cout << "Checking system health..." << std::endl; // Implement fault tolerance checks here } int main() { while (true) { try { performComplexCalculation(); } catch (const std::exception &e) { std::cerr << "Error occurred: " << e.what() << std::endl; // Handle exception and attempt recovery } faultToleranceMechanism(); // Simulate operation delay std::this_thread::sleep_for(std::chrono::seconds(1)); } return 0; }这些例子展示了如何通过不同的方法和技术手段来提高各类系统的稳定性和可靠性。根据具体情况,还可以进行更多调整和优化。原理解释稳定性测试主要用于识别软件在压力条件下的表现。其目标是在系统正常工作时产生模拟负载,并观察是否会出现崩溃、错误或数据丢失。对于鸿蒙OS,这尤其重要,因为它要保证跨设备的无缝体验。算法原理流程图+------------------+ | Start Stability | | Testing Process | +------------------+ | v +------------------+ | Initialize Test | | Environment | +------------------+ | v +------------------+ | Apply Load | | (Simulate Usage) | +------------------+ | v +------------------+ | Monitor System | | Performance | +------------------+ | v +------------------+ | Collect Logs | | and Metrics | +------------------+ | v +------------------+ | Analyze Results | +------------------+ | v +------------------+ | Generate Report | +------------------+算法原理解释初始化测试环境: 设置所需的软件和硬件配置,确保测试环境接近真实应用场景。施加负载: 使用自动化工具模拟用户交互,如多任务处理、网络请求等。监控系统性能: 收集CPU、内存、网络I/O等指标。收集日志和指标: 捕获在测试过程中生成的所有日志和数据。结果分析: 检查系统行为以确定是否有异常。生成报告: 总结测试发现,提供改进建议。实际详细应用代码示例实现#include <iostream> #include <thread> #include <vector> // A function to simulate heavy computation. void performHeavyComputation(int threadId) { std::cout << "Thread " << threadId << " starting computation.\n"; // Simulate a heavy computational task volatile double result = 0; for (int i = 0; i < 100000000; ++i) { result += sin(i) * cos(i); } std::cout << "Thread " << threadId << " finished with result " << result << ".\n"; } int main() { const int numThreads = 8; std::vector<std::thread> threads; // Launch multiple threads to simulate load for (int i = 0; i < numThreads; ++i) { threads.emplace_back(performHeavyComputation, i); } // Join the threads with the main thread for (auto& th : threads) { th.join(); } std::cout << "All computations are done.\n"; return 0; }测试代码可以使用上述代码进行简单的多线程重负载测试,观察在不同设备上的表现。部署场景实验室环境: 使用专用硬件进行最大负载测试。云环境: 在虚拟机上进行分布式测试,以模拟真实世界使用情况。本地设备: 在实际用户设备上进行长期运行测试,以检查电池消耗和热管理。材料链接HarmonyOS DocumentationC++ Multithreading Guide总结稳定性测试是鸿蒙OS开发的重要环节,它不仅帮助发现潜在的问题,还能提高用户体验。在对复杂且跨平台的系统进行高负载测试时,合适的算法和工具是必不可少的。未来展望随着鸿蒙OS生态系统的发展,稳定性测试将更加注重AI驱动的测试方法,以便自动适应更多种类的设备和复杂的使用场景。长远来看,这将大幅降低故障率并优化系统资源的利用。
上滑加载中
推荐直播
-
DTT年度收官盛典:华为开发者空间大咖汇,共探云端开发创新
2025/01/08 周三 16:30-18:00
Yawei 华为云开发工具和效率首席专家 Edwin 华为开发者空间产品总监
数字化转型进程持续加速,驱动着技术革新发展,华为开发者空间如何巧妙整合鸿蒙、昇腾、鲲鹏等核心资源,打破平台间的壁垒,实现跨平台协同?在科技迅猛发展的今天,开发者们如何迅速把握机遇,实现高效、创新的技术突破?DTT 年度收官盛典,将与大家共同探索华为开发者空间的创新奥秘。
去报名 -
GaussDB应用实战:手把手带你写SQL
2025/01/09 周四 16:00-18:00
Steven 华为云学堂技术讲师
本期直播将围绕数据库中常用的数据类型、数据库对象、系统函数及操作符等内容展开介绍,帮助初学者掌握SQL入门级的基础语法。同时在线手把手教你写好SQL。
去报名
热门标签