一、四签名拿到client ID
前提准备工作
在DevEco Studio上外面可以获得两个证书
注意:在DevEco Studio上刚才申请两个证书的账号和密码一定要记住,我是放在了新建文件夹里
这个是第三个证书, 然后再绑定应用获得AGC平台颁发的p7b证书,这样我们就可以拿到client ID了(注意我们要拿的是应用的ID),记得点击添加公钥指纹
点击下载获得第四个证书
勾上这些API,然后在DevEco Studio配置ID和证书
这个是写在modules.json5
二、location kit 和 map kit
功能优点:用户可以查看自己的位置,和景点的位置(marker标记),详细地图方便了解具体位置
这里需要打开定位权限,需要权限控制模板的可以查看我上一篇文章
第一个是精准定位以米级别的,模糊定位是5公里级别的,我们这个权限是user——grant级别的,这个需要弹窗授权,框口中可以设置精准定位还是模糊定位(当然这里还有一个后台定位的权限,这里暂时不需要)
- 这个时候我们就可以通过
geoLocationManager
这个API获得自身的定位信息了 - 关于定位信息我这边给一个稍微详细点的代码
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit'
import { geoLocationManager } from '@kit.LocationKit'
import json from '@ohos.util.json'
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Index {
@State isGrant:boolean = false
@State location:geoLocationManager.Location | null = null
async aboutToAppear(){
this.isGrant = await this.requestPermissions(["ohos.permission.LOCATION","ohos.permission.APPROXIMATELY_LOCATION"])
}
async requestPermissions(permissions:Permissions[]){
const atManager = abilityAccessCtrl.createAtManager()
const requestResult = await atManager.requestPermissionsFromUser(getContext(),permissions)
const isAuth=requestResult.authResults.every(item=>item===abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
return isAuth === true ? Promise.resolve(true) : Promise.reject(false)
}
build() {
Navigation(){
Column({space: 10}){
Text('位置服务是否开启:'+this.isGrant)
Text('当前的位置信息:'+json.stringify(this.location,null,2))
Button('获取当前位置').onClick(async ()=>{
try{
this.location = await geoLocationManager.getCurrentLocation()
}catch(err){
promptAction.showToast({message:'请再控制中心,开启定位服务'})
}
})
Button('开启位置变化订阅').onClick(()=>{
geoLocationManager.on('locationChange',
{
priority: geoLocationManager.LocationRequestPriority.ACCURACY,//精度优先,卫星定位
scenario: geoLocationManager.LocationRequestScenario.NAVIGATION,//导航场景,卫星定位
timeInterval: 1000,//1s
distanceInterval: 10,//10m
},
(location)=>{this.location=location})
})
Button('关闭订阅').onClick(()=>{
geoLocationManager.off('locationChange')
})
Button('判断地理编码与逆地理编码是否可用').onClick(()=>{
const isAvailable = geoLocationManager.isGeocoderAvailable()
AlertDialog.show({ message:'是否支持:'+isAvailable})
})
Button('逆地理编码(逆解析)-坐标转位置').onClick(async ()=>{
const addresses = await geoLocationManager.getAddressesFromLocation({locale:'zh',latitude: 30.584355,longitude: 114.298572})//maxItems:1
AlertDialog.show({ message:JSON.stringify(addresses,null,2)})
})
Button('地理编码-位置转坐标').onClick(async ()=>{
const location = await geoLocationManager.getAddressesFromLocationName({description:'北京'})
AlertDialog.show({ message:JSON.stringify(location,null,2)})
})
}
.constraintSize({minHeight:'100%'})
}
.title('Location Kit 位置服务')
.titleMode(NavigationTitleMode.Mini)
}
}
注意:打开地图有几个前提:
- 1.四签名
- 2.应用的client ID
- 3.AGC平台勾选相关API
- 4.真机的UDID添加到AGC平台的设备中(差点漏了这个知识点,下面是获取真机UDID的步骤)
①打开这个文件路径,找那个最新的文件夹,然后点开里面的toolchains文件
②打开CDM终端复制hdc shell bm get --udid回车,复制UDID,在AGC平台添加设备
官方文档上面有一套map的模板代码,我们这边调整了一下,比如使用Marker标记旅游的地点,打开定位按钮。
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { geoLocationManager } from '@kit.LocationKit';
@Entry
@Component
struct HuaweiMapDemo {
private TAG = "HuaweiMapDemo";
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
private marker?: map.Marker;
async aboutToAppear(){
this.requestPermissions(["ohos.permission.LOCATION","ohos.permission.APPROXIMATELY_LOCATION"])
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOptions = {
position: {
target: {
latitude: 22.19,
longitude: 114.11
},
zoom: 10
},
myLocationControlsEnabled: true,
alwaysShowScaleEnabled:true
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
let location = await geoLocationManager.getCurrentLocation();
this.mapController.setMyLocation(location);
// 启用我的位置图层
this.mapController.setMyLocationEnabled(true);
// 启用我的位置按钮
this.mapController.setMyLocationControlsEnabled(true);
// let style: mapCommon.MyLocationStyle = {
// anchorU: 0.5,
// anchorV: 0.5,
// radiusFillColor: 0xffff0000,
// // icon为自定义图标资源,使用时需要替换
// // 图标存放在resources/rawfile,icon参数传入rawfile文件夹下的相对路径
// icon: 'images/logo.png'
// };
// await this.mapController.setMyLocationStyle(style);
this.mapEventManager = this.mapController.getEventManager();
//this.mapEventManager.on("myLocationButtonClick", callback);
// Marker初始化参数
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: 22.64,
longitude: 113.92
},
rotation: 0,
visible: true,
zIndex: 0,
alpha: 1,
anchorU: 0.5,
anchorV: 1,
clickable: true,
draggable: true,
flat: false,
// 图标存放在resources/rawfile,icon参数传入rawfile文件夹下的相对路径
icon: 'images/head.png'
};
// 创建Marker
this.marker = await this.mapController.addMarker(markerOptions);
// 设置信息窗的标题
this.marker.setTitle('深圳');
// 设置信息窗的子标题
this.marker.setSnippet('小黑程序员');
// 设置标记可点击
this.marker.setClickable(true);
// 设置信息窗的锚点位置
this.marker.setInfoWindowAnchor(1, 1);
// 设置信息窗可见
this.marker.setInfoWindowVisible(true);
// 地图加载完成事件
// let callback = () => {
// console.info(this.TAG, `on-mapLoad`);
// }
// this.mapEventManager.on("mapLoad", callback);
}
};
// let markerOptions:mapCommon.MarkerOptions = {
// position:{latitude:39.9,longitude:116.4}
// }
// let marker:map.Marker = await this.mapController?.addMarker(markerOptions)
// marker.
}
async requestPermissions(permissions:Permissions[]){
const atManager = abilityAccessCtrl.createAtManager()
const requestResult = await atManager.requestPermissionsFromUser(getContext(),permissions)
const isAuth=requestResult.authResults.every(item=>item===abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
return isAuth === true ? Promise.resolve(true) : Promise.reject(false)
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController) {
this.mapController.show();
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效
onPageHide(): void {
// 将地图切换到后台
if (this.mapController) {
this.mapController.hide();
}
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
}
三、文本转语音功能
功能优点:一个旅游景点会有一些相关的介绍,使用文本转语音功能使用户更加方便了解景点的相关信息
这里我们使用了相关的APItextToSpeech
来创建引擎然后使用textToSpeech.TextToSpeechEngine
里面的setListener
方法来播放文本
注意:每次播放完毕后要使用shutdown
方法释放引擎
let ttsEngine: textToSpeech.TextToSpeechEngine;
@Entry
...
// 创建引擎,通过callback形式返回
private createByCallback() {
// 设置创建引擎参数
let extraParam: Record<string, Object> = {"style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName'};
let initParamsInfo: textToSpeech.CreateEngineParams = {
language: 'zh-CN',
person: 0,
online: 1,
extraParams: extraParam
};
// 调用createEngine方法
textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {
if (!err) {
console.info('Succeeded in creating engine.');
// 接收创建引擎的实例
ttsEngine = textToSpeechEngine;
} else {
console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
}
});
};
// 调用speak播报方法
private speak() {
let speakListener: textToSpeech.SpeakListener = {
// 开始播报回调
onStart(requestId: string, response: textToSpeech.StartResponse) {
console.info(`onStart, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 完成播报回调
onComplete(requestId: string, response: textToSpeech.CompleteResponse) {
console.info(`onComplete, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 停止播报完成回调,调用stop方法并完成时会触发此回调
onStop(requestId: string, response: textToSpeech.StopResponse) {
console.info(`onStop, requestId: ${requestId} response: ${JSON.stringify(response)}`);
},
// 返回音频流
onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {
console.info(`onData, requestId: ${requestId} sequence: ${JSON.stringify(response)} audio: ${JSON.stringify(audio)}`);
},
// 错误回调,播报过程发生错误时触发此回调
onError(requestId: string, errorCode: number, errorMessage: string) {
console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);
}
};
// 设置回调
ttsEngine.setListener(speakListener);
// 设置播报相关参数
let extraParam: Record<string, Object> = {"queueMode": 0, "speed": 1, "volume": 2, "pitch": 1, "languageContext": 'zh-CN', "audioType": "pcm", "soundChannel": 3, "playType":1}
let speakParams: textToSpeech.SpeakParams = {
requestId: '123456-a', // requestId在同一实例内仅能用一次,请勿重复设置
extraParams: extraParam
};
// 调用speak播报方法
ttsEngine.speak(this.originalText, speakParams);
};
这里面的this.originalText
使用@State 状态变量来管理,当用户点击某个文本时复制成那个文本就可以了,是string类型的
- 核心:
- ①
this.createByCallback()
创建引擎 - ②
this.speak()
文本转语音播放 - ③
this.createByCallback()
释放
<这里MP4格式的视频传不了,语音功能效果不错,还有其他的语音相关调整功能,查询语种音色信息等,相关API和方法可以在官网上查询>
四、搞个AI小助手
功能优点:用户不懂的问题可以直接问AI,畅所欲言,协同旅行
UIAbility跳转,跳转页面可以查看我之前的文章,使用的是deepseek-v3的接口,接口很多,可以自己去搭建
.onClick(() => {
const context = getContext() as common.UIAbilityContext
context.startAbility({
bundleName: 'com.example.myapplication1_my_map_kit',
moduleName: 'entry',
abilityName: 'EntryAbilityAI'
})
})
评选规则用的rawfile路径,html页面,使用了webview的APIcontroller:new webview.WebviewController()
import { webview } from '@kit.ArkWeb'
@Entry
@Component
struct Index {
webctrl = new webview.WebviewController()
build() {
Column(){
Web({
src:$rawfile('mypages/index.html'),
controller: this.webctrl
})
}
.height('100%')
.width('100%')
}
}