1、前言

经过我们前面10章的学习,我们基本上可以开发出一个简单的APP了,为了巩固学习的内容,我们先开发一个计算器APP来连个手(文末有源代码),界面如下:

image.png

这里面我们会用到知识如下:

开发UI界面(基本控件的使用以及基本布局的使用);

监听用户点击事件,在输入栏中及时反馈(动态交互);

支持将过程持久化到本地,下次打开后自动恢复;

let’s go!

2、创建项目 &初始化

打开开发工具 DevEco-Studio,按照以下顺序创建项目,过程不再详述,看截图。

图片

图片

图片

3、UI描述

我们的布局比较简单,分三个大块从上到下,分别是:

用户输入与计算结果显示部分;

核心数字键盘部分;

一个大的=按钮

示意图如下:

image.png

我们可以注意到,数字按钮的样式是一样的(蓝色),操作符按钮的样式也是一样的(绿色),三个功能按钮的样式也是一样的(红色),因此,我们可以使用鸿蒙的样式复用只是来减少重复代码量。

我们三部分复用的样式代码如下:

// 数字键盘的公共样式
@Extend(Button) function numberBtn(num: number, click: (num: number) => void) {
  .type(ButtonType.Capsule)
  .width(60)
  .height(60)
  .onClick(() => click(num))
}
// 运算符公共样式
@Extend(Button) function operatorBtn() {
  .type(ButtonType.Capsule)
  .backgroundColor(Color.Green)
  .width(60)
  .height(60)
}
// 额外功能公共样式
@Extend(Button) function extraBtn() {
  .type(ButtonType.Capsule)
  .width(60)
  .height(60)
  .backgroundColor(Color.Red)
}

紧接着就是我们的UI描述部分代码,如下:

  build() {
    Column() {
      Column() {
        Text(this.message)
          .fontSize(20)
          .width('100%')
          .textAlign(TextAlign.End)
      }
      .padding(10)
      .backgroundColor('gray')
      .borderWidth(1)
      .borderRadius(10)
      .margin(10)
      Row() {
        Button('7').numberBtn(7, this.onNumberClick)
        Button('8').numberBtn(8, this.onNumberClick)
        Button('9').numberBtn(9, this.onNumberClick)
        Button('重置').extraBtn().onClick(this.onResetClick)
      }
      .justifyContent(FlexAlign.SpaceAround)
      .numberRow()
      Row() {
        Button('4').numberBtn(4, this.onNumberClick)
        Button('5').numberBtn(5, this.onNumberClick)
        Button('6').numberBtn(6, this.onNumberClick)
        Button('删除').extraBtn().onClick(this.onDeleteClick)
      }
      .numberRow()
      .justifyContent(FlexAlign.SpaceAround)
      Row() {
        Button('1').numberBtn(1, this.onNumberClick)
        Button('2').numberBtn(2, this.onNumberClick)
        Button('3').numberBtn(3, this.onNumberClick)
        Button('清理').extraBtn().onClick(this.onClearClick)
      }
      .numberRow()
      .justifyContent(FlexAlign.SpaceAround)
      Row() {
        Button('%')
          .type(ButtonType.Capsule)
          .width(60)
          .height(60)
        Button('0').numberBtn(0, this.onNumberClick)
        Button('.')
          .type(ButtonType.Capsule)
          .width(60)
          .height(60)
          .onClick(this.onDotClick)
        Button('保存').operatorBtn().onClick(this.onSaveClick)
      }
      .numberRow()
      .justifyContent(FlexAlign.SpaceAround)
      Row() {
        Button('x')
          .operatorBtn()
          .onClick(() => this.onOperatorClick('x'))
        Button('÷')
          .operatorBtn()
          .onClick(() => this.onOperatorClick('÷'))
        Button('+')
          .operatorBtn()
          .onClick(() => this.onOperatorClick('+'))
        Button('-')
          .operatorBtn()
          .onClick(() => this.onOperatorClick('-'))
      }
      .numberRow()
      .justifyContent(FlexAlign.SpaceAround)
      Row() {
        Button('=')
          .width('100%')
          .backgroundColor(Color.Pink)
          .onClick(this.onCalcClick)
      }
      .margin(10)
    }
    .height('100%')
  }

4、用户交互

紧接着,我们来实现用户的功能交互

  onNumberClick = (num: number) => {
    if (this.message === '0') {
      this.message = `${num}`;
    } else {
      this.message += `${num}`
    }
  }

  onOperatorClick = (_opt: string) => {
    this.message += _opt;
    this.calcFinished = false;
  }

  onSaveClick = () => {
    AppStorage.SetOrCreate('lastResult', this.message);
  }
  onClearClick = () => {
    PersistentStorage.DeleteProp('lastResult')
    this.message = '0';
  }

其他的功能感兴趣可以在文末看详细的源代码。演示如下(包含了数据持久化,现场恢复的演示)

5、源代码

代码仓库地址:

https://gitee.com/lantingshuxu/harmony-calculator

速览核心的UI代码:

// 数字键盘的公共样式
@Extend(Button) function numberBtn(num: number, click: (num: number) => void) {
  .type(ButtonType.Capsule)
  .width(60)
  .height(60)
  .onClick(() => click(num))
}
// 运算符公共样式

鸿蒙开发入门_鸿蒙开发自学_

@Extend(Button) function operatorBtn() { .type(ButtonType.Capsule) .backgroundColor(Color.Green) .width(60) .height(60) } // 额外功能公共样式 @Extend(Button) function extraBtn() { .type(ButtonType.Capsule) .width(60) .height(60) .backgroundColor(Color.Red) } PersistentStorage.PersistProp('lastResult', '0'); type Operator = '+' | '-' | '*' | '/' | '(' | ')'; // 传入字符串,运算其结果(可以跳过) function calculate(expression: string): number {     //.. 这部分代码是计算用户输入结果d的,不重要,省略 } @Entry @Component struct Index { @StorageProp('lastResult') message: string = '0' calcFinished: boolean = true; @Styles numberRow() { .width('100%') .margin({ bottom: 10 }) } build() { Column() { Column() { Text(this.message) .fontSize(20) .width('100%') .textAlign(TextAlign.End) } .padding(10) .backgroundColor('gray') .borderWidth(1) .borderRadius(10) .margin(10) Row() { Button('7').numberBtn(7, this.onNumberClick) Button('8').numberBtn(8, this.onNumberClick) Button('9').numberBtn(9, this.onNumberClick) Button('重置').extraBtn().onClick(this.onResetClick) } .justifyContent(FlexAlign.SpaceAround) .numberRow() Row() { Button('4').numberBtn(4, this.onNumberClick) Button('5').numberBtn(5, this.onNumberClick) Button('6').numberBtn(6, this.onNumberClick) Button('删除').extraBtn().onClick(this.onDeleteClick) } .numberRow() .justifyContent(FlexAlign.SpaceAround) Row() { Button('1').numberBtn(1, this.onNumberClick) Button('2').numberBtn(2, this.onNumberClick) Button('3').numberBtn(3, this.onNumberClick) Button('清理').extraBtn().onClick(this.onClearClick) } .numberRow() .justifyContent(FlexAlign.SpaceAround) Row() { Button('%') .type(ButtonType.Capsule) .width(60) .height(60) Button('0').numberBtn(0, this.onNumberClick) Button('.') .type(ButtonType.Capsule) .width(60) .height(60) .onClick(this.onDotClick) Button('保存').operatorBtn().onClick(this.onSaveClick) } .numberRow() .justifyContent(FlexAlign.SpaceAround) Row() { Button('x') .operatorBtn() .onClick(() => this.onOperatorClick('x')) Button('÷') .operatorBtn() .onClick(() => this.onOperatorClick('÷')) Button('+') .operatorBtn() .onClick(() => this.onOperatorClick('+')) Button('-') .operatorBtn() .onClick(() => this.onOperatorClick('-')) } .numberRow() .justifyContent(FlexAlign.SpaceAround) Row() { Button('=') .width('100%') .backgroundColor(Color.Pink) .onClick(this.onCalcClick) } .margin(10) } .height('100%') } resetCheck() { if (this.calcFinished) { this.message = '0'; this.calcFinished = false; } } onNumberClick = (num: number) => { this.resetCheck(); if (this.message === '0') { this.message = `${num}`; } else { this.message += `${num}` } } onOperatorClick = (_opt: string) => { this.message += _opt; this.calcFinished = false; } onDotClick = () => { this.resetCheck(); this.message += '.'; } onResetClick = () => { this.message = '0'; this.calcFinished = true; } onDeleteClick = () => { this.resetCheck(); if (this.message.length > 1) { this.message = this.message.substr(0, this.message.length - 1); } else { this.message = '0' } } onSaveClick = () => { AppStorage.SetOrCreate('lastResult', this.message); } onClearClick = () => { PersistentStorage.DeleteProp('lastResult') this.message = '0'; } onCalcClick = () => { this.message = calculate(this.message.replace(/x/g, '*').replace(/÷/g, '/')) + ''; this.calcFinished = true; } }

6、结语

从代码我们可以看到,依旧存在很多相似的重复代码,我们还可以优化下吗?

答案是可以的,使用条件渲染。

请持续关注 “鸿蒙UI开发快速入门 —— part11”

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。