
【引言】
在鴻蒙NEXT平臺(tái)上,我們可以輕松地開發(fā)出一個(gè)經(jīng)緯度距離計(jì)算器,幫助用戶快速計(jì)算兩點(diǎn)之間的距離。本文將詳細(xì)介紹如何在鴻蒙NEXT中實(shí)現(xiàn)這一功能,通過簡單的用戶界面和高效的計(jì)算邏輯,為用戶提供便捷的服務(wù)。
【環(huán)境準(zhǔn)備】
? 操作系統(tǒng):Windows 10
? 開發(fā)工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
? 目標(biāo)設(shè)備:華為Mate60 Pro
? 開發(fā)語言:ArkTS
? 框架:ArkUI
? API版本:API 12
【思路】
在本案例中,我們將創(chuàng)建一個(gè)名為“距離計(jì)算器”的組件,用戶可以輸入起點(diǎn)和終點(diǎn)的經(jīng)緯度,系統(tǒng)將自動(dòng)計(jì)算并顯示兩點(diǎn)之間的距離。以下是實(shí)現(xiàn)的主要思路:
1 組件結(jié)構(gòu)設(shè)計(jì):
使用Column和Row布局組件來組織界面元素,使其具有良好的可讀性和用戶體驗(yàn)。在界面頂部添加標(biāo)題,明確應(yīng)用的功能。
2 輸入?yún)^(qū)域:
提供兩個(gè)輸入框,分別用于輸入起點(diǎn)和終點(diǎn)的經(jīng)緯度。用戶可以手動(dòng)輸入,也可以通過點(diǎn)擊示例按鈕快速填充常用位置(如北京和上海)。設(shè)計(jì)清空按鈕,方便用戶快速重置輸入。
3 狀態(tài)管理:
使用@State裝飾器管理組件的狀態(tài),包括輸入框的聚焦?fàn)顟B(tài)、經(jīng)緯度值和計(jì)算結(jié)果。通過@Watch裝飾器監(jiān)視輸入變化,確保在用戶輸入經(jīng)緯度時(shí),能夠?qū)崟r(shí)更新計(jì)算結(jié)果。
4 距離計(jì)算邏輯:在輸入變化時(shí),調(diào)用地圖模塊的calculateDistance方法,計(jì)算兩點(diǎn)之間的距離,并將結(jié)果更新到界面上。結(jié)果以公里為單位顯示,確保用戶能夠直觀理解計(jì)算結(jié)果。
5 界面美化:通過設(shè)置顏色、邊框、圓角等樣式,使界面更加美觀和用戶友好。使用適當(dāng)?shù)淖煮w和大小,確保信息的清晰可讀。
【完整代碼】
import { mapCommon } from '@kit.MapKit'; // 導(dǎo)入地圖通用模塊
import { map } from '@kit.MapKit'; // 導(dǎo)入地圖模塊
@Entry // 入口裝飾器,標(biāo)識(shí)該組件為應(yīng)用的入口
@Component // 組件裝飾器,定義一個(gè)組件
struct DistanceCalculator { // 定義一個(gè)名為 DistanceCalculator 的結(jié)構(gòu)體
@State private primaryColor: string = '#fea024'; // 定義主題顏色,初始值為橙色
@State private fontColor: string = "#2e2e2e"; // 定義字體顏色,初始值為深灰色
@State private isStartFocused: boolean = false; // 定義起點(diǎn)輸入框的聚焦?fàn)顟B(tài),初始為 false
@State private isEndFocused: boolean = false; // 定義終點(diǎn)輸入框的聚焦?fàn)顟B(tài),初始為 false
@State private isSecondStartFocused: boolean = false; // 定義第二起點(diǎn)輸入框的聚焦?fàn)顟B(tài),初始為 false
@State private isSecondEndFocused: boolean = false; // 定義第二終點(diǎn)輸入框的聚焦?fàn)顟B(tài),初始為 false
@State private baseSpacing: number = 30; // 定義基礎(chǔ)間距,初始值為 30
@State @Watch('onInputChange') private startLongitude: string = ""; // 定義起點(diǎn)經(jīng)度,初始為空,并監(jiān)視輸入變化
@State @Watch('onInputChange') private startLatitude: string = ""; // 定義起點(diǎn)緯度,初始為空,并監(jiān)視輸入變化
@State @Watch('onInputChange') private endLongitude: string = ""; // 定義終點(diǎn)經(jīng)度,初始為空,并監(jiān)視輸入變化
@State @Watch('onInputChange') private endLatitude: string = ""; // 定義終點(diǎn)緯度,初始為空,并監(jiān)視輸入變化
@State distance: number = 0; // 定義兩點(diǎn)之間的距離,初始值為 0
aboutToAppear(): void { // 生命周期鉤子函數(shù),組件即將顯示時(shí)調(diào)用
this.onInputChange(); // 調(diào)用輸入變化處理函數(shù)以初始化
}
onInputChange() { // 輸入變化處理函數(shù)
let fromLatLng: mapCommon.LatLng = { // 創(chuàng)建起點(diǎn)經(jīng)緯度對(duì)象
latitude: Number(this.startLatitude), // 將起點(diǎn)緯度轉(zhuǎn)換為數(shù)字
longitude: Number(this.startLongitude) // 將起點(diǎn)經(jīng)度轉(zhuǎn)換為數(shù)字
};
let toLatLng: mapCommon.LatLng = { // 創(chuàng)建終點(diǎn)經(jīng)緯度對(duì)象
latitude: Number(this.endLatitude), // 將終點(diǎn)緯度轉(zhuǎn)換為數(shù)字
longitude: Number(this.endLongitude) // 將終點(diǎn)經(jīng)度轉(zhuǎn)換為數(shù)字
};
this.distance = map.calculateDistance(fromLatLng, toLatLng); // 計(jì)算起點(diǎn)和終點(diǎn)之間的距離
}
build() { // 構(gòu)建界面函數(shù)
Column() { // 垂直布局容器
// 標(biāo)題欄,展示應(yīng)用名
Text("經(jīng)緯度距離計(jì)算") // 創(chuàng)建文本組件,顯示標(biāo)題
.width('100%') // 設(shè)置寬度為 100%
.height(54) // 設(shè)置高度為 54 像素
.fontSize(18) // 設(shè)置字體大小為 18
.fontWeight(600) // 設(shè)置字體粗細(xì)為 600
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.textAlign(TextAlign.Center) // 設(shè)置文本對(duì)齊方式為居中
.fontColor(this.fontColor); // 設(shè)置字體顏色為定義的字體顏色
// 輸入?yún)^(qū)域
Column() { // 垂直布局容器
Row() { // 水平布局容器
Text('示例(北京-->上海)') // 創(chuàng)建文本組件,顯示示例信息
.fontColor("#5871ce") // 設(shè)置字體顏色為藍(lán)色
.fontSize(18) // 設(shè)置字體大小為 18
.padding(`${this.baseSpacing / 2}lpx`) // 設(shè)置內(nèi)邊距
.backgroundColor("#f2f1fd") // 設(shè)置背景顏色
.borderRadius(5) // 設(shè)置圓角半徑為 5
.clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 設(shè)置點(diǎn)擊效果
.onClick(() => { // 點(diǎn)擊事件處理
this.startLongitude = "116.4074"; // 設(shè)置起點(diǎn)經(jīng)度為北京經(jīng)度
this.startLatitude = "39.9042"; // 設(shè)置起點(diǎn)緯度為北京緯度
this.endLongitude = "121.4737"; // 設(shè)置終點(diǎn)經(jīng)度為上海經(jīng)度
this.endLatitude = "31.2304"; // 設(shè)置終點(diǎn)緯度為上海緯度
});
Blank(); // 占位符,用于占據(jù)空間
Text('清空') // 創(chuàng)建文本組件,顯示“清空”按鈕
.fontColor("#e48742") // 設(shè)置字體顏色為橙色
.fontSize(18) // 設(shè)置字體大小為 18
.padding(`${this.baseSpacing / 2}lpx`) // 設(shè)置內(nèi)邊距
.clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 設(shè)置點(diǎn)擊效果
.backgroundColor("#ffefe6") // 設(shè)置背景顏色
.borderRadius(5) // 設(shè)置圓角半徑為 5
.onClick(() => { // 點(diǎn)擊事件處理
this.startLongitude = ""; // 清空起點(diǎn)經(jīng)度
this.startLatitude = ""; // 清空起點(diǎn)緯度
this.endLongitude = ""; // 清空終點(diǎn)經(jīng)度
this.endLatitude = ""; // 清空終點(diǎn)緯度
});
}.height(45) // 設(shè)置行高為 45 像素
.justifyContent(FlexAlign.SpaceBetween) // 設(shè)置子元素在主軸上的對(duì)齊方式
.width('100%'); // 設(shè)置寬度為 100%
Divider().margin({ top: 5, bottom: 5 }); // 創(chuàng)建分隔符,設(shè)置上下邊距
// 起點(diǎn)輸入
Row() { // 水平布局容器
Text('起點(diǎn)') // 創(chuàng)建文本組件,顯示“起點(diǎn)”
.fontWeight(600) // 設(shè)置字體粗細(xì)為 600
.fontSize(18) // 設(shè)置字體大小為 18
.fontColor(this.fontColor); // 設(shè)置字體顏色為定義的字體顏色
}
.margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); // 設(shè)置上下邊距
Row() { // 水平布局容器
TextInput({ text: $$this.startLongitude, placeholder: '經(jīng)度' }) // 創(chuàng)建起點(diǎn)經(jīng)度輸入框
.caretColor(this.primaryColor) // 設(shè)置光標(biāo)顏色為主題顏色
.layoutWeight(1) // 設(shè)置布局權(quán)重
.type(InputType.NUMBER_DECIMAL) // 設(shè)置輸入類型為小數(shù)
.placeholderColor(this.isStartFocused ? this.primaryColor : Color.Gray) // 設(shè)置占位符顏色
.fontColor(this.isStartFocused ? this.primaryColor : this.fontColor) // 設(shè)置字體顏色
.borderColor(this.isStartFocused ? this.primaryColor : Color.Gray) // 設(shè)置邊框顏色
.borderWidth(1) // 設(shè)置邊框?qū)挾? .borderRadius(10) // 設(shè)置圓角半徑為 10
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.showUnderline(false) // 不顯示下劃線
.onBlur(() => this.isStartFocused = false) // 失去焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 false
.onFocus(() => this.isStartFocused = true); // 獲得焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 true
Line().width(10); // 創(chuàng)建分隔符,設(shè)置寬度為 10 像素
TextInput({ text: $$this.startLatitude, placeholder: '緯度' }) // 創(chuàng)建起點(diǎn)緯度輸入框
.caretColor(this.primaryColor) // 設(shè)置光標(biāo)顏色為主題顏色
.layoutWeight(1) // 設(shè)置布局權(quán)重
.type(InputType.NUMBER_DECIMAL) // 設(shè)置輸入類型為小數(shù)
.placeholderColor(this.isEndFocused ? this.primaryColor : Color.Gray) // 設(shè)置占位符顏色
.fontColor(this.isEndFocused ? this.primaryColor : this.fontColor) // 設(shè)置字體顏色
.borderColor(this.isEndFocused ? this.primaryColor : Color.Gray) // 設(shè)置邊框顏色
.borderWidth(1) // 設(shè)置邊框?qū)挾? .borderRadius(10) // 設(shè)置圓角半徑為 10
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.showUnderline(false) // 不顯示下劃線
.onBlur(() => this.isEndFocused = false) // 失去焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 false
.onFocus(() => this.isEndFocused = true); // 獲得焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 true
}
// 終點(diǎn)輸入
Text('終點(diǎn)') // 創(chuàng)建文本組件,顯示“終點(diǎn)”
.fontWeight(600) // 設(shè)置字體粗細(xì)為 600
.fontSize(18) // 設(shè)置字體大小為 18
.fontColor(this.fontColor) // 設(shè)置字體顏色為定義的字體顏色
.margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); // 設(shè)置上下邊距
Row() { // 水平布局容器
TextInput({ text: $$this.endLongitude, placeholder: '經(jīng)度' }) // 創(chuàng)建終點(diǎn)經(jīng)度輸入框
.caretColor(this.primaryColor) // 設(shè)置光標(biāo)顏色為主題顏色
.layoutWeight(1) // 設(shè)置布局權(quán)重
.type(InputType.NUMBER_DECIMAL) // 設(shè)置輸入類型為小數(shù)
.placeholderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray) // 設(shè)置占位符顏色
.fontColor(this.isSecondStartFocused ? this.primaryColor : this.fontColor) // 設(shè)置字體顏色
.borderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray) // 設(shè)置邊框顏色
.borderWidth(1) // 設(shè)置邊框?qū)挾? .borderRadius(10) // 設(shè)置圓角半徑為 10
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.showUnderline(false) // 不顯示下劃線
.onBlur(() => this.isSecondStartFocused = false) // 失去焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 false
.onFocus(() => this.isSecondStartFocused = true); // 獲得焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 true
Line().width(10); // 創(chuàng)建分隔符,設(shè)置寬度為 10 像素
TextInput({ text: $$this.endLatitude, placeholder: '緯度' }) // 創(chuàng)建終點(diǎn)緯度輸入框
.caretColor(this.primaryColor) // 設(shè)置光標(biāo)顏色為主題顏色
.layoutWeight(1) // 設(shè)置布局權(quán)重
.type(InputType.NUMBER_DECIMAL) // 設(shè)置輸入類型為小數(shù)
.placeholderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray) // 設(shè)置占位符顏色
.fontColor(this.isSecondEndFocused ? this.primaryColor : this.fontColor) // 設(shè)置字體顏色
.borderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray) // 設(shè)置邊框顏色
.borderWidth(1) // 設(shè)置邊框?qū)挾? .borderRadius(10) // 設(shè)置圓角半徑為 10
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.showUnderline(false) // 不顯示下劃線
.onBlur(() => this.isSecondEndFocused = false) // 失去焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 false
.onFocus(() => this.isSecondEndFocused = true); // 獲得焦點(diǎn)時(shí)設(shè)置聚焦?fàn)顟B(tài)為 true
}
}
.width('650lpx') // 設(shè)置輸入?yún)^(qū)域?qū)挾葹?650 像素
.padding(`${this.baseSpacing}lpx`) // 設(shè)置內(nèi)邊距
.margin({ top: 20 }) // 設(shè)置上邊距為 20 像素
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.borderRadius(10) // 設(shè)置圓角半徑為 10
.alignItems(HorizontalAlign.Start); // 設(shè)置子元素在交叉軸上的對(duì)齊方式
// 顯示計(jì)算結(jié)果
Column() { // 垂直布局容器
Text() { // 文本組件
Span(`兩點(diǎn)之間的距離是:`) // 創(chuàng)建文本片段,顯示提示信息
Span(`${(this.distance / 1000).toFixed(2)} `).fontColor(this.primaryColor) // 創(chuàng)建文本片段,顯示距離(公里),并設(shè)置顏色
Span(`公里`) // 創(chuàng)建文本片段,顯示單位“公里”
}
.fontWeight(600) // 設(shè)置字體粗細(xì)為 600
.fontSize(18) // 設(shè)置字體大小為 18
.fontColor(this.fontColor); // 設(shè)置字體顏色為定義的字體顏色
}
.width('650lpx') // 設(shè)置結(jié)果顯示區(qū)域?qū)挾葹?650 像素
.backgroundColor(Color.White) // 設(shè)置背景顏色為白色
.borderRadius(10) // 設(shè)置圓角半徑為 10
.padding(`${this.baseSpacing}lpx`) // 設(shè)置內(nèi)邊距
.margin({ top: `${this.baseSpacing}lpx` }) // 設(shè)置上邊距
.alignItems(HorizontalAlign.Start); // 設(shè)置子元素在交叉軸上的對(duì)齊方式
}
.height('100%') // 設(shè)置整個(gè)組件高度為 100%
.width('100%') // 設(shè)置整個(gè)組件寬度為 100%
.backgroundColor("#eff0f3"); // 設(shè)置背景顏色為淺灰色
}
}