鸿蒙NEXT开发案例:经纬度距离计算

c.gif

【引言】

在鸿蒙NEXT平台上,我们可以轻松地开发出一个经纬度距离计算器,帮助用户快速计算两点之间的距离。本文将详细介绍如何在鸿蒙NEXT中实现这一功能,通过简单的用户界面和高效的计算逻辑,为用户提供便捷的服务。

【环境准备】

• 操作系统:Windows 10

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

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 12

【思路】

在本案例中,我们将创建一个名为“距离计算器”的组件,用户可以输入起点和终点的经纬度,系统将自动计算并显示两点之间的距离。以下是实现的主要思路:

1 组件结构设计:

使用Column和Row布局组件来组织界面元素,使其具有良好的可读性和用户体验。在界面顶部添加标题,明确应用的功能。

2 输入区域:

提供两个输入框,分别用于输入起点和终点的经纬度。用户可以手动输入,也可以通过点击示例按钮快速填充常用位置(如北京和上海)。设计清空按钮,方便用户快速重置输入。

3 状态管理:

使用@State装饰器管理组件的状态,包括输入框的聚焦状态、经纬度值和计算结果。通过@Watch装饰器监视输入变化,确保在用户输入经纬度时,能够实时更新计算结果。

4 距离计算逻辑:在输入变化时,调用地图模块的calculateDistance方法,计算两点之间的距离,并将结果更新到界面上。结果以公里为单位显示,确保用户能够直观理解计算结果。

5 界面美化:通过设置颜色、边框、圆角等样式,使界面更加美观和用户友好。使用适当的字体和大小,确保信息的清晰可读。

【完整代码】

import { mapCommon } from '@kit.MapKit'; // 导入地图通用模块
import { map } from '@kit.MapKit'; // 导入地图模块

@Entry // 入口装饰器,标识该组件为应用的入口
@Component // 组件装饰器,定义一个组件
struct DistanceCalculator { // 定义一个名为 DistanceCalculator 的结构体
  @State private primaryColor: string = '#fea024'; // 定义主题颜色,初始值为橙色
  @State private fontColor: string = "#2e2e2e"; // 定义字体颜色,初始值为深灰色
  @State private isStartFocused: boolean = false; // 定义起点输入框的聚焦状态,初始为 false
  @State private isEndFocused: boolean = false; // 定义终点输入框的聚焦状态,初始为 false
  @State private isSecondStartFocused: boolean = false; // 定义第二起点输入框的聚焦状态,初始为 false
  @State private isSecondEndFocused: boolean = false; // 定义第二终点输入框的聚焦状态,初始为 false
  @State private baseSpacing: number = 30; // 定义基础间距,初始值为 30
  @State @Watch('onInputChange') private startLongitude: string = ""; // 定义起点经度,初始为空,并监视输入变化
  @State @Watch('onInputChange') private startLatitude: string = ""; // 定义起点纬度,初始为空,并监视输入变化
  @State @Watch('onInputChange') private endLongitude: string = ""; // 定义终点经度,初始为空,并监视输入变化
  @State @Watch('onInputChange') private endLatitude: string = ""; // 定义终点纬度,初始为空,并监视输入变化
  @State distance: number = 0; // 定义两点之间的距离,初始值为 0

  aboutToAppear(): void { // 生命周期钩子函数,组件即将显示时调用
    this.onInputChange(); // 调用输入变化处理函数以初始化
  }

  onInputChange() { // 输入变化处理函数
    let fromLatLng: mapCommon.LatLng = { // 创建起点经纬度对象
      latitude: Number(this.startLatitude), // 将起点纬度转换为数字
      longitude: Number(this.startLongitude) // 将起点经度转换为数字
    };
    let toLatLng: mapCommon.LatLng = { // 创建终点经纬度对象
      latitude: Number(this.endLatitude), // 将终点纬度转换为数字
      longitude: Number(this.endLongitude) // 将终点经度转换为数字
    };
    this.distance = map.calculateDistance(fromLatLng, toLatLng); // 计算起点和终点之间的距离
  }

  build() { // 构建界面函数
    Column() { // 垂直布局容器
      // 标题栏,展示应用名
      Text("经纬度距离计算") // 创建文本组件,显示标题
        .width('100%') // 设置宽度为 100%
        .height(54) // 设置高度为 54 像素
        .fontSize(18) // 设置字体大小为 18
        .fontWeight(600) // 设置字体粗细为 600
        .backgroundColor(Color.White) // 设置背景颜色为白色
        .textAlign(TextAlign.Center) // 设置文本对齐方式为居中
        .fontColor(this.fontColor); // 设置字体颜色为定义的字体颜色

      // 输入区域
      Column() { // 垂直布局容器
        Row() { // 水平布局容器
          Text('示例(北京-->上海)') // 创建文本组件,显示示例信息
            .fontColor("#5871ce") // 设置字体颜色为蓝色
            .fontSize(18) // 设置字体大小为 18
            .padding(`${this.baseSpacing / 2}lpx`) // 设置内边距
            .backgroundColor("#f2f1fd") // 设置背景颜色
            .borderRadius(5) // 设置圆角半径为 5
            .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 设置点击效果
            .onClick(() => { // 点击事件处理
              this.startLongitude = "116.4074"; // 设置起点经度为北京经度
              this.startLatitude = "39.9042"; // 设置起点纬度为北京纬度
              this.endLongitude = "121.4737"; // 设置终点经度为上海经度
              this.endLatitude = "31.2304"; // 设置终点纬度为上海纬度
            });
          Blank(); // 占位符,用于占据空间
          Text('清空') // 创建文本组件,显示“清空”按钮
            .fontColor("#e48742") // 设置字体颜色为橙色
            .fontSize(18) // 设置字体大小为 18
            .padding(`${this.baseSpacing / 2}lpx`) // 设置内边距
            .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 设置点击效果
            .backgroundColor("#ffefe6") // 设置背景颜色
            .borderRadius(5) // 设置圆角半径为 5
            .onClick(() => { // 点击事件处理
              this.startLongitude = ""; // 清空起点经度
              this.startLatitude = ""; // 清空起点纬度
              this.endLongitude = ""; // 清空终点经度
              this.endLatitude = ""; // 清空终点纬度
            });
        }.height(45) // 设置行高为 45 像素
        .justifyContent(FlexAlign.SpaceBetween) // 设置子元素在主轴上的对齐方式
        .width('100%'); // 设置宽度为 100%

        Divider().margin({ top: 5, bottom: 5 }); // 创建分隔符,设置上下边距

        // 起点输入
        Row() { // 水平布局容器
          Text('起点') // 创建文本组件,显示“起点”
            .fontWeight(600) // 设置字体粗细为 600
            .fontSize(18) // 设置字体大小为 18
            .fontColor(this.fontColor); // 设置字体颜色为定义的字体颜色
        }
        .margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); // 设置上下边距

        Row() { // 水平布局容器
          TextInput({ text: $$this.startLongitude, placeholder: '经度' }) // 创建起点经度输入框
            .caretColor(this.primaryColor) // 设置光标颜色为主题颜色
            .layoutWeight(1) // 设置布局权重
            .type(InputType.NUMBER_DECIMAL) // 设置输入类型为小数
            .placeholderColor(this.isStartFocused ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isStartFocused ? this.primaryColor : this.fontColor) // 设置字体颜色
            .borderColor(this.isStartFocused ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径为 10
            .backgroundColor(Color.White) // 设置背景颜色为白色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isStartFocused = false) // 失去焦点时设置聚焦状态为 false
            .onFocus(() => this.isStartFocused = true); // 获得焦点时设置聚焦状态为 true

          Line().width(10); // 创建分隔符,设置宽度为 10 像素

          TextInput({ text: $$this.startLatitude, placeholder: '纬度' }) // 创建起点纬度输入框
            .caretColor(this.primaryColor) // 设置光标颜色为主题颜色
            .layoutWeight(1) // 设置布局权重
            .type(InputType.NUMBER_DECIMAL) // 设置输入类型为小数
            .placeholderColor(this.isEndFocused ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isEndFocused ? this.primaryColor : this.fontColor) // 设置字体颜色
            .borderColor(this.isEndFocused ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径为 10
            .backgroundColor(Color.White) // 设置背景颜色为白色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isEndFocused = false) // 失去焦点时设置聚焦状态为 false
            .onFocus(() => this.isEndFocused = true); // 获得焦点时设置聚焦状态为 true
        }

        // 终点输入
        Text('终点') // 创建文本组件,显示“终点”
          .fontWeight(600) // 设置字体粗细为 600
          .fontSize(18) // 设置字体大小为 18
          .fontColor(this.fontColor) // 设置字体颜色为定义的字体颜色
          .margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); // 设置上下边距

        Row() { // 水平布局容器
          TextInput({ text: $$this.endLongitude, placeholder: '经度' }) // 创建终点经度输入框
            .caretColor(this.primaryColor) // 设置光标颜色为主题颜色
            .layoutWeight(1) // 设置布局权重
            .type(InputType.NUMBER_DECIMAL) // 设置输入类型为小数
            .placeholderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isSecondStartFocused ? this.primaryColor : this.fontColor) // 设置字体颜色
            .borderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径为 10
            .backgroundColor(Color.White) // 设置背景颜色为白色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isSecondStartFocused = false) // 失去焦点时设置聚焦状态为 false
            .onFocus(() => this.isSecondStartFocused = true); // 获得焦点时设置聚焦状态为 true

          Line().width(10); // 创建分隔符,设置宽度为 10 像素

          TextInput({ text: $$this.endLatitude, placeholder: '纬度' }) // 创建终点纬度输入框
            .caretColor(this.primaryColor) // 设置光标颜色为主题颜色
            .layoutWeight(1) // 设置布局权重
            .type(InputType.NUMBER_DECIMAL) // 设置输入类型为小数
            .placeholderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isSecondEndFocused ? this.primaryColor : this.fontColor) // 设置字体颜色
            .borderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径为 10
            .backgroundColor(Color.White) // 设置背景颜色为白色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isSecondEndFocused = false) // 失去焦点时设置聚焦状态为 false
            .onFocus(() => this.isSecondEndFocused = true); // 获得焦点时设置聚焦状态为 true
        }
      }
      .width('650lpx') // 设置输入区域宽度为 650 像素
      .padding(`${this.baseSpacing}lpx`) // 设置内边距
      .margin({ top: 20 }) // 设置上边距为 20 像素
      .backgroundColor(Color.White) // 设置背景颜色为白色
      .borderRadius(10) // 设置圆角半径为 10
      .alignItems(HorizontalAlign.Start); // 设置子元素在交叉轴上的对齐方式

      // 显示计算结果
      Column() { // 垂直布局容器
        Text() { // 文本组件
          Span(`两点之间的距离是:`) // 创建文本片段,显示提示信息
          Span(`${(this.distance / 1000).toFixed(2)} `).fontColor(this.primaryColor) // 创建文本片段,显示距离(公里),并设置颜色
          Span(`公里`) // 创建文本片段,显示单位“公里”
        }
        .fontWeight(600) // 设置字体粗细为 600
        .fontSize(18) // 设置字体大小为 18
        .fontColor(this.fontColor); // 设置字体颜色为定义的字体颜色
      }
      .width('650lpx') // 设置结果显示区域宽度为 650 像素
      .backgroundColor(Color.White) // 设置背景颜色为白色
      .borderRadius(10) // 设置圆角半径为 10
      .padding(`${this.baseSpacing}lpx`) // 设置内边距
      .margin({ top: `${this.baseSpacing}lpx` }) // 设置上边距
      .alignItems(HorizontalAlign.Start); // 设置子元素在交叉轴上的对齐方式
    }
    .height('100%') // 设置整个组件高度为 100%
    .width('100%') // 设置整个组件宽度为 100%
    .backgroundColor("#eff0f3"); // 设置背景颜色为浅灰色
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容