HarmonyOS NEXT體驗(yàn)官#聊天UI效果與簡(jiǎn)單AI接口調(diào)用
想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):
鴻蒙發(fā)展的真快呀,半年前還在用api9,現(xiàn)在直接api12了。下載新的模擬機(jī)時(shí)發(fā)現(xiàn),DevEcoStudio都從3.1下載到了現(xiàn)在的5.0了,這算不算見(jiàn)證了它的成長(zhǎng)呢,哈哈。
想嘗試簡(jiǎn)單寫一個(gè)聊天頁(yè)面調(diào)用模型,嘗試下語(yǔ)法方面有什么變化,然后發(fā)現(xiàn)很簡(jiǎn)單的聊天UI,盡然會(huì)有這么多坑,害!話不多說(shuō)開(kāi)干!
一、滾動(dòng)聊天區(qū)域
想想一個(gè)聊天區(qū)域無(wú)非就是一個(gè)滾動(dòng)列表判斷一下然后彈性布局左右對(duì)齊,太簡(jiǎn)單了,使用Scroll這個(gè)可滾動(dòng)的容器組件,將消息列表遍歷一下,單獨(dú)定義消息的左右對(duì)齊,完事!
這時(shí)出現(xiàn)了第一個(gè)不好控制的效果,當(dāng)對(duì)話少無(wú)法占滿滾動(dòng)區(qū)域大小盡然是默認(rèn)居中!
當(dāng)子組件的布局尺寸超過(guò)父組件的尺寸時(shí),內(nèi)容才可以滾動(dòng),翻了會(huì)官網(wǎng)文檔沒(méi)找到Scroll內(nèi)元素向上對(duì)齊的方法。
只好動(dòng)用空空的腦袋想一想了:
- 對(duì)話少時(shí),子組件的布局尺寸小于父組件的尺寸無(wú)法滾動(dòng),默認(rèn)居中對(duì)齊。
- 對(duì)話多時(shí)超過(guò)父組件的尺寸可以滾動(dòng),滾動(dòng)是不是就能上對(duì)齊了!
想到就干:設(shè)置子組件最小長(zhǎng)度為Scroll容器長(zhǎng)度。
.constraintSize({minHeight:"100%"})
可以?。梢允强梢怨罄m(xù)一個(gè)bug,好像就這樣玩出來(lái)的 TAT )。
// 聊天內(nèi)容區(qū)域
Scroll() {
Column(){
ForEach(this.messages, (item: message) => {
Flex({justifyContent: item.role === 'user' ? FlexAlign.End : FlexAlign.Start}) {
if (item.role === 'user') {
ItemComponent(item, true) // 自己發(fā)送的消息
} else {
ItemComponent(item, false) // 對(duì)方發(fā)送的消息
}
}
})
}
.constraintSize({minHeight:"100%"}) // 設(shè)置子組件最小長(zhǎng)度為Scroll容器長(zhǎng)度
}
.width("100%")
.height("90%")
.scrollBar(BarState.Off)
二、消息左右對(duì)齊與傳輸數(shù)據(jù)格式
1、消息如何設(shè)置左右對(duì)齊
是我的消息我就放右邊,不是我的就放左邊,判斷判斷。
這里可以用if非常好,那就簡(jiǎn)單了,來(lái)個(gè)參數(shù)告訴我這條消息是不是自己的,然后:
- Flex布局容器 里面用三目運(yùn)算符設(shè)置對(duì)齊方式,可以吧。
- 頭像左右不好控制用if控制其顯示,是不是很方便。
最后就是顏色,大小,樣式的調(diào)整了,沒(méi)啥美感就這樣了,開(kāi)擺。
//渲染消息
@Builder
function ItemComponent(item: message, isSelf: boolean) {
Flex({ justifyContent:isSelf ? FlexAlign.End : FlexAlign.Start}) {
if(!isSelf) {
Image($r("app.media.chatbot"))
.width("50vp")
}
Text(item.content)
.fontSize(16)
.lineHeight(24)
.backgroundColor(isSelf ? '#ffdddd' : '#ddffdd') // 根據(jù)發(fā)送者設(shè)置背景色
.padding({ top: 8, bottom: 8, left: 10, right: 10 })
.borderRadius(10)
.textAlign(TextAlign.Start)
.constraintSize({maxWidth: "240vp"}) // 設(shè)置最大寬度
if(isSelf) {
Image($r("app.media.me"))
.width("50vp")
}
}
.margin({ top: 4, bottom: 4 }) // 消息間距
}
2、傳輸數(shù)據(jù)格式
- 因?yàn)橐朗钦l(shuí)發(fā)的消息就記錄了一下角色,對(duì)比了一下QQ,想到昵稱盡然沒(méi)有,等待后續(xù)加工中…
- 寫到j(luò)son格式的數(shù)據(jù),需要取返回消息的一個(gè)值,HarmonyOS next不在支持any了,語(yǔ)法檢查有點(diǎn)嚴(yán)格哈,只好自定義類型了。
需要哪個(gè)字段定義哪個(gè)好像也行,偷懶不可取哦。 - 因?yàn)榻涌谡{(diào)用要將全部上下文消息整體發(fā)送過(guò)去,所以定義了個(gè)messages數(shù)組,還可以forearch展示用的也是這個(gè)數(shù)組。
// 發(fā)送的每條消息類型
class message{
role: string; // 發(fā)送角色
content: string; // 發(fā)送的消息
// 如有更多需求,如:發(fā)消息人物昵稱,可進(jìn)行相應(yīng)的設(shè)計(jì)
constructor(role: string, content: string) {
this.role = role
this.content = content
}
}
// 根據(jù)ai反回的數(shù)據(jù)類型定義的接口類型,next不支持any類型
interface userInfo{
id: string,
object: string,
created: string,
result: string
is_truncated: boolean,
need_clear_history: boolean,
finish_reason: string
}
// @State數(shù)據(jù)與頁(yè)面雙向數(shù)據(jù)綁定,當(dāng)消息增加時(shí)頁(yè)面刷新顯示
@State messages: Array<message> = [
new message("user","你好"),
new message("assistant", "你好,請(qǐng)問(wèn)有什么我可以幫助你的嗎?無(wú)論你有什么問(wèn)題或需要幫助,我都會(huì)盡力回答和協(xié)助你。請(qǐng)隨時(shí)告訴我你的需求。")
];
三、輸入框和發(fā)送
這里有個(gè)bug很難受,各位大佬,求評(píng)論,救救 TAT。
一個(gè)輸入框,一個(gè)按鈕發(fā)送,用預(yù)覽器調(diào)試挺方便的,然后到了模擬機(jī)有個(gè)叫 鍵盤 的東東沒(méi)考慮到。
這個(gè)東西盡然會(huì)把,消息給頂上去!消息少時(shí)就輸入鍵盤一跳出來(lái)就白白了 (看不到消息了)。
研究了一下微信,消息少時(shí)輸入時(shí)消息是頂格的,消息多時(shí)輸入消息看到的最下放時(shí)最新消息,也就滾動(dòng)到了看的見(jiàn)最下方。
嘗試解決:
- 翻動(dòng)我’‘可愛(ài)’'的官方文檔 找到一個(gè)叫 Scroller 的可滾動(dòng)容器組件的控制器,當(dāng)它按下時(shí),獲取當(dāng)前消息區(qū)域滾動(dòng)條的位置,當(dāng)它抬起時(shí),讓它回到按下時(shí)的位置,無(wú)效
- 搜索其他語(yǔ)言解決辦法:vue聊天頁(yè)面在進(jìn)入之后信息滑動(dòng)到底部位置 然后還是翻動(dòng)我’‘可愛(ài)’'的官方文檔,不知道該怎么搜索,沒(méi)找到相應(yīng)的函數(shù)
- 求救
// 可滾動(dòng)的容器組件,當(dāng)子組件的布局尺寸超過(guò)父組件的尺寸時(shí),內(nèi)容可以滾動(dòng)。
myScroll: Scroller = new Scroller();
locat: OffsetResult = {xOffset: 0, yOffset: 0};
// 輸入框和發(fā)送按鈕
Row() {
TextInput({ text: this.inputText, placeholder: '輸入你想詢問(wèn)的問(wèn)題' })
.height(40)
.width("70%")
.onChange((val) => {
this.inputText = val
})
.margin({right: 2})
.onClick(() => {
this.locat = this.myScroll.currentOffset();
})
.onEditChange((isEditing) => {
if(isEditing) {
this.myScroll.scrollTo(this.locat);
}
})
Button('發(fā)送')
.onClick(() => {
// 發(fā)送消息并更新UI
this.messages.push(new message("user", this.inputText))
this.inputText = ''
getTake(this.messages, this.token).then((response: AxiosResponse<userInfo>) =>{
let text = response.data.result
this.messages.push(new message("assistant", text))
})
})
}
.justifyContent(FlexAlign.Center)
.height("10%")
四、AI接口調(diào)用
這個(gè)我會(huì),我會(huì)使用axios,突然想到新版不知道能不能用,證明結(jié)果:axios已經(jīng)適配鴻蒙HarmonyOS NEXT。
OpenHarmony三方庫(kù)中心倉(cāng) axios。
import axios, {AxiosResponse} from '@ohos/axios'
這里調(diào)用的是文興一言模型,要調(diào)用什么ai可查看其開(kāi)發(fā)文檔。
const apiKey = "xxxxxx"
const secret = "xxxxxxxxxxx"
// 根據(jù)apikey和secret獲取token
export function get_access_token(){
return axios.post(`/oauth/2.0/token?grant_type=client_credentials&client_id=${apiKey}&client_secret=${secret}`)
}
// 根據(jù)token和所有消息發(fā)送一次請(qǐng)求,獲取結(jié)果
export function getTake(messages: Array<message>, token: string): Promise<AxiosResponse>{
const params = JSON.stringify({
messages: messages
})
return axios.post(`/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=${token}`, params)
}
同時(shí)HarmonyOS next 內(nèi)置了很多AI服務(wù)哦。
如:textToSpeech (文本轉(zhuǎn)語(yǔ)音) import { textToSpeech } from '@kit.CoreSpeechKit'; objectDetection(多目標(biāo)識(shí)別)import { visionBase, objectDetection } from '@kit.CoreVisionKit;等。
具體查看官方文檔:AI | 華為開(kāi)發(fā)者聯(lián)盟 (huawei.com)
五、總結(jié)
果然手搓UI界面得:先設(shè)計(jì),懂布局。
換句話說(shuō):創(chuàng)建UI界面時(shí),首要步驟是進(jìn)行精心的設(shè)計(jì),其次則需要深入理解并掌握布局技巧。
HanmonyOS next入門很簡(jiǎn)單,但最好一個(gè)產(chǎn)品很不容易,歡迎來(lái)一起學(xué)習(xí)。
最后附上效果圖和源碼地址:
效果圖
目錄結(jié)構(gòu)
源碼地址
源碼:https://atomgit.com/leaf-domain/chatUI。
openharmony最新開(kāi)發(fā)文檔:https://docs.openharmony.cn/pages/v5.0/zh-cn/device-dev/quick-start/quickstart-overview.md。
最新工具:https://developer.huawei.com/consumer/cn/download。