6月13日讨论内容整理
一、Axios的相关知识点
src/main/ets/utils/http.ets:
import axios, { InternalAxiosRequestConfig, AxiosError, AxiosResponse, AxiosRequestConfig } from '@ohos/axios'
// 实例化 通用配置
const httpInstance = axios.create({
// 美蔻的基地址
baseURL: 'https://meikou-api.itheima.net/',
// 超时事件最长多久不响应,就认为是失败
timeout: 5000
})
// 拦截器配置
// 请求拦截器
// token配置等
httpInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
return config
}, (error: AxiosError) => {
return Promise.reject(error)
})
// 添加响应拦截器
// 错误统一处理等
httpInstance.interceptors.response.use((response: AxiosResponse) => {
return response
}, (error: AxiosError) => {
return Promise.reject(error)
})
// 通用的响应类型,外层都是相同的
// result 不同,利用泛型来动态设置即可
interface HttpResponse<T> {
code: string
msg: string
result: T // result通过泛型动态设置
}
// 通用的请求方法,将 类型,返回值,参数都封装到一起 简化调用
// AxiosRequestConfig<null> 参数类型
// Promise<AxiosResponse<HttpResponse<T>, null>> 返回值类型
// D=null 设置默认值
function fetchData<T, D = null>(config: AxiosRequestConfig<D>): Promise<AxiosResponse<HttpResponse<T>, null>> {
return httpInstance <null, AxiosResponse<HttpResponse<T>>, D>(config)
}
export { httpInstance, HttpResponse, fetchData }
在上面的代码中,我们可以对Axois的特性做一个分析
(1)Axios较http原生请求方式封装的区别:
a. http 请求 ⅰ. 基地址 ⅱ. 请求和响应拦截器 b. axios 二次封装 ⅰ. 基地址 ⅱ. 请求和响应拦截器 ⅲ. 查询参数传递:params ⅳ. 请求体参数传递:data
根据以上封装内容可知,Axios的封装中多了查询参数和请求体参数两部分
(2)对实操代码的分析可得
①Axios自带拦截器功能,主要有两种用途:可以进行token的拦截、错误代码的拦截
②http封装的内容构成:
a.实例化通用配置(设置基地址、超时响应时间)
b.拦截器
c.设置一个泛型接口HttpResponse
d.通用请求方法
e.将上面设置的一个常量、一个接口、一个函数方法导出
③通用请求方法fetchData<T,D=null>
其中T是响应数据的类型,D是请求数据的类型,默认是null
二、鸿蒙的生命周期相关概念
以下图表可以很好地表名生命周期函数的运行顺序及相关关系
具体知识参考文章《鸿蒙开发基础 - 关于生命周期钩子详解》:juejin.cn/post/737252…
其中容易忽略的一个生命周期函数:onBackPress()
使用onBackPress,做一个弹窗提醒用户,要return一个true阻断默认的返回我们项目里,在做笔记本相关的内容时,当点击退出按钮的时候会用到,作为防止用户误操作的提醒。
三、lazyForEach的使用方法
具体方法简要概括如下:
①在获取数据getData方法中正常使用forEach渲染数据的
②在提交给服务器的参数中设置要查询的页数pageSize
③将内部操作数据的方法类型,替换为我们希望的数据
这个我们希望的数据最后要实现的接口是IDataSource,在IDataSource的内置方法中设置有监听数据的注册与注销的函数,用于控制需要渲染出的数据长度
四、如何使用录音转文字功能
features/home/src/main/ets/components/AudioSearchComp.ets:
①设置3种录制状态,月用于控制系统运行功能
②设置开启录制、关闭录制方法;申请录制权限 (进入页面即执行)
③Button的长按手势的属性方法.gesture(LongPressGesture().方法),调用开启录制、关闭录制的方法 分为开启、结束、取消三种情况
features/home/src/main/ets/views/SearchView.ets:
④组件被调用
案例源码:
features/home/src/main/ets/components/AudioSearchComp.ets:
import { KeyboardAvoidMode, window } from '@kit.ArkUI';
import { audio } from '@kit.AudioKit';
import { speechRecognizer } from '@kit.CoreSpeechKit';
import { permissionPlugin } from '@mk/basic/Index';
// import { permissionPlugin } from '@mk/basic';
export enum VoiceState {
DEFAULT, // 默认
VOICING, // 录音
VOICEOVER // 录完了
}
@Component
export struct AudioSearchComp {
@State voiceState: VoiceState = VoiceState.DEFAULT
keyword: string = ''
audioCapturer: audio.AudioCapturer | null = null
asrEngine: speechRecognizer.SpeechRecognitionEngine | null = null
// 改变
onChange: (keyword: string) => void = () => {
}
// 完成
onComplete: (keyword: string) => void = () => {
}
// 开启录制
async startRecord() {
// 开始识别
this.asrEngine = await speechRecognizer.createEngine({
language: 'zh-CN',
online: 1
})
// 保存组件的 this,后续通过_this来使用组件
const _this = this
// 语音转换文字的回调函数
this.asrEngine.setListener({
onStart(sessionId: string, eventMessage: string) {
console.info(`onStart, sessionId: ${sessionId} eventMessage: ${eventMessage}`);
},
onEvent(sessionId: string, eventCode: number, eventMessage: string) {
console.info(`onEvent, sessionId: ${sessionId} eventCode: ${eventCode} eventMessage: ${eventMessage}`);
},
// 有转换结果了
onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult) {
console.info(`onResult, sessionId: ${sessionId} sessionId: ${JSON.stringify(result)}`);
// 把 录制结果传递出去即可,使用 上面保存的_this
_this.onChange(result.result)
_this.voiceState=VoiceState.VOICING
// 录音的文本保存到 keyword 变量中,后续即可通过 keyword 来获取
_this.keyword = result.result
},
// 转换完毕了
onComplete(sessionId: string, eventMessage: string) {
_this.voiceState = VoiceState.DEFAULT
_this.onComplete(_this.keyword)
console.info(`onComplete, sessionId: ${sessionId} eventMessage: ${eventMessage}`);
},
onError(sessionId: string, errorCode: number, errorMessage: string) {
console.error(`onError, sessionId: ${sessionId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);
}
})
const recognizerParams: speechRecognizer.StartParams = {
sessionId: '10000',
audioInfo: {
audioType: 'pcm',
sampleRate: 16000,
soundChannel: 1,
sampleBit: 16
}
}
// 开启监听 结合啥概念茶农的参数
this.asrEngine?.startListening(recognizerParams)
// 开始录音
const audioStreamInfo: audio.AudioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
}
const audioCapturerInfo: audio.AudioCapturerInfo = {
source: audio.SourceType.SOURCE_TYPE_MIC,
capturerFlags: 0
}
const audioCapturerOptions: audio.AudioCapturerOptions = {
streamInfo: audioStreamInfo,
capturerInfo: audioCapturerInfo
}
this.audioCapturer = await audio.createAudioCapturer(audioCapturerOptions)
// 录音的数据 传递给 语音转文字的工具
this.audioCapturer.on('readData', (buffer) => {
console.log('mk-logger', buffer.byteLength)
this.asrEngine?.writeAudio('10000', new Uint8Array(buffer))
})
await this.audioCapturer.start()
this.voiceState = VoiceState.VOICING
}
// 关闭录制
async closeRecord() {
this.audioCapturer?.stop()
this.audioCapturer?.release()
this.asrEngine?.finish('10000')
this.asrEngine?.cancel('10000')
this.asrEngine?.shutdown()
if (this.keyword) {
this.voiceState = VoiceState.DEFAULT
this.keyword = ''
} else {
this.voiceState = VoiceState.VOICEOVER
}
}
aboutToAppear(): void {
permissionPlugin.requestPermissions([
'ohos.permission.MICROPHONE'
])
.then(() => {
window.getLastWindow(getContext())
.then(win => {
win.getUIContext()
.setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE)
})
})
// })
}
build() {
Column() {
// 根据状态 更改显示的文本
if (this.voiceState !== VoiceState.DEFAULT) {
Column({ space: 16 }) {
if (this.voiceState === VoiceState.VOICING) {
Text('请说,我在聆听...')
.fontSize(14)
} else if (this.voiceState === VoiceState.VOICEOVER && this.keyword === '') {
Text('未检测到语音,请长按按钮重试')
.fontSize(14)
}
Text() {
Span('你可以这样说:')
Span('太阳眼镜/冬款连衣裙')
.fontColor($r('app.color.gray'))
}
.fontSize(12)
}
.justifyContent(FlexAlign.Center)
.height(150)
}
Blank()
Button() {
Row({ space: 4 }) {
Image($r('sys.media.ohos_ic_public_voice'))
.width(16)
.aspectRatio(1)
.fillColor($r('app.color.white'))
if (this.voiceState === VoiceState.VOICING) {
Text('松开立即搜索')
.fontSize(14)
.fontColor($r('app.color.white'))
} else {
Text('长按语音搜索')
.fontSize(14)
.fontColor($r('app.color.white'))
}
}
}
.padding({ left: 12, right: 12 })
.height(36)
.linearGradient({ angle: 135, colors: [[$r('app.color.linear_begin'), 0], [$r('app.color.linear_end'), 1]] })
.margin({ bottom: 16 })
.gesture(LongPressGesture()// 长按手势
.onAction(() => {
this.startRecord()
// 开启
// this.voiceState = VoiceState.VOICING
})
.onActionEnd(() => {
this.closeRecord()
// 结束
// this.voiceState = VoiceState.VOICEOVER
// this.keyword = '么么哒'
})
.onActionCancel(() => {
this.closeRecord()
// 取消
}))
}
.layoutWeight(1)
.width('100%')
.backgroundImage($r('app.media.search_bg'))
.backgroundImageSize(ImageSize.Contain)
.backgroundImagePosition(Alignment.Bottom)
.onVisibleAreaChange([0, 1], () => {
this.keyword = ''
this.voiceState = VoiceState.DEFAULT
})
}
}
features/home/src/main/ets/views/SearchView.ets:
import { Log } from '@abner/log'
import { router, window } from '@kit.ArkUI'
import { AudioSearchComp } from '../components/AudioSearchComp'
@Entry
@Component
export struct SearchView {
@StorageProp('safeTop') safeTop: number = 0
// 关键词
@State keyword: string = '鞋子,帽子,裤子'
// 修改 状态栏的文字的颜色
aboutToAppear(): void {
window.getLastWindow(getContext())
.then((win) => {
win.setWindowSystemBarProperties({ statusBarContentColor: '#FFFFFF' })
})
}
aboutToDisappear(): void {
window.getLastWindow(getContext())
.then((win) => {
win.setWindowSystemBarProperties({ statusBarContentColor: '#000000' })
})
}
toSearchResult(value: string) {
router.pushUrl({
url: 'pages/SearchResultPage',
params: {
keyword: value
}
})
}
build() {
Column() {
// search
Row() {
Image($r('app.media.ic_public_left'))
.width(24)
.aspectRatio(1)
.fillColor($r('app.color.white'))
.margin(13)
.onClick(() => {
router.back()
})
Search({ placeholder: '商品关键字...', value: this.keyword })
.searchIcon({ src: $r('app.media.ic_public_search'), color: $r('app.color.gray') })// 搜索图标
.placeholderColor($r('app.color.gray'))// 提示文本颜色
.placeholderFont({ size: 14 })// 提示文本大小
.searchButton('搜索', { fontSize: 14, fontColor: $r('app.color.red') })// 搜索按钮
.backgroundColor($r('app.color.white'))// 背景色
.textFont({ size: 14 })// 文字大小
.layoutWeight(1)// 占比
.padding(0)
.margin(0)
.height(36)
.caretStyle({ color: $r('app.color.red') })
.defaultFocus(true)// 默认获取焦点
.onSubmit((value) => {
// Log.info(value)// value可以获取输入的内容
this.toSearchResult(value)
})
}
.padding({ top: this.safeTop, right: 16 })
.linearGradient({ angle: 135, colors: [[$r('app.color.linear_begin'), 0], [$r('app.color.linear_end'), 1]] })
// 语音搜索组件
AudioSearchComp({
onChange: (keyword: string) => {
Log.info('转换的文本是:' + keyword)
this.keyword = keyword
},
onComplete: (keyword: string) => {
// AlertDialog.show({
// message: '最后的信息为:' + keyword
// })
// 跳转到搜索页即可
this.toSearchResult(keyword)
}
})
}
}
}