【鸿蒙开发实战】HarmonyOS单位转换器

代码功能概述

这段代码实现了一个功能完整的鸿蒙单位转换器应用,全面展示了ArkTS在表单处理、数据计算和分类管理等方面的核心能力。主要功能包括:

- 多分类单位转换:支持长度、重量、温度、面积四种单位类型的转换

- 实时计算:输入数值或切换单位时立即显示转换结果

- 智能输入验证:确保用户输入为有效数字

- 单位交换功能:一键交换源单位和目标单位

- 格式化显示:智能格式化计算结果,移除不必要的尾随零

- 温度特殊处理:针对温度单位使用专用转换公式

代码逻辑分析

应用采用"状态驱动UI"的现代化架构设计:

1. 初始化阶段:应用启动时,通过`aboutToAppear()`生命周期方法进行初始计算

2. 状态管理:使用多个`@State`装饰器管理输入值、分类选择、单位选择和计算结果

3. 用户交互流程:

   - 选择分类 → 更新`selectedCategory` → 重置单位选择并重新计算

   - 输入数值 → 验证并更新`inputValue` → 触发实时计算

   - 切换单位 → 更新`fromUnit`或`toUnit` → 重新计算转换结果

   - 点击交换按钮 → 交换源目标和目标单位 → 更新计算结果

4. 数据计算:根据单位类型使用不同的转换算法,普通单位使用系数转换,温度单位使用专用公式

5. UI自动更新:所有状态变化自动触发界面重新渲染,实现即时反馈

完整代码

// 鸿蒙单位转换器 - UnitConverterTutorial.ets
// 本应用演示ArkTS的表单处理、数据计算和分类管理

@Entry
@Component
struct UnitConverterTutorial {
  // @State装饰器:管理转换器状态
  @State inputValue: string = '1';
  @State selectedCategory: number = 0;
  @State fromUnit: number = 0;
  @State toUnit: number = 1;
  @State result: string = '';
  @State isReversed: boolean = false;

  // 单位分类数据
  private categories: Category[] = [
    {
      name: '长度',
      units: ['米', '厘米', '毫米', '千米', '英尺', '英寸'],
      factors: [1, 100, 1000, 0.001, 3.28084, 39.3701]
    },
    {
      name: '重量',
      units: ['千克', '克', '磅', '盎司'],
      factors: [1, 1000, 2.20462, 35.274]
    },
    {
      name: '温度',
      units: ['摄氏度', '华氏度', '开尔文'],
      factors: [1, 1, 1], // 温度需要特殊处理
      isTemperature: true
    },
    {
      name: '面积',
      units: ['平方米', '平方厘米', '平方英尺', '公顷'],
      factors: [1, 10000, 10.7639, 0.0001]
    }
  ];

  // 生命周期:组件创建时初始化计算
  aboutToAppear() {
    this.calculateResult();
  }

  build() {
    Column({ space: 20 }) {
      // 应用标题
      Text('单位转换器')
        .fontSize(32)
        .fontWeight(FontWeight.Bold)
        .fontColor('2D3748')
        .margin({ top: 20 })

      // 分类选择
      this.BuildCategorySelector()

      // 转换输入区域
      this.BuildConverterArea()

      // 结果显示
      this.BuildResultDisplay()

      // 转换信息
      this.BuildConversionInfo()
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('F7FAFC')
  }

  // 构建分类选择器 - 演示水平滚动选择
  @Builder BuildCategorySelector() {
    Scroll() {
      Row({ space: 10 }) {
        ForEach(this.categories, (category: Category, index: number) => {
          Button(category.name)
            .onClick(() => {
              this.selectedCategory = index;
              this.fromUnit = 0;
              this.toUnit = 1;
              this.calculateResult();
            })
            .backgroundColor(this.selectedCategory === index ? '4299E1' : 'E2E8F0')
            .fontColor(this.selectedCategory === index ? Color.White : '4A5568')
            .padding({ left: 16, right: 16 })
            .borderRadius(20)
            .height(40)
        })
      }
      .padding(10)
    }
    .scrollable(ScrollDirection.Horizontal)
    .width('100%')
    .margin({ bottom: 10 })
  }

  // 构建转换区域 - 演示复杂表单布局
  @Builder BuildConverterArea() {
    Column({ space: 15 }) {
      // 输入行
      Row({ space: 10 }) {
        // 输入框
        TextInput({ placeholder: '输入数值', text: this.inputValue })
          .onChange((value: string) => {
            // 验证输入是否为有效数字
            if (this.isValidNumber(value)) {
              this.inputValue = value;
              this.calculateResult();
            }
          })
          .type(InputType.Number)
          .padding(15)
          .backgroundColor(Color.White)
          .border({ width: 1, color: 'CBD5E0' })
          .borderRadius(8)
          .layoutWeight(1)

        // 源单位选择
        this.BuildUnitPicker(this.fromUnit, false)
      }

      // 转换方向按钮 - 修复:Button不能直接传入函数
      Button('⇅')
        .onClick(() => {
          // 交换单位
          const temp = this.fromUnit;
          this.fromUnit = this.toUnit;
          this.toUnit = temp;
          this.isReversed = !this.isReversed;
          this.calculateResult();
        })
        .backgroundColor('4299E1')
        .fontColor(Color.White)
        .width(40)
        .height(40)
        .borderRadius(20)
        .margin({ top: 5, bottom: 5 })

      // 输出行
      Row({ space: 10 }) {
        // 结果展示(只读)
        TextInput({ text: this.result })
          .enabled(false) // 禁用编辑
          .padding(15)
          .backgroundColor('EDF2F7')
          .border({ width: 1, color: 'CBD5E0' })
          .borderRadius(8)
          .layoutWeight(1)
          .fontColor('2D3748')

        // 目标单位选择
        this.BuildUnitPicker(this.toUnit, true)
      }
    }
    .width('100%')
    .padding(20)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 3, color: '000000', offsetX: 0, offsetY: 1 })
  }

  // 构建单位选择器 - 修复:使用自定义选择器替代Picker
  @Builder BuildUnitPicker(selectedIndex: number, isTarget: boolean) {
    Column() {
      // 使用Text显示当前选择的单位
      Text(this.getCurrentUnitName(selectedIndex))
        .fontSize(16)
        .fontColor('2D3748')
        .padding(10)
        .backgroundColor('F7FAFC')
        .border({ width: 1, color: 'CBD5E0' })
        .borderRadius(8)
        .width(120)
        .textAlign(TextAlign.Center)

      // 单位选择按钮组
      Row({ space: 5 }) {
        Button('<')
          .onClick(() => {
            this.selectPreviousUnit(isTarget);
          })
          .width(25)
          .height(25)
          .backgroundColor('4299E1')
          .fontColor(Color.White)
          .fontSize(12)

        Button('>')
          .onClick(() => {
            this.selectNextUnit(isTarget);
          })
          .width(25)
          .height(25)
          .backgroundColor('4299E1')
          .fontColor(Color.White)
          .fontSize(12)
      }
      .margin({ top: 5 })
    }
  }

  // 构建结果显示 - 演示格式化输出
  @Builder BuildResultDisplay() {
    // 修复:使用条件渲染而不是逻辑代码
    if (this.result && this.inputValue && parseFloat(this.inputValue)) {
      Column({ space: 8 }) {
        Text('转换结果')
          .fontSize(16)
          .fontColor('718096')
          .alignSelf(ItemAlign.Start)

        Text(`${this.inputValue} ${this.getCurrentUnitName(this.fromUnit)} = ${this.result} ${this.getCurrentUnitName(this.toUnit)}`)
          .fontSize(20)
          .fontWeight(FontWeight.Medium)
          .fontColor('2D3748')
          .textAlign(TextAlign.Center)
          .width('100%')
      }
      .width('100%')
      .padding(15)
      .backgroundColor('EBF8FF')
      .border({ width: 2, color: '4299E1' })
      .borderRadius(8)
    }
  }

  // 构建转换信息 - 修复:移除UI描述中的逻辑代码
  @Builder BuildConversionInfo() {
    Column({ space: 10 }) {
      Text('单位信息')
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .fontColor('2D3748')
        .alignSelf(ItemAlign.Start)

      // 修复:直接调用方法而不是变量声明
      Text(this.getConversionInfoText())
        .fontSize(14)
        .fontColor('4A5568')
        .textAlign(TextAlign.Start)
        .width('100%')

      if (this.categories[this.selectedCategory].isTemperature) {
        Text('温度转换使用特殊公式')
          .fontSize(12)
          .fontColor('718096')
          .fontStyle(FontStyle.Italic)
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .shadow({ radius: 2, color: '000000', offsetX: 0, offsetY: 1 })
  }

  // 选择上一个单位
  private selectPreviousUnit(isTarget: boolean): void {
    const currentCategory = this.categories[this.selectedCategory];
    const maxIndex = currentCategory.units.length - 1;

    if (isTarget) {
      this.toUnit = this.toUnit > 0 ? this.toUnit - 1 : maxIndex;
    } else {
      this.fromUnit = this.fromUnit > 0 ? this.fromUnit - 1 : maxIndex;
    }
    this.calculateResult();
  }

  // 选择下一个单位
  private selectNextUnit(isTarget: boolean): void {
    const currentCategory = this.categories[this.selectedCategory];
    const maxIndex = currentCategory.units.length - 1;

    if (isTarget) {
      this.toUnit = this.toUnit < maxIndex ? this.toUnit + 1 : 0;
    } else {
      this.fromUnit = this.fromUnit < maxIndex ? this.fromUnit + 1 : 0;
    }
    this.calculateResult();
  }

  // 获取转换信息文本
  private getConversionInfoText(): string {
    const factor = this.getConversionFactor();
    return `1 ${this.getCurrentUnitName(this.fromUnit)} = ${factor.toFixed(6)} ${this.getCurrentUnitName(this.toUnit)}`;
  }

  // 获取当前单位名称
  private getCurrentUnitName(unitIndex: number): string {
    return this.categories[this.selectedCategory].units[unitIndex];
  }

  // 计算转换结果
  private calculateResult(): void {
    const inputNum = parseFloat(this.inputValue);

    if (isNaN(inputNum)) {
      this.result = '';
      return;
    }

    const currentCategory = this.categories[this.selectedCategory];

    if (currentCategory.isTemperature) {
      // 温度转换特殊处理
      this.result = this.convertTemperature(inputNum).toFixed(2);
    } else {
      // 普通单位转换
      const factor = this.getConversionFactor();
      const calculated = inputNum  factor;
      // 格式化结果,移除不必要的尾随零
      this.result = calculated % 1 === 0 ? calculated.toString() : calculated.toFixed(4);
    }
  }

  // 获取转换系数
  private getConversionFactor(): number {
    const currentCategory = this.categories[this.selectedCategory];
    const fromFactor = currentCategory.factors[this.fromUnit];
    const toFactor = currentCategory.factors[this.toUnit];
    return fromFactor / toFactor;
  }

  // 温度转换
  private convertTemperature(value: number): number {
    const currentCategory = this.categories[this.selectedCategory];

    // 先将输入值转换为基准单位(摄氏度)
    let celsius: number;
    switch (this.fromUnit) {
      case 0: // 摄氏度
        celsius = value;
        break;
      case 1: // 华氏度
        celsius = (value - 32)  5 / 9;
        break;
      case 2: // 开尔文
        celsius = value - 273.15;
        break;
      default:
        celsius = value;
    }

    // 再从摄氏度转换为目标单位
    switch (this.toUnit) {
      case 0: // 摄氏度
        return celsius;
      case 1: // 华氏度
        return celsius  9 / 5 + 32;
      case 2: // 开尔文
        return celsius + 273.15;
      default:
        return celsius;
    }
  }

  // 验证数字输入
  private isValidNumber(value: string): boolean {
    if (value === '' || value === '-') return true;
    const num = parseFloat(value);
    return !isNaN(num);
  }
}

// 分类数据接口
interface Category {
  name: string;
  units: string[];
  factors: number[];
  isTemperature?: boolean;
}

想入门鸿蒙开发又怕花冤枉钱?别错过!现在能免费系统学 -- 从 ArkTS 面向对象核心的类和对象、继承多态,到吃透鸿蒙开发关键技能,还能冲刺鸿蒙基础 +高级开发者证书,更惊喜的是考证成功还送好礼!快加入我的鸿蒙班,一起从入门到精通,班级链接:点击免费进入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值