HarmonyOS鸿蒙Next中Navigation跳转到下一页是空白的
HarmonyOS鸿蒙Next中Navigation跳转到下一页是空白的
import { image } from '[@kit](/user/kit).ImageKit';
import { fileIo, } from '[@kit](/user/kit).CoreFileKit';
import { photoAccessHelper } from '[@kit](/user/kit).MediaLibraryKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { textRecognition } from '[@kit](/user/kit).CoreVisionKit';
import { common } from '[@kit](/user/kit).AbilityKit';
import { PromptAction } from '[@kit](/user/kit).ArkUI';
import router from '[@ohos](/user/ohos).router';
// import { GlobalContext } from '../common/GlobalContext';
const TAG: string = 'ImageTextRecognizer';
@Entry
@Component
struct ImageTextRecognizerPage {
@State chooseImage: PixelMap | undefined = undefined
@State dataValues: string = ''
@State isLoading: boolean = false
@State hasResult: boolean = false
@State isTimeout: boolean = false;
private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
private promptAction: PromptAction = this.getUIContext().getPromptAction() as PromptAction;
private recognizeTimer: number | null = null;
private pageInfos: NavPathStack = new NavPathStack();
openPicture(): Promise<string> {
return new Promise<string>((resolve) => {
let photoPicker: photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker();
photoPicker.select({
MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1
}).then((res: photoAccessHelper.PhotoSelectResult) => {
resolve(res.photoUris[0]);
}).catch((err: BusinessError) => {
resolve('');
})
})
}
async selectImage() {
this.hasResult = false;
let uri = await this.openPicture();
console.warn(TAG, `图片是:${JSON.stringify(uri)}`)
if (!uri) {
return;
}
setTimeout(
async () => {
let imageSource: image.ImageSource | undefined = undefined;
try {
let fileSource = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
imageSource = image.createImageSource(fileSource.fd);
this.chooseImage = await imageSource.createPixelMap();
} catch (error) {
console.error(TAG, 'Failed to load image: ' + JSON.stringify(error));
}
}, 100)
}
async textRecogn() {
if (!this.chooseImage) {
this.dataValues = '请先选择图片';
return;
}
// 重置状态
this.isLoading = true;
this.hasResult = false;
this.isTimeout = false;
this.dataValues = '';
// 清除可能存在的旧计时器
if (this.recognizeTimer) {
clearTimeout(this.recognizeTimer);
}
// 启动5秒超时计时器
this.recognizeTimer = setTimeout(() => {
if (this.isLoading) {
this.isTimeout = true;
this.isLoading = false;
this.hasResult = true;
this.dataValues = '识别超时(超过5秒),请重试';
this.promptAction.showToast({ message: '识别超时' });
}
}, 5000);
let visionInfo: textRecognition.VisionInfo = {
pixelMap: this.chooseImage
};
let textConfiguration: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: false
};
try {
// 执行文字识别
const data = await textRecognition.recognizeText(visionInfo, textConfiguration);
// 识别成功:清除计时器,更新状态
if (this.recognizeTimer) {
clearTimeout(this.recognizeTimer);
}
this.dataValues = data.value;
this.hasResult = true;
this.isLoading = false;
} catch (error) {
// 识别失败:清除计时器,更新状态
if (this.recognizeTimer) {
clearTimeout(this.recognizeTimer);
}
// 排除超时导致的失败(已在计时器中处理)
if (!this.isTimeout) {
this.dataValues = `识别失败: ${error.message}`;
this.hasResult = true;
this.isLoading = false;
}
}
}
// 跳转到预览页面
navigateToPreview() {
if (!this.chooseImage) {
return;
}
// // 保存图片到全局上下文
// GlobalContext.getInstance().pixelMap = this.chooseImage;
// 1. 定义跳转参数(直接封装pixelMap,无需GlobalContext)
this.pageInfos.pushPath({
name: 'PreviewPage', param: this.chooseImage, onPop: (popInfo: PopInfo) => {
console.warn(TAG, `page is: ${popInfo.info.name},result: ${JSON.stringify(popInfo.result)}`)
}
}); // 将name
}
aboutToDisappear() {
// 页面销毁时清除计时器
if (this.recognizeTimer) {
clearTimeout(this.recognizeTimer);
this.recognizeTimer = null;
}
}
build() {
// 标题区域
Navigation(this.pageInfos) {
Column() {
Text('图片文字识别')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 })
.fontColor('#1272FF')
// 图片显示区域
if (this.chooseImage) {
Stack() {
Image(this.chooseImage)
.objectFit(ImageFit.Contain)
.width('90%')
.height(240)
.borderRadius(12)
.backgroundColor('#F5F5F5')
.shadow({
radius: 8,
color: '#1A000000',
offsetX: 2,
offsetY: 4
})
.sharedTransition('imageTransition', { duration: 400, curve: Curve.EaseOut })
.onClick(() => {
this.navigateToPreview();
})
if (this.isLoading) {
Column() {
LoadingProgress()
.width(40)
.height(40)
.color('#1272FF')
Text('识别中...')
.fontSize(14)
.margin({ top: 8 })
.fontColor('#666666')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#CCFFFFFF')
.borderRadius(12)
}
}
.width('90%')
.height(240)
.margin({ bottom: 20 })
} else {
// 占位图
Column() {
Image($r('app.media.bg_fish'))
.width(60)
.height(60)
.fillColor('#CCCCCC')
Text('请选择图片')
.fontSize(16)
.margin({ top: 12 })
.fontColor('#999999')
}
.width('90%')
.height(240)
.justifyContent(FlexAlign.Center)
.border({ width: 2, color: '#E5E5E5', style: BorderStyle.Dashed })
.borderRadius(12)
.backgroundColor('#FAFAFA')
.margin({ bottom: 20 })
}
}
// 识别结果区域
if (this.hasResult || this.dataValues) {
Column() {
Row() {
Text('识别结果')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
if (this.dataValues && !this.dataValues.startsWith('识别失败') &&
!this.dataValues.startsWith('请先选择')) {
Text(`字数: ${this.dataValues.length}`)
.fontSize(12)
.fontColor('#666666')
.margin({ left: 12 })
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
.margin({ bottom: 8 })
Scroll() {
Text(this.dataValues)
.fontSize(16)
.fontColor('#444444')
.textAlign(TextAlign.Start)
.lineHeight(24)
.width('100%')
.copyOption(CopyOptions.LocalDevice)
}
.width('100%')
.height(180)
.padding(12)
.borderRadius(8)
.backgroundColor('#F8F9FA')
.border({ width: 1, color: '#E8E8E8' })
}
.width('90%')
.margin({ bottom: 20 })
}
// 按钮区域
Column() {
Button('选择图片')
.type(ButtonType.Normal)
.fontColor(Color.White)
.fontSize(16)
.backgroundColor('#1272FF')
.width('100%')
.height(48)
.margin({ bottom: 12 })
.borderRadius(24)
.onClick(() => {
this.selectImage()
})
Button('文字识别')
.type(ButtonType.Normal)
.fontColor(Color.White)
.fontSize(16)
.backgroundColor(this.chooseImage ? '#1272FF' : '#CCCCCC')
.width('100%')
.height(48)
.borderRadius(24)
.enabled(!!this.chooseImage)
.onClick(async () => {
this.textRecogn()
})
}
.width('90%')
.margin({ bottom: 30 })
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
.backgroundColor('#FFFFFF')
}
.width('100%')
.height('100%')
// .title('图片文字识别')
}
}
// pages/PreviewPage.ets
import { image } from '[@kit](/user/kit).ImageKit';
import { fileUri } from '[@kit](/user/kit).CoreFileKit';
import { photoAccessHelper } from '[@kit](/user/kit).MediaLibraryKit';
import { systemShare } from '[@kit](/user/kit).ShareKit';
import { uniformTypeDescriptor as utd } from '[@kit](/user/kit).ArkData';
import { common } from '[@kit](/user/kit).AbilityKit';
import { display, PromptAction } from '[@kit](/user/kit).ArkUI';
import fs from '[@ohos](/user/ohos).file.fs';
// import { GlobalContext } from '../common/GlobalContext';
import { deviceInfo } from '[@kit](/user/kit).BasicServicesKit';
const TAG: string = 'ImageTextRecognizer';
interface ImageSizeType {
width: number
height: number
scale: number
offsetX: number
offsetY: number
offsetStartX: number
offsetStartY: number
dragOffsetX: number
dragOffsetY: number
}
//用于挖孔区避让
interface StatusBarMargin {
left: number;
right: number;
top: number;
bottom: number;
}
[@Builder](/user/Builder)
export function PreviewPageBuilder() {
PreviewPage()
}
[@ComponentV2](/user/ComponentV2)
export struct PreviewPage {
[@Local](/user/Local) statusBarMargin: StatusBarMargin = {
left: 0,
right: 0,
top: 0,
bottom: 0
};
[@Local](/user/Local) pixelMap: PixelMap | undefined = undefined;
[@Local](/user/Local) containerHeight: number = 0
[@Local](/user/Local) containerWidth: number = 0
[@Local](/user/Local) disabledSwipe: boolean = false
[@Local](/user/Local) isScaled: boolean = false // 标记图片是否处于放大状态
[@Local](/user/Local) activeImage: ImageSizeType = {
width: 0,
height: 0,
scale: 1,
offsetX: 0,
offsetY: 0,
offsetStartX: 0,
offsetStartY: 0,
dragOffsetX: 0,
dragOffsetY: 0,
}
[@Local](/user/Local) rotationAngle: number = 0;
private defaultScale: number = 1
private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
private promptAction: PromptAction = this.getUIContext().getPromptAction() as PromptAction;
private uiContext = this.getUIContext() as UIContext;
private deviceType: string = '';
private pathStack: NavPathStack = new NavPathStack();
aboutToAppear() {
// // 从全局上下文获取图片
// this.pixelMap = GlobalContext.getInstance().pixelMap;
// 设置沉浸式
this.setImmersive();
//this.registerOrientationListener();
this.getDeviceType();
//计算挖孔区边距
this.context.windowStage?.getMainWindow().then(async (win) => {
const properties = win.getWindowProperties();
const windowWidth = this.uiContext.px2vp(properties.windowRect.width);
const windowHeight = this.uiContext.px2vp(properties.windowRect.height);
await this.calculateStatusBarMargin(windowWidth, windowHeight);
});
}
// 处理物理返回键
onBackPress() {
this.goBack();
return true; // 阻止默认返回行为
}
// 重置图片状态
resetImageState() {
this.getUIContext().animateTo({
duration: 300,
curve: Curve.EaseOut,
onFinish: () => {
this.disabledSwipe = false;
this.isScaled = false;
}
}, () => {
this.activeImage = {
width: this.activeImage.width,
height: this.activeImage.height,
scale: 1,
offsetX: 0,
offsetY: 0,
offsetStartX: 0,
offsetStartY: 0,
dragOffsetX: 0,
dragOffsetY: 0,
};
});
}
// 保存图片到相册
async saveImage(): Promise<void> {
if (!this.pixelMap) {
return; // 确保有图片
}
try {
// 1. 将 PixelMap 转为 ArrayBuffer
const packer = image.createImagePacker();
const buffer = await packer.packToData(this.pixelMap, { format: 'image/jpeg', quality: 90 });
// 2. 保存到相册
const helper = photoAccessHelper.getPhotoAccessHelper(this.context);
const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
await fs.write(file.fd, buffer);
await fs.close(file.fd);
this.promptAction.showToast({ message: "图片保存成功" });
console.warn(`${TAG} 图片保存成功`);
} catch (error) {
this.promptAction.showToast({ message: "图片保存失败" });
console.error(`${TAG} 保存失败: ${JSON.stringify(error)}`);
}
}
// 分享图片
async shareImage() {
if (!this.pixelMap) {
this.promptAction.showToast({ message: "无图片可分享" });
return;
}
try {
// 1. 定义临时文件路径
const tempPath = `${this.context.filesDir}/temp_share.jpg`;
// 2. 将 PixelMap 转为 buffer 并写入临时文件
const packer = image.createImagePacker();
const buffer = await packer.packToData(this.pixelMap, { format: 'image/jpeg', quality: 90 });
// 3. 同步打开文件
const file = fs.openSync(tempPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
await fs.write(file.fd, buffer);
await fs.close(file.fd);
// 4. 检查文件是否存在
if (!fs.accessSync(tempPath)) {
console.error('临时图片文件不存在');
this.promptAction.showToast({ message: "分享失败:文件不存在" });
return;
}
// 5. 调用系统分享
const imageUtd = utd.getUniformDataTypeByFilenameExtension('.jpg', utd.UniformDataType.IMAGE);
const shareData = new systemShare.SharedData({
utd: imageUtd,
uri: fileUri.getUriFromPath(tempPath),
title: '分享图片',
description: '识别后的图片'
});
const controller = new systemShare.ShareController(shareData);
await controller.show(this.context, {
selectionMode: systemShare.SelectionMode.SINGLE,
previewMode: systemShare.SharePreviewMode.DETAIL
});
this.promptAction.showToast({ message: "分享成功" });
} catch (error) {
console.error(`${TAG} 分享失败: ${JSON.stringify(error)}`);
this.promptAction.showToast({ message: "分享失败" });
}
}
build() {
NavDestination() {
Stack() {
// 图片区域 - 支持手势操作
if (this.pixelMap) {
// 顶部返回键(固定在左上角,放大时隐藏)
Row() {
Image($rawfile('back.png'))
.width(40)
.height(40)
.onClick(() => {
this.goBack();
})
}
.position({
x: this.statusBarMargin.left,
y: this.statusBarMargin.top
})
.visibility(this.isScaled ? Visibility.Hidden : Visibility.Visible) // 放大时隐藏
.zIndex(1)
// 图片预览区域
Image(this.pixelMap)
.objectFit(ImageFit.Contain)
.width('100%')
.height('100%')
.sharedTransition('imageTransition', { duration: 400, curve: Curve.EaseOut })
.scale({ x: this.activeImage.scale, y: this.activeImage.scale })
.offset({ x: this.activeImage.offsetX, y: this.activeImage.offsetY })
.rotate({
angle: this.rotationAngle,
centerX: '50%',
centerY: '50%'
})
.draggable(false)
.onAreaChange((_, newValue) => {
this.containerWidth = Number(newValue.width);
this.containerHeight = Number(newValue.height);
})
更多关于HarmonyOS鸿蒙Next中Navigation跳转到下一页是空白的的实战教程也可以访问 https://www.itying.com/category-93-b0.html
请在mainpage添加需要跳转的页面
参考文档:Router切换Navigation-设置组件导航和页面路由-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者 (huawei.com)
更多关于HarmonyOS鸿蒙Next中Navigation跳转到下一页是空白的的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
空白应该没有在route_map.json中添加路由信息,导航不到真正要去的页面。
你是不是。。。在你第二个页面里没取你传过来的参数,然后你的组件就没构建,因为if的关系。
.onReady((ctx: NavDestinationContext) => {
this.pathStack = ctx.pathStack
this.pixelMap = ctx.pathInfo.param as image.PixelMap
});
我没有加导航页,光用子页了,,
能用 this.pathStack.getParamByName 或者 this.pathStack.getParamByIndex 获取参数吗,该咋写呢,
let fileName: string[] = this.pathStack.getParamByName('ReadPage') as string[]
// 跳转时传递参数(参考检索信息)
this.pathStack.pushPathByName('pageOne', { id: 123, title: "示例" });
// 目标页面获取参数
let params: any[] = this.pathStack.getParamByName('pageOne');
console.log(params.id); // 输出:123,
在HarmonyOS Next中,Navigation跳转后页面空白通常由以下原因导致:
- 页面组件未正确构建或加载。
- Navigation组件配置错误,如目标页面路径不正确。
- 页面生命周期函数(如aboutToAppear)中未正确初始化数据或UI。
- 使用了不兼容的API或组件。
检查目标页面的构建逻辑和Navigation的路径配置。
在HarmonyOS Next中,Navigation跳转后页面空白通常由以下几个原因导致:
主要问题分析
1. 页面注册问题
从你的routerMap配置看,PreviewPage使用了@Builder构建器,但注册方式可能存在问题。在HarmonyOS Next中,页面注册需要确保:
// 正确的页面注册方式
@Entry
@Component
struct PreviewPage {
// ...
}
// 或者在routerMap中直接使用组件名
2. 参数传递问题
你的跳转代码中传递了PixelMap参数:
this.pageInfos.pushPath({
name: 'PreviewPage',
param: this.chooseImage, // 直接传递PixelMap对象
onPop: ...
});
PixelMap是复杂对象,直接通过Navigation参数传递可能导致序列化问题。建议使用以下方式:
3. 页面生命周期问题
PreviewPage中缺少必要的生命周期方法初始化数据:
aboutToAppear() {
// 需要从Navigation参数中获取传递的数据
const params = this.getNavDestinationContext()?.getParams();
if (params) {
this.pixelMap = params as PixelMap;
}
}
解决方案
方案一:修复页面注册
确保PreviewPage使用正确的装饰器:
// PreviewPage.ets
@Entry
@Component
export struct PreviewPage {
// 移除 @ComponentV2 和 @Builder
// ...
}
方案二:修改参数传递方式
避免直接传递PixelMap:
// 在主页面
navigateToPreview() {
// 使用全局状态管理或临时存储
const imageId = this.saveImageToTemp(this.chooseImage);
this.pageInfos.pushPath({
name: 'PreviewPage',
param: { imageId: imageId },
onPop: ...
});
}
// 在PreviewPage中
aboutToAppear() {
const params = this.getNavDestinationContext()?.getParams();
if (params?.imageId) {
this.pixelMap = this.loadImageFromTemp(params.imageId);
}
}
方案三:检查页面结构
确保PreviewPage的build方法正确包裹:
build() {
// 必须有根容器
Column() {
// 页面内容
}
.width('100%')
.height('100%')
}
方案四:调试建议
- 在
aboutToAppear中添加日志,确认页面是否被加载 - 检查控制台是否有错误信息
- 尝试先传递简单参数(如字符串)测试跳转是否正常
- 确保routerMap配置路径正确
关键检查点
- 页面装饰器:使用
@Entry和@Component - 参数类型:避免传递复杂对象,使用ID或路径引用
- 生命周期:在
aboutToAppear中接收参数 - 路由配置:确保routerMap中的name与pushPath的name一致
可以先从传递简单参数开始测试,确认Navigation跳转基本功能正常,再逐步解决PixelMap传递问题。

