“答开发者问”之HarmonyOS 鸿蒙Next技术问题解析 第16期
“答开发者问”之HarmonyOS 鸿蒙Next技术问题解析 第16期
本期问题如下(解决方案见评论区):
- 如何监听弱网情况事件?
- 如何在entryability里实现一个全局监听器?
- 如何实现LED效果的弹幕滚动?
- HarmonyOS应用未上架,如何调试检测应用更新功能是否正常?
- ListItem怎么设置只能一侧滑动?
向所有参与社区互助的开发者致以最诚挚的感谢!
特别感谢本期优质答复贡献者: @哈莫尼OS
社区的蓬勃发展,离不开每一位积极参与者的贡献。本期“答开发者问”栏目,精选自广大热心开发者针对提问帖所贡献的众多优质答复之中。它们不仅是智慧与经验的璀璨结晶,更是“众人拾柴火焰高”这一真理的生动体现。在此,我们由衷地感谢每一位热心参与、乐于分享的开发者,是你们的热情与智慧,让这个社区充满了生机与活力,每一次的解答都是对技术探索精神的最好诠释。同时,我们也诚挚邀请更多的开发者加入到这场智慧碰撞的盛宴中来。无论是抛出难题寻求解答,还是慷慨解囊分享经验,您的每一份参与都将为鸿蒙开发者社区注入新的活力,推动我们共同前行,在技术的海洋中扬帆远航。
答开发者问系列汇总:
往期问题回顾:
“答开发者问”之HarmonyOS技术问题解析 第1期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第2期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第3期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第4期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第5期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第6期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第7期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第8期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第9期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第10期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第11期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第12期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第13期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第14期-华为开发者问答 | 华为开发者联盟 (huawei.com)
“答开发者问”之HarmonyOS技术问题解析 第15期-华为开发者问答 | 华为开发者联盟 (huawei.com)
注意:
开发者小伙伴们,规范提问,高效沟通!更快得到问题答案的秘诀来啦,点击链接直达
更多关于“答开发者问”之HarmonyOS 鸿蒙Next技术问题解析 第16期的实战教程也可以访问 https://www.itying.com/category-93-b0.html
问题五:ListItem怎么设置只能一侧滑动?
如何实现ListItem只能从右向左滑,禁止从左向右滑。示例代码如下:
// xxx.ets
@Entry
@Component
struct ListItemExample2 {
@State arr: number[] = [0, 1, 2, 3, 4];
@State enterEndDeleteAreaString: string = 'not enterEndDeleteArea';
@State exitEndDeleteAreaString: string = 'not exitEndDeleteArea';
private scroller: ListScroller = new ListScroller();
@Builder itemEnd() {
Row() {
Button('Delete').margin('4vp')
Button('Set').margin('4vp').onClick(() => {
this.scroller.closeAllSwipeActions();
})
}.padding('4vp').justifyContent(FlexAlign.SpaceEvenly)
}
build() {
Column() {
List({ space: 10, scroller: this.scroller }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('item' + item)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
.transition({ type: TransitionType.Delete, opacity: 0 })
.swipeAction({
end: {
builder: () => { this.itemEnd() }
}
})
}, (item: number) => item.toString())
}
Text(this.enterEndDeleteAreaString).fontSize(20)
Text(this.exitEndDeleteAreaString).fontSize(20)
}
.padding(10)
.backgroundColor(0xDCDCDC)
.width('100%')
.height('100%')
}
}
解决方案:
通过SwipeActionOption限制滑动方向,将不需要滑动方向设为null
,并配置edgeEffect。
ListItem() {
// 主内容区域
}
.swipeAction({
// 只设置end参数实现右侧滑动
end: () => {
Button('删除').onClick(() => {})
},
start: null, // 禁止左侧滑动
edgeEffect: SwipeEdgeEffect.None
})
原链接:
ListItem怎么设置只能一边滑动-华为开发者问答 | 华为开发者联盟 (huawei.com)
更多关于“答开发者问”之HarmonyOS 鸿蒙Next技术问题解析 第16期的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
问题四:HarmonyOS应用未上架 ,如何调试检测应用更新功能是否正常?
checkAppUpdate这个API在APP未上架时是否可以生效调用?
解决方案:
checkAppUpdate这个API在APP未上架时可以生效调用。 应用市场更新功能为开发者提供版本检测、显示更新提醒功能。开发者使用应用市场更新功能可以提醒用户及时更新到最新版本。
接口说明:
checkAppUpdate(context: common.UIAbilityContext): Promise<checkupdateresult>
。检查更新接口,用于检测当前是否有新版本。showUpdateDialog(context:common.UIAbilityContext): Promise<showupdateresultcode>
。显示升级对话框接口,用于提示用户进行升级。- 参考链接:应用市场更新接口API
开发步骤:
开发者可以在应用启动或者设置内手动触发检查版本更新,并显示升级弹窗,点击升级跳转应用市场进行升级。 注意: 假设某应用在应用市场的版本是100001,开发者可以将本地调试代码中app.json5文件里的versionCode改为100000,然后可以调试版本更新检查业务。
检测新版本和升级弹窗代码如下:
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { updateManager } from '@kit.AppGalleryKit';
const TAG = 'checkAppUpdate';
@Entry
@Component
struct Index {
@State message: string = 'check';
@State @Watch('showUpdateDialog') updateAvailableResult: number = 0; // 默认为0,1表示存在新版本
context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
checkAppUpdate() {
try {
updateManager.checkAppUpdate(this.context).then((checkResult: updateManager.CheckUpdateResult) => {
this.updateAvailableResult = checkResult.updateAvailable.valueOf();
hilog.info(0, TAG, `Succeeded in checking Result updateAvailable: ${checkResult.updateAvailable}`);
}).catch((error: BusinessError) => {
hilog.error(0, TAG, `checkAppUpdate onError.code is ${error.code}, message is ${error.message}`);
});
} catch (error) {
hilog.error(0, TAG, `checkAppUpdate onError.code is ${error.code}, message is ${error.message}`);
}
}
showUpdateDialog() {
try {
updateManager.showUpdateDialog(this.context).then((resultCode: updateManager.ShowUpdateResultCode) => {
hilog.info(0, TAG, `Succeeded in showing UpdateDialog resultCode: ${resultCode}`);
}).catch((error: BusinessError) => {
hilog.error(0, TAG, `showUpdateDialog onError.code is ${error.code}, message is ${error.message}`);
});
} catch (error) {
hilog.error(0, TAG, `showUpdateDialog onError.code is ${error.code}, message is ${error.message}`);
}
}
build() {
Column() {
Text(this.message)
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.margin(50)
.onClick(() => {
this.checkAppUpdate();
})
}
.height('100%')
.width('100%')
}
}
原链接:
HarmonyOS 应用未上架 ,如何调试检测应用更新功能是否正常-华为开发者问答 | 华为开发者联盟 (huawei.com)
赞赞赞
问题三:如何实现LED效果的弹幕滚动?
如何实现下图中的效果,需要滚动?
解决方案:
通过Marquee实现滚动播放的效果,Canvas组件画出一个LED效果的遮罩,具体步骤如下:
-
计算文本是否超过Marquee的宽度,如果没有超过,用空格补齐,以保证正常滚动,示例代码如下:
CompleteTextByTextWidth(src: string, fontSize: number, fontWeight: number) { let displayInfo = display.getDefaultDisplaySync() let displayWidth = displayInfo.width let TextWidth = this.getUIContext() .getMeasureUtils() .measureText({ textContent: src, fontSize: fontSize, fontWeight: fontWeight }) while (TextWidth <= displayWidth) { src += ' ' TextWidth = this.getUIContext() .getMeasureUtils() .measureText({ textContent: src, fontSize: fontSize, fontWeight: fontWeight }) } return src }
-
使用Marquee实现滚动显示,代码示例如下:
Marquee({ start: this.start, step: this.step, fromStart: this.fromStart, src: this.danMuSrc }) .width(this.danMuScreenWidth) .height(this.danMuScreenHeigth) .fontColor('#ffc60f0f') .fontSize(48) .fontWeight(700) .backgroundColor('#182431')
-
通过Canvas绘制网格遮罩,实现LED点阵屏的效果,代码示例如下:
// LED点阵屏模拟遮罩 Canvas(this.canvasContext) .width(this.danMuScreenWidth) .height(this.danMuScreenHeigth) .onReady(() => { this.canvasContext.beginPath(); this.canvasContext.lineWidth = 0.55; let lineCountW = this.danMuScreenWidth / this.spaceWidth let lineCountH = this.danMuScreenHeigth / this.spaceWidth // 相等间隔画出横线 for (let index = 0; index <= lineCountH; index++) { let y = index === lineCountH ? (this.danMuScreenHeigth) : this.spaceWidth * index; this.canvasContext.moveTo(0, y); this.canvasContext.lineTo(this.danMuScreenWidth, y); } // 相等间隔画出竖线 for (let index = 0; index <= lineCountW; index++) { let x = index === lineCountW ? (this.danMuScreenWidth) : this.spaceWidth * index; this.canvasContext.moveTo(x, 0); this.canvasContext.lineTo(x, this.danMuScreenHeigth); } this.canvasContext.stroke(); })
完整代码示例如下:
import { display } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State start: boolean = false
@State danMuSrc: string = ""
context = this.getUIContext().getHostContext() as Context
private fromStart: boolean = true
private step: number = 10
private danMuScreenWidth: number = this.getUIContext().px2vp(display.getDefaultDisplaySync().width)
private danMuScreenHeigth: number = 200
private spaceWidth: number = 3
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
aboutToAppear(): void {
this.danMuSrc = this.CompleteTextByTextWidth(this.danMuSrc, 48, 700)
}
CompleteTextByTextWidth(src: string, fontSize: number, fontWeight: number) {
let displayInfo = display.getDefaultDisplaySync()
let displayWidth = displayInfo.width
let TextWidth = this.getUIContext()
.getMeasureUtils()
.measureText({ textContent: src, fontSize: fontSize, fontWeight: fontWeight })
while (TextWidth <= displayWidth) {
src += ' '
TextWidth = this.getUIContext()
.getMeasureUtils()
.measureText({ textContent: src, fontSize: fontSize, fontWeight: fontWeight })
}
return src
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Stack() {
Marquee({
start: this.start,
step: this.step,
fromStart: this.fromStart,
src: this.danMuSrc
})
.width(this.danMuScreenWidth)
.height(this.danMuScreenHeigth)
.fontColor('#ffc60f0f')
.fontSize(48)
.fontWeight(700)
.backgroundColor('#182431')
// LED点阵屏模拟遮罩
Canvas(this.canvasContext)
.width(this.danMuScreenWidth)
.height(this.danMuScreenHeigth)
.onReady(() => {
this.canvasContext.beginPath();
this.canvasContext.lineWidth = 0.55;
let lineCountW = this.danMuScreenWidth / this.spaceWidth
let lineCountH = this.danMuScreenHeigth / this.spaceWidth
// 相等间隔画出横线
for (let index = 0; index <= lineCountH; index++) {
let y = index === lineCountH ? (this.danMuScreenHeigth) : this.spaceWidth * index;
this.canvasContext.moveTo(0, y);
this.canvasContext.lineTo(this.danMuScreenWidth, y);
}
// 相等间隔画出竖线
for (let index = 0; index <= lineCountW; index++) {
let x = index === lineCountW ? (this.danMuScreenWidth) : this.spaceWidth * index;
this.canvasContext.moveTo(x, 0);
this.canvasContext.lineTo(x, this.danMuScreenHeigth);
}
this.canvasContext.stroke();
})
}
TextInput({ placeholder: '输入弹幕' })
.type(InputType.USER_NAME)
.placeholderColor(0x182431)
.showPasswordIcon(false)
.placeholderFont({ size: 16, weight: FontWeight.Regular })
.opacity(0.6)
.width('90%')
.margin({ top: 20 })
.hitTestBehavior(HitTestMode.Default)
.onChange((value: string) => {
this.danMuSrc = this.CompleteTextByTextWidth(value, 48, 700);
})
Button('Start')
.onClick(() => {
this.start = true
})
.width(120)
.height(40)
.fontSize(16)
.margin({ top: 20 })
.fontWeight(500)
.backgroundColor('#007DFF')
}
.width('100%')
.height('100%')
}
}
原链接:
如何实现LED效果的弹幕滚动现实-华为开发者问答 | 华为开发者联盟 (huawei.com)
如何在entryability里实现一个全局监听器?
如何在app里面自定义事件并监听?
解决方案:
定义全局事件管理器类:
import { hilog } from '@kit.PerformanceAnalysisKit';
// common/EventManager.ets
export class EventManager {
private static instance: EventManager;
private listeners: Map<string, Function[]> = new Map();
static getInstance(): EventManager {
if (!EventManager.instance) {
EventManager.instance = new EventManager();
}
return EventManager.instance;
}
// 注册事件监听
on(eventName: string, callback: Function): void {
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, []);
}
this.listeners.get(eventName)?.push(callback);
}
// 触发事件
emit(eventName: string, ...args: string[]): void {
const callbacks = this.listeners.get(eventName);
callbacks?.forEach(callback => {
try {
callback(...args);
} catch (error) {
hilog.error(0x0000, 'EventManager', `Event callback error: ${error}`);
}
});
}
// 移除监听
off(eventName: string, callback: Function): void {
const callbacks = this.listeners.get(eventName);
if (callbacks) {
this.listeners.set(eventName, callbacks.filter(cb => cb !== callback));
}
}
}
定义GlobalContext类:
// common/GlobalContext.ets
export class GlobalContext {
private static instance: GlobalContext;
private _objects: Map<string, Object>;
private constructor() {
this._objects = new Map<string, Object | string>();
}
public static getContext(): GlobalContext {
if (!GlobalContext.instance) {
GlobalContext.instance = new GlobalContext();
}
return GlobalContext.instance;
}
getObject(key: string): Object | undefined {
return this._objects.get(key);
}
setObject(key: string, objectClass: Object): void {
this._objects.set(key, objectClass);
}
deleteObject(key: string): void {
this._objects.delete(key);
}
}
初始化并挂载到全局上下文:
// entryability/EntryAbility.ets
import { GlobalContext } from '../common/GlobalContext';
import { EventManager } from '../common/EventManager';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 初始化全局事件管理器
GlobalContext.getContext().setObject('eventManager', EventManager.getInstance());
}
}
使用事件:
const eventManager = GlobalContext.getContext().getObject('eventManager') as EventManager;
// 注册监听器
this.eventManager.on('customEvent', (data: string) => {
hilog.info(0x0000, 'testTag', `Received event data: ${JSON.stringify(data)}`);
});
// 触发事件
eventManager.emit('customEvent', '改变值');
问题一:如何监听弱网情况事件?
请问on(‘netUnavailable’)是弱网情况的监听订阅事件吗?代码如下:
netConn.on('netUnavailable', () => {
let netisuseful: SubscribedAbstractProperty<boolean> = AppStorage.link("Isusenet")
if (netisuseful.get()) {
AppStorage.set('Isusenet', false)
}
windowStage.loadContent('pages/NetWrong', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
windowStage.getMainWindow((err, data) => {
if (err.code) {
console.error('Failed to obtain the main window.')
return;
}
// 获取到窗口对象
// GlobalThisUtil.setProperty("windowClass",data)
this.windowFull = data;
})
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
console.info('网络不可用hhl')
});
解决方案:
netUnavailable
事件属于网络状态监听中的网络不可用,而不是弱网监控。弱网监听可以通过netCapabilitiesChange
事件监听,结合带宽、延迟等参数判断。具体实现如下:
在module.json5
中添加权限:
"requestPermissions": [
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
let netConn = connection.createNetConnection();
// 先使用register接口注册网络状态变化事件。
netConn.register((error: BusinessError) => {
console.error(JSON.stringify(error));
});
// 订阅网络能力变化事件(含弱网)
netConn.on('netCapabilitiesChange', (data) => {
let currentKps: number = data.netCap?.linkDownBandwidthKbps as number
// 带宽低于 500kbps 视为弱网
if (currentKps < 500) {
// 执行弱网处理逻辑
}
});
原链接:
请问这个是弱网情况的监听订阅吗netUnavailable-华为开发者问答 | 华为开发者联盟 (huawei.com)
HarmonyOS Next是鸿蒙的独立演进版本,采用全新架构,不再兼容安卓APK。其核心技术特点包括:
- 原生鸿蒙应用使用ArkTS/ArkUI开发;
- 分布式能力升级,支持跨设备原子化服务;
- 全新声明式UI开发范式;
- 高性能方舟运行时。
开发者需注意:Next版本SDK与API存在不兼容变更,需使用DevEco Studio 4.0以上版本进行适配开发。当前重点优化了原生鸿蒙应用的启动性能、内存管理和多端协同能力。
-
监听弱网情况:使用@ohos.net.connection模块的on()方法监听networkStateChange事件,获取当前网络状态和信号强度,通过RTT或丢包率判断弱网。
-
EntryAbility全局监听器:在EntryAbility的onCreate()中初始化EventHub实例,通过emit()和on()方法实现跨Ability事件通信,注意在onDestroy()中释放资源。
-
LED弹幕效果:使用Text组件配合marquee属性实现滚动,通过Stack布局叠加多个Text,设置不同颜色和偏移量模拟LED点阵效果,使用动画控制器调节滚动速度。
-
未上架应用调试更新:使用AppGallery Connect的App Testing服务分发测试版本,或通过hdc命令手动安装新旧版本进行验证,检查版本号是否正常更新。
-
ListItem单侧滑动:设置SwipeAction的direction属性为Left或Right,另一侧留空,通过edgeEffect控制滑动边界效果。