HarmonyOS鸿蒙开发 应用开发常见问题总结(持续更新...)

发布于:2025-02-11 ⋅ 阅读:(37) ⋅ 点赞:(0)

HarmonyOS鸿蒙开发 应用开发常见问题总结(持续更新…)

在学习鸿蒙开发过程中,也是遇到了不少问题,在代码编写过程中,遇到了一些问题,由于是学习阶段,需要时刻记录一下。

If a component attribute supports local initialization, a valid, runtime-independent default value should be set for it.

意思:如果组件属性支持本地初始化,则应为其设置有效的、独立于运行时的默认值。

  • 解决需要为属性设置默认值

原代码:

@ComponentV2
export struct CustomErrorComp {
  @Param @Require dataSetConfig: CustomDataSetConfig;
  build() {
    Column() {
      // 设置icon、title、message等元素
      CustomDataSetBuilder(this.dataSetConfig)
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.white'))
  }
}

由于传入的参数dataSetConfig,未设置默认值,这里CustomDataSetConfig是一个interface,
传入默认值代码更改后:

@Param @Require dataSetConfig: CustomDataSetConfig = {
    title: "",
    message: "",
    btnTitle: "",
  }
...
其他不变

E [ArkRuntime Log] TypeError: is not callable

由于自定义类的interface中有onCallback?: () => void处理点击回调

源代码中的判断有问题,源代码:

    .onClick(()=> {
      if (this.dataSetConfig.onCallback !== null) {
        this.dataSetConfig.onCallback!();
      }
    })

一开始以为和flutter判断一样!== null,结果这样判断不对
将代码修改如下就正常了:

    .onClick(()=> {
      if (this.dataSetConfig.onCallback) {
        this.dataSetConfig.onCallback!();
      }
    })

E [ArkRuntime Log] TypeError: Cannot read property length of undefined

无法读取未定义的属性长度

这里未判断,获取了string的length

所以在使用length前需要做一下typeof this.message !== 'undefined'判断

// message
    if (this.message !== null && typeof this.message !== 'undefined') {
      Text(this.message)
        .fontSize(12)
        .fontColor(Color.red)
        .margin({
          bottom: this.message.length > 0 ? 10 : 0
        })
    }

Navigation push之后底部Tabbar 没有隐藏问题

参考https://developer.huawei.com/consumer/cn/doc/architecture-guides/develop-arkui-67-0000002122758974
这里希望可以使用自定义导航栏,根据页面index去切换不同的导航栏,或者隐藏导航栏,思路如下:

修改
通过Navigation嵌套tab,将其隐藏的方式,代码如下

@Entry
@ComponentV2
struct Index {
  @Local message: string = 'Hello World';
  @Provider('tabCurrentIndex') tabCurrentIndex: number = 0;

  // 初始化NavPathStack
  @Provider('pageInfos') pageInfos: NavPathStack = new NavPathStack()

使用Navigation嵌套tab

Navigation(this.pageInfos){
      Tabs({index: this.tabCurrentIndex, barPosition:BarPosition.End}){
        TabContent(){
          NewsTabsView()
        }
        .tabBar(this.TabBuilder({
          title: "首页",
          targetIndex: 0,
          normalImg: $r('app.media.ic_tab_shouye'),
          selectedImg: $r('app.media.ic_tab_shouye_filled')
        }))
      }
      .barMode(BarMode.Fixed) // 默认值
      .scrollable(false)  // 限制滑动
      .onAnimationStart((_index: number, targetIndex: number, _event: TabsAnimationEvent) => {
        this.tabCurrentIndex = targetIndex;
      })
    }
    .hideTitleBar(true)

跳转使用代码

.onClick(() => {
                  this.pageInfos.pushPathByName("setting", null, true);
                })

The buildFunction ‘PageSettingBuilder’ configured in the routerMap json file does not exist.

配置route_map时候,指定了buildFunction,
route_map如下

{
  "routerMap": [
    {
      "name": "setting",
      "pageSourceFile": "src/main/ets/pages/pages/SettingIndexPage.ets",
      "buildFunction": "PageSettingBuilder",
      "data": {
        "description": "this is setting page"
      }
    }
  ]
}

所以如果没有在SettingIndexPage.ets页面中创建PageSettingBuilder,则会出现报错

所以在页面增加一个

/// 设置页面
/// 跳转页面入口函数
@Builder
export function PageSettingBuilder() {
  SettingIndexPage()
}

@ComponentV2
export struct SettingIndexPage {
	// 其他代码
}

Error message:is not callable

在我的代码中,为navigation添加
我的代码如下

  @Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()

NavDestination() {
        List({ space: 12 }) {
          ForEach(this.data, (item: PageInfo) => {
            ListItem() {
              Text("Page" + item.title)
                .onClick(() => {
                  this.pageInfos.pushPathByName("setting", null, true);
                })
            }
          }, (item: PageInfo) => item.url.toString())
        }
        .width("90%")
        .margin({ top: 12 })
      }
      .title("我的")
      .mode(NavDestinationMode.STANDARD)
      .onReady((context: NavDestinationContext) => {
        this.pageInfos = context.pathStack
      })

将onReady去掉即可,没有报错了,暂时有点奇怪,还不清楚原因,学习中

  @Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()

NavDestination() {
        List({ space: 12 }) {
          ForEach(this.data, (item: PageInfo) => {
            ListItem() {
              Text("Page" + item.title)
                .onClick(() => {
                  this.pageInfos.pushPathByName("setting", null, true);
                })
            }
          }, (item: PageInfo) => item.url.toString())
        }
        .width("90%")
        .margin({ top: 12 })
      }
      .title("我的")

Navigation页面跳转如何传参数

Navigation中,可以使用this.pageInfos.pushPathByName("setting", param, true);进行页面跳转,
当然在开发中,跳转页面很多时候需要传递参数,传递参数,可以定义interface
我的使用方式如下

export interface SettingPageParam<T> {
  from:string,
  tag:string
  data: T
}

// 进行跳转

                  let param: SettingPageParam<string> = {
                    from: "my",
                    tag: "mytag",
                    data: "来自我的tab的参数"
                  };
                  this.pageInfos.pushPathByName("setting", param, true);

接收参数,需要通过NavPathStack的getParamByName

  aboutToAppear(): void {
    let param:SettingPageParam<string> = this.pageInfos.getParamByName('setting')[0] as SettingPageParam<string>;
    console.info("SettingIndexPage aboutToAppear param.from:"+ param.from + ", param.tag:"+ param.tag + ", param.data:"+ param.data);
  }

// 打印的结果
01-02 16:38:03.633   25140-25140   A03d00/JSAPP                    com.example.myhmapp   I     SettingIndexPage aboutToAppear param.from:my, param.tag:mytag, param.data:来自我的tab的参数

CustomDialogController中的builder,如果使用new报一下错误

Error message:Cannot read property controller of undefined
这里使用new BaseComHud(),结果报错了,错误信息:Error message:Cannot read property controller of undefined


@CustomDialog
export struct BaseComHud {
  // message: string = '';
  controller?: CustomDialogController
  ...其他代码
}

customDialogController: CustomDialogController | null = new CustomDialogController({
    builder:new BaseComHud(),
    alignment: DialogAlignment.Center,
  });

当前也很奇怪原因,不过去掉new,代码成功,代码修改如下

customDialogController: CustomDialogController | null = new CustomDialogController({
    builder:BaseComHud(),
    alignment: DialogAlignment.Center,
  });

The component ‘Blank’ can’t have any child.

很奇怪原因,代码使用如下,结果报错The component ‘Blank’ can’t have any child.

// 占位
          Blank(){}.layoutWeight(1)

修改后代码,去掉{},成功运行

// 占位
          Blank().layoutWeight(1)

PersistenceV2持久化数据,在重启后未能获取到数据。

@ObservedV2
export class User {
  @Trace userId?: string    // 可选属性
  @Trace nickName?: string   // 可选属性
}

@Local user: User = PersistenceV2.connect(User, () => new User())!;

设置数据保存
this.user.userId = "110";
this.user.nickName = "test-user-108Lucy";


重启后数据不能获取到user的userId,nickName;

但如果将User的属性userId、nickName改成非可选属性,

@ObservedV2
export class User {
  @Trace userId: string = ""
  @Trace nickName?: string = ""
}

并且进行初始化,就能正常保存数据,重启后也能正常获取到。

The @Concurrent decorator can decorate only common functions and async functions.

当使用taskpool时候,如果在struct组件中使用@Concurrent,会报错: @Concurrent decorator can decorate only common functions and async functions.

这时候我们需要将方法放在外面,不能放在class、struct作为其method。

@Concurrent
function printMessage(message: string) {
  console.log(`printMessage message:${message}`);
}

之后在执行taskpool的excute

let task1: taskpool.Task = new taskpool.Task(printMessage,"100"); // 100: test number
let task2: taskpool.Task = new taskpool.Task(printMessage,"200"); // 200: test number
taskpool.execute(task1, taskpool.Priority.LOW).then((value: Object) => {
  console.info("taskpool result1: " + value);
});
taskpool.execute(task2, taskpool.Priority.MEDIUM).then((value: Object) => {
  console.info("taskpool result2: " + value);
});

网站公告

今日签到

点亮在社区的每一天
去签到