HarmonyOS ArkTS 实战:实现一个进制转换器

项目效果

本文实现的是一个基于 HarmonyOS 和 ArkTS 的进制转换器。项目中使用 ArkUI 组件完成页面布局,通过 @State 管理状态数据,实现二进制、八进制、十进制、十六进制之间的实时互相转换,输入任意进制其他进制自动更新,支持复制功能。

最终运行效果如下:
在这里插入图片描述

页面主要包含以下内容:

  • 顶部应用标题;
  • 四个输入区域:二进制、八进制、十进制、十六进制;
  • 当前输入的进制高亮显示;
  • 每个进制对应一个复制按钮;
  • 输入键盘提示(十六进制显示A-F);
  • 页面整体采用 ArkUI 声明式布局。

本文重点是演示如何在 HarmonyOS 项目中使用 ArkTS 和 ArkUI 实现一个程序员工具类单页面应用。项目代码主要写在 entry/src/main/ets/pages/Index.ets 文件中,适合作为 HarmonyOS ArkTS 入门到进阶之间的练习案例。

前言

在编程和计算机学习中,我们经常需要在不同进制之间转换数字。二进制、八进制、十进制、十六进制是最常用的四种进制,手动转换很麻烦,一个好用的进制转换器可以大大提高效率。

从应用开发角度来看,这个项目不依赖后端接口,也不需要数据库,但能练习 ArkTS 中的状态管理、多输入框联动、输入验证、数字进制转换和剪贴板操作等内容。

本文基于 HarmonyOS 和 ArkTS 实现一个进制转换器。用户在任意进制输入框输入数字,其他三个进制会自动实时转换显示,每个结果都可以一键复制。

这个项目的核心不是简单的进制转换API,而是多输入框的联动和输入验证。在一个输入框输入时,其他输入框自动更新,这正是 ArkUI 声明式开发的基本思想。

一、项目目标

本次实践主要实现以下目标:

  • 创建 HarmonyOS ArkTS 页面;
  • 使用 @Entry@Component 定义页面组件;
  • 使用 @State 管理页面状态;
  • 支持二进制(BIN)输入和转换;
  • 支持八进制(OCT)输入和转换;
  • 支持十进制(DEC)输入和转换;
  • 支持十六进制(HEX)输入和转换;
  • 输入时实时转换其他进制;
  • 输入验证,只允许合法字符;
  • 每个结果一键复制;
  • 当前编辑的输入框高亮;
  • 大数字支持;
  • 使用 @Builder 封装进制输入项;
  • 完成一个可以运行的进制转换器页面。

这个项目虽然是单页面应用,但它实现了四种进制的实时互相转换,是程序员常用的工具,比普通静态页面更适合练习 ArkTS。

二、技术栈

类型 内容
开发方向 HarmonyOS 应用开发
开发语言 ArkTS
UI 框架 ArkUI
SDK 版本 HarmonyOS API 23 及以上
工程模型 Stage 模型
核心组件 Text / TextInput / Button / Column / Row
状态管理 @State
数据处理 进制转换 / 输入验证 / 实时联动
项目入口 entry/src/main/ets/pages/Index.ets
运行平台 模拟器或真机

本项目是 HarmonyOS 原生 ArkTS 项目。页面主体由 ArkUI 组件构建,核心逻辑写在 Index.ets 文件中,不依赖后端接口,也不需要额外配置数据库。

三、为什么选择进制转换器项目

进制转换器适合作为 ArkTS 练习项目,主要有以下几个原因。

第一,实用性强。程序员和计算机相关专业学生经常需要进制转换。

第二,业务场景真实。调试、看内存地址、网络编程等场景都需要进制转换。

第三,适合练习状态管理。四个输入框联动,一个变化其他都要更新。

第四,适合练习输入验证。不同进制允许的字符不同,需要过滤非法输入。

第五,适合练习数字处理。JavaScript/TypeScript的parseInt和toString支持进制转换。

第六,扩展空间比较大。基础功能完成后,可以继续增加ASCII转换、字节序转换、IP地址转换、Unicode转换和浮点数转换。

在本项目中,当前输入的十进制数值是核心数据。所有进制的显示都基于这个数值转换而来。

四、功能规则说明

支持的进制:

进制 基数 允许字符
二进制(BIN) 2 0, 1
八进制(OCT) 8 0-7
十进制(DEC) 10 0-9
十六进制(HEX) 16 0-9, A-F, a-f

转换规则:

  • 在任意输入框输入合法字符,其他输入框自动更新
  • 输入非法字符自动过滤
  • 空输入时所有输入框显示空或0
  • 支持大数字(在JavaScript安全整数范围内)
  • 十六进制自动转大写显示

复制功能:点击复制按钮复制对应进制的字符串到剪贴板。

五、项目结构

本项目主要修改首页文件:

entry
└── src
    └── main
        └── ets
            └── pages
                └── Index.ets

其中:

文件 作用
Index.ets 编写页面结构、状态数据和进制转换逻辑

本文不涉及复杂路由,也不需要额外创建多个页面。对于练习项目来说,把主要逻辑集中在一个 Index.ets 文件中更方便理解。

六、核心实现思路

本项目的核心流程如下:

  1. 使用 @State 保存当前十进制数值和四个进制的显示字符串;
  2. 用户在某个输入框输入;
  3. 过滤非法字符,只保留该进制允许的字符;
  4. 将输入的字符串解析为十进制数值;
  5. 将十进制数值转换为其他三个进制的字符串;
  6. 更新所有输入框的显示;
  7. 高亮当前正在编辑的输入框;
  8. 点击复制按钮复制对应内容;
  9. 处理空输入和非法输入。

项目中最重要的状态变量如下:

@State binValue: string = ''
@State octValue: string = ''
@State decValue: string = ''
@State hexValue: string = ''
@State activeRadix: number = 10

其中:

状态变量 作用
binValue 二进制字符串
octValue 八进制字符串
decValue 十进制字符串
hexValue 十六进制字符串
activeRadix 当前激活的进制

内部以十进制数值为中间值,所有转换都经过十进制中转,这样逻辑最简单。

七、Index.ets 完整代码

打开文件:

entry/src/main/ets/pages/Index.ets

将其中内容替换为下面代码:

@Entry
@Component
struct Index {
  @State binValue: string = '0'
  @State octValue: string = '0'
  @State decValue: string = '0'
  @State hexValue: string = '0'
  @State activeRadix: number = 10

  private updateFromRadix(value: string, radix: number): void {
    let filtered = this.filterInput(value, radix)
    if (filtered === '') {
      this.binValue = ''
      this.octValue = ''
      this.decValue = ''
      this.hexValue = ''
      return
    }

    let num = parseInt(filtered, radix)
    if (isNaN(num)) {
      num = 0
    }

    this.binValue = num.toString(2)
    this.octValue = num.toString(8)
    this.decValue = num.toString(10)
    this.hexValue = num.toString(16).toUpperCase()
  }

  private filterInput(value: string, radix: number): string {
    let pattern: RegExp
    switch (radix) {
      case 2:
        pattern = /[^01]/g
        break
      case 8:
        pattern = /[^0-7]/g
        break
      case 10:
        pattern = /[^0-9]/g
        break
      case 16:
        pattern = /[^0-9a-fA-F]/g
        break
      default:
        return value
    }
    return value.replace(pattern, '')
  }

  private copyValue(value: string): void {
    if (!value) return
    let context = getContext(this)
    try {
      context.clipboard.setContent(value)
    } catch (e) {
      console.error('复制失败')
    }
  }

  @Builder
  RadixInput(label: string, sublabel: string, value: string, radix: number, color: string) {
    Column() {
      Row() {
        Column() {
          Text(label)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor(color)
          Text(sublabel)
            .fontSize(12)
            .fontColor('#9CA3AF')
        }
        .alignItems(HorizontalAlign.Start)
        Blank()
        Button('复制')
          .height(32)
          .fontSize(13)
          .padding({ left: 12, right: 12 })
          .fontColor(color)
          .backgroundColor(color + '15')
          .borderRadius(16)
          .onClick(() => {
            this.copyValue(value)
          })
      }
      .width('100%')
      .margin({ bottom: 12 })

      TextInput({
        text: value,
        placeholder: '0'
      })
        .width('100%')
        .height(56)
        .fontSize(20)
        .fontFamily('monospace')
        .fontColor('#182431')
        .backgroundColor(this.activeRadix === radix ? color + '08' : '#F9FAFB')
        .borderRadius(12)
        .border({
          width: this.activeRadix === radix ? 2 : 1,
          color: this.activeRadix === radix ? color : '#E5E7EB'
        })
        .padding({ left: 16, right: 16 })
        .type(InputType.Number)
        .onFocus(() => {
          this.activeRadix = radix
        })
        .onChange((newValue: string) => {
          this.activeRadix = radix
          this.updateFromRadix(newValue, radix)
        })
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(16)
    .margin({ bottom: 12 })
    .shadow({
      radius: 6,
      color: '#06000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  build() {
    Scroll() {
      Column() {
        Text('进制转换器')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor('#182431')
          .margin({ top: 22 })
        Text('基于 HarmonyOS ArkTS 的程序员工具')
          .fontSize(14)
          .fontColor('#6B7280')
          .margin({ top: 8, bottom: 20 })

        this.RadixInput('二进制', 'BIN (Base 2)', this.binValue, 2, '#3B82F6')
        this.RadixInput('八进制', 'OCT (Base 8)', this.octValue, 8, '#8B5CF6')
        this.RadixInput('十进制', 'DEC (Base 10)', this.decValue, 10, '#10B981')
        this.RadixInput('十六进制', 'HEX (Base 16)', this.hexValue, 16, '#F59E0B')

        Column() {
          Text('💡 提示')
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .fontColor('#6B7280')
            .width('100%')
            .margin({ bottom: 8 })
          Text('在任意输入框输入数字,其他进制会自动转换。二进制只允许0和1,八进制0-7,十进制0-9,十六进制0-9和A-F。')
            .fontSize(13)
            .fontColor('#9CA3AF')
            .lineHeight(20)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F9FAFB')
        .borderRadius(12)
        .margin({ top: 8 })
      }
      .width('100%')
      .padding({ left: 18, right: 18, bottom: 30 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
    .scrollBar(BarState.Off)
  }
}

八、代码实现说明

1. 输入过滤

每个进制只允许特定字符,用正则表达式过滤非法输入:

  • 二进制:只保留0和1
  • 八进制:只保留0-7
  • 十进制:只保留0-9
  • 十六进制:只保留0-9和a-f/A-F
2. 进制转换

使用JavaScript内置的parseInt和toString方法:

  • parseInt(str, radix):将指定进制的字符串转为十进制数字
  • num.toString(radix):将十进制数字转为指定进制的字符串

以十进制为中间值,实现任意进制之间的转换。

3. 实时联动

输入变化时立即更新所有进制,不需要点击转换按钮,体验流畅。

4. 焦点高亮

当前输入框获得焦点时,边框和背景变为对应进制的主题色,直观显示当前在编辑哪个。

5. 一键复制

每个进制都有独立的复制按钮,点击复制对应的值到剪贴板。

6. 等宽字体

数字使用等宽字体显示,对齐整齐,阅读更舒服。

九、运行项目

代码编写完成后,在DevEco Studio中运行项目。在不同输入框输入数字,检查其他进制是否正确转换,测试非法字符是否被过滤,测试复制功能是否正常。

十、开发中遇到的问题

  • 输入过滤:必须过滤掉非法字符,否则parseInt会出错
  • 空输入处理:输入为空时所有框都清空,不显示0
  • 焦点状态:输入和聚焦都要更新activeRadix
  • 十六进制大写:统一转大写显示,更规范

十一、总结

本文基于HarmonyOS和ArkTS实现了一个进制转换器。项目通过parseInt和toString实现四种进制的实时互相转换,支持输入验证、焦点高亮和一键复制。这个项目展示了ArkTS中多输入框联动、输入验证和数字处理的基本方法,适合作为入门练习项目。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐