鸿蒙NEXT开发案例:温度转换

b23.gif

【引言】

温度是日常生活中常见的物理量,但不同国家和地区可能使用不同的温度单位,如摄氏度(Celsius)、华氏度(Fahrenheit)、开尔文(Kelvin)、兰氏度(Rankine)和列氏度(Reaumur)。为了方便用户在这些温度单位之间进行快速准确的转换,我们开发了一款温度转换工具。该工具支持五种温度单位之间的相互转换,旨在为用户提供便捷的服务。

【环境准备】

• 操作系统:Windows 10

• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 12

• 三方库:@nutpi/temperature-converter(核心算法)

【项目结构】

项目的核心组件是 TemperatureConverterApp,它负责构建整个应用的用户界面,并处理用户输入及温度单位之间的转换逻辑。

  1. 温度单位类定义

我们定义了一个温度单位类 TemperatureUnit,用于封装温度单位的基本信息及其操作方法。每个温度单位都有一个标题、当前温度值和输入框的焦点状态。通过 setValue 方法,可以设置温度值并保留三位小数。

  1. 温度单位类型枚举

为了更好地管理和使用温度单位,我们定义了一个温度单位类型对象 TemperatureUnitType,列出了五种温度单位的名称。

  1. 应用程序主组件

TemperatureConverterApp 组件是整个应用的入口,它定义了应用的样式属性,并实现了UI的构建逻辑。组件中包含了多个状态变量,用于设置应用的颜色、字体大小等样式。

在UI构建逻辑中,我们使用了鸿蒙NEXT提供的布局组件,如 Column 和 Row,来组织页面的布局。页面顶部有一个标题 "温度转换",下方是一个垂直布局的容器,动态生成每个温度单位的输入框。每个输入框都绑定了 onChange 事件,当用户输入或更改温度值时,会触发相应的转换逻辑,更新其他温度单位的值。

  1. 温度转换逻辑

温度转换逻辑通过调用 @nutpi/temperature-converter 库中的方法实现。当用户在某个温度单位的输入框中输入温度后,程序会根据当前输入的温度单位,调用相应的转换方法,计算出其他温度单位对应的值,并更新界面上的显示。

例如,如果用户在摄氏度输入框中输入温度,程序会自动计算出华氏度、开尔文、兰氏度和列氏度的值,并更新相应的输入框。

【用户体验】

为了提升用户体验,我们在输入框上添加了焦点状态的处理。当输入框获得焦点时,背景颜色和边框颜色会发生变化,以提示用户当前的操作位置。此外,输入框还支持输入过滤,只允许输入数字和小数点,确保输入的有效性。

【完整代码】

导包

ohpm install @nutpi/temperature-converter

代码

// 引入温度转换器模块
import { TemperatureConverter } from "@nutpi/temperature-converter"

// 定义温度单位类型对象,用于存储各温度单位的名称
const TemperatureUnitType: object = Object({
  Celsius: '摄氏度', // 摄氏度
  Fahrenheit: '华氏度', // 华氏度
  Kelvin: '开尔文', // 开尔文
  Rankine: '兰氏度', // 兰氏度
  Reaumur: '列氏度' // 列氏度
})

// 使用装饰器定义一个温度单位类
@ObservedV2
class TemperatureUnit {
  title: string // 温度单位的标题
  @Trace value: string = "" // 当前温度值,使用@Trace装饰器追踪变化
  @Trace isInputFocused: boolean = false // 输入框是否获得焦点,同样使用@Trace追踪变化

  // 构造函数,初始化时传入温度单位的标题
  constructor(title: string) {
    this.title = title
  }

  // 设置温度值的方法,保留三位小数
  setValue(value: number) {
    this.value = `${parseFloat(value.toFixed(3))}` // 将数值转换成字符串,保留三位小数
    console.info(`温度值:${this.value}`) // 打印当前温度值到控制台
  }
}

// 定义温度转换器应用程序的入口组件
@Entry
@Component
struct TemperatureConverterApp {
  // 定义一系列的状态变量,用于设置应用的颜色、字体大小等样式
  @State private primaryColor: string = "#080808" // 主色调
  @State private secondaryColor: string = "#f7f7f7" // 次要色调
  @State private bgColor: string = "#f4f8fb" // 背景颜色
  @State private placeholderColor: string = "#2f9b6c" // 占位符颜色
  @State private textColor: string = "#a3a3a3" // 文本颜色
  @State private fontSizeSmall: number = 16 // 较小的字体大小
  @State private fontSizeLarge: number = 18 // 较大的字体大小
  @State private basePadding: number = 30 // 基础内边距

  // 初始化温度单位数组,创建每个温度单位的实例
  @State private temperatureUnits: TemperatureUnit[] =
    Object.keys(TemperatureUnitType).map(unit => new TemperatureUnit(TemperatureUnitType[unit]))

  // 构建应用程序的UI
  build() {
    Column() { // 创建一个垂直布局容器
      // 添加标题
      Text('温度转换')
        .fontColor(this.primaryColor) // 设置字体颜色
        .fontSize(this.fontSizeSmall) // 设置字体大小
        .width('100%') // 设置宽度
        .height(50) // 设置高度
        .textAlign(TextAlign.Center) // 设置文本对齐方式
        .backgroundColor(Color.White) // 设置背景颜色
        .shadow({ // 添加阴影效果
          radius: 2, // 阴影半径
          color: this.secondaryColor, // 阴影颜色
          offsetX: 0, // X轴偏移量
          offsetY: 5 // Y轴偏移量
        });

      // 循环遍历温度单位数组,动态生成每个温度单位的输入框
      Column() {
        ForEach(this.temperatureUnits, (unit: TemperatureUnit, index: number) => {
          Row() { // 创建一个水平布局容器
            // 显示温度单位的标题
            Text(`${unit.title}`).fontSize(this.fontSizeSmall).fontColor(this.primaryColor)

            // 创建输入框
            Row() {
              TextInput({
                text: unit.value, // 输入框的初始值
                placeholder: !unit.isInputFocused ? `请输入${unit.title}` : '' // 输入框的占位符文本
              })
                .inputFilter('[0-9.-]', (e) => console.log(JSON.stringify(e))) // 过滤输入内容,只允许数字和小数点
                .fontSize(this.fontSizeSmall) // 设置字体大小
                .backgroundColor(Color.Transparent) // 设置背景颜色
                .padding(0) // 设置内边距
                .width('100%') // 设置宽度
                .height('100%') // 设置高度
                .placeholderColor(unit.isInputFocused ? this.placeholderColor : this.textColor) // 设置占位符颜色
                .fontColor(unit.isInputFocused ? this.placeholderColor : this.primaryColor) // 设置字体颜色
                .caretColor(this.placeholderColor) // 设置光标颜色
                .borderRadius(0) // 设置圆角
                .onBlur(() => unit.isInputFocused = false) // 失去焦点时的处理
                .onFocus(() => unit.isInputFocused = true) // 获得焦点时的处理
                .onChange((value: string) => { // 输入内容改变时的处理
                  if (!unit.isInputFocused) { // 如果输入框未获得焦点,则不处理数据
                    console.info(`当前位置${index}没有焦点,不处理数据内容`)
                    return
                  }
                  if (unit.value == value) { // 如果新旧值相同,则不处理
                    console.info(`当前位置${index}内容与修改内容相同,不需要继续处理`)
                    return
                  }
                  console.info(`onChange, unit.value:${unit.value}, value:${value}`) // 打印变更信息
                  const tempValue = Number(value); // 将输入的字符串转换成数字
                  unit.setValue(tempValue) // 更新当前温度单位的值

                  // 根据用户输入的温度单位,计算并更新其他温度单位的值
                  switch (index) {
                    case 0:
                      this.temperatureUnits[1].setValue(TemperatureConverter.celsiusToFahrenheit(tempValue))
                      this.temperatureUnits[2].setValue(TemperatureConverter.celsiusToKelvin(tempValue))
                      this.temperatureUnits[3].setValue(TemperatureConverter.celsiusToRankine(tempValue))
                      this.temperatureUnits[4].setValue(TemperatureConverter.celsiusToReaumur(tempValue))
                      break;
                    case 1:
                      this.temperatureUnits[0].setValue(TemperatureConverter.fahrenheitToCelsius(tempValue))
                      this.temperatureUnits[2].setValue(TemperatureConverter.fahrenheitToKelvin(tempValue))
                      this.temperatureUnits[3].setValue(TemperatureConverter.fahrenheitToRankine(tempValue))
                      this.temperatureUnits[4].setValue(TemperatureConverter.fahrenheitToReaumur(tempValue))
                      break;
                    case 2:
                      this.temperatureUnits[0].setValue(TemperatureConverter.kelvinToCelsius(tempValue))
                      this.temperatureUnits[1].setValue(TemperatureConverter.kelvinToFahrenheit(tempValue))
                      this.temperatureUnits[3].setValue(TemperatureConverter.kelvinToRankine(tempValue))
                      this.temperatureUnits[4].setValue(TemperatureConverter.kelvinToReaumur(tempValue))
                      break;
                    case 3:
                      this.temperatureUnits[0].setValue(TemperatureConverter.rankineToCelsius(tempValue))
                      this.temperatureUnits[1].setValue(TemperatureConverter.rankineToFahrenheit(tempValue))
                      this.temperatureUnits[2].setValue(TemperatureConverter.rankineToKelvin(tempValue))
                      this.temperatureUnits[4].setValue(TemperatureConverter.rankineToReaumur(tempValue))
                      break;
                    case 4:
                      this.temperatureUnits[0].setValue(TemperatureConverter.reaumurToCelsius(tempValue))
                      this.temperatureUnits[1].setValue(TemperatureConverter.reaumurToFahrenheit(tempValue))
                      this.temperatureUnits[2].setValue(TemperatureConverter.reaumurToKelvin(tempValue))
                      this.temperatureUnits[3].setValue(TemperatureConverter.reaumurToRankine(tempValue))
                      break;
                  }
                });
            }
            .padding(`${this.basePadding / 2}lpx`) // 设置内边距
            .backgroundColor(unit.isInputFocused ? this.bgColor : Color.Transparent) // 设置背景颜色
            .layoutWeight(1) // 设置布局权重
            .height(40) // 设置高度
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角
            .borderColor(unit.isInputFocused ? this.placeholderColor : this.secondaryColor) // 设置边框颜色
            .margin({ left: `${this.basePadding / 2}lpx`, right: `${this.basePadding / 2}lpx` }); // 设置外边距
          }.margin({ top: `${this.basePadding / 2}lpx`, bottom: `${this.basePadding / 2}lpx` }); // 设置外边距
        })
      }
      .alignItems(HorizontalAlign.Start) // 设置水平对齐方式
      .width('650lpx') // 设置宽度
      .padding(`${this.basePadding}lpx`) // 设置内边距
      .margin({ top: `${this.basePadding}lpx` }) // 设置外边距
      .borderRadius(10) // 设置圆角
      .backgroundColor(Color.White) // 设置背景颜色
      .shadow({ // 添加阴影效果
        radius: 10, // 阴影半径
        color: this.secondaryColor, // 阴影颜色
        offsetX: 0, // X轴偏移量
        offsetY: 0 // Y轴偏移量
      });

      // 添加工具介绍部分
      Column() {
        // 添加标题
        Text('工具介绍').fontSize(this.fontSizeLarge).fontWeight(600).fontColor(this.primaryColor);

        // 添加工具介绍的文本
        Text('这款温度单位转换工具专为满足您在科学研究、日常生活及工作中的需求而设计。借助此工具,您可以轻松实现摄氏度(Celsius)、华氏度(Fahrenheit)和开尔文(Kelvin)之间的无缝切换。无论是学术研究、日常应用还是专业工作,都能为您提供精准便捷的温度换算服务。')
          .textAlign(TextAlign.JUSTIFY) // 设置文本对齐方式
          .fontSize(this.fontSizeSmall) // 设置字体大小
          .fontColor(this.primaryColor) // 设置字体颜色
          .margin({ top: `${this.basePadding / 2}lpx` }); // 设置外边距
      }
      .alignItems(HorizontalAlign.Start) // 设置水平对齐方式
      .width('650lpx') // 设置宽度
      .padding(`${this.basePadding}lpx`) // 设置内边距
      .margin({ top: `${this.basePadding}lpx` }) // 设置外边距
      .borderRadius(10) // 设置圆角
      .backgroundColor(Color.White) // 设置背景颜色
      .shadow({ // 添加阴影效果
        radius: 10, // 阴影半径
        color: this.secondaryColor, // 阴影颜色
        offsetX: 0, // X轴偏移量
        offsetY: 0 // Y轴偏移量
      });
    }
    .height('100%') // 设置高度
    .width('100%') // 设置宽度
    .backgroundColor(this.bgColor); // 设置背景颜色
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容