#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用 原创 精华

X叶域Q
发布于 2024-7-30 13:27
浏览
4收藏

鸿蒙发展的真快呀,半年前还在用api9,现在直接api12了。下载新的模拟机时发现,DevEcoStudio都从3.1下载到了现在的5.0了,这算不算见证了它的成长呢,哈哈。
#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

想尝试简单写一个聊天页面调用模型,尝试下语法方面有什么变化,然后发现很简单的聊天UI,尽然会有这么多坑,害!话不多说开干!

一、滚动聊天区域

想想一个聊天区域无非就是一个滚动列表判断一下然后弹性布局左右对齐,太简单了,使用Scroll这个可滚动的容器组件,将消息列表遍历一下,单独定义消息的左右对齐,完事!

这时出现了第一个不好控制的效果,当对话少无法占满滚动区域大小尽然是默认居中!
#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

当子组件的布局尺寸超过父组件的尺寸时,内容才可以滚动,翻了会官网文档没找到Scroll内元素向上对齐的方法
只好动用空空的脑袋想一想了:
#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

  • 对话少时,子组件的布局尺寸小于父组件的尺寸无法滚动,默认居中对齐

  • 对话多时超过父组件的尺寸可以滚动,滚动是不是就能上对齐了!

想到就干:设置子组件最小长度为Scroll容器长度

.constraintSize({minHeight:"100%"})

#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

可以!(可以是可以哈,后续一个bug,好像就这样玩出来的 TAT )

// 聊天内容区域
Scroll() {
  Column(){
    ForEach(this.messages, (item: message) => {
      Flex({justifyContent: item.role === 'user' ? FlexAlign.End : FlexAlign.Start}) {
        if (item.role === 'user') {
          ItemComponent(item, true)   // 自己发送的消息
        } else {
          ItemComponent(item, false)  // 对方发送的消息
        }
      }
    })
  }
  .constraintSize({minHeight:"100%"})	// 设置子组件最小长度为Scroll容器长度
}
.width("100%")
.height("90%")
.scrollBar(BarState.Off)

二、消息左右对齐与传输数据格式

1、消息如何设置左右对齐

是我的消息我就放右边,不是我的就放左边,判断判断

这里可以用if非常好,那就简单了,来个参数告诉我这条消息是不是自己的,然后:

  1. Flex布局容器 里面用三目运算符设置对齐方式,可以吧

  2. 头像左右不好控制用if控制其显示,是不是很方便

最后就是颜色,大小,样式的调整了,没啥美感就这样了,开摆

#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

//渲染消息
@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') // 根据发送者设置背景色
      .padding({ top: 8, bottom: 8, left: 10, right: 10 })
      .borderRadius(10)
      .textAlign(TextAlign.Start)
      .constraintSize({maxWidth: "240vp"})	// 设置最大宽度

    if(isSelf) {
      Image($r("app.media.me"))
        .width("50vp")
    }
  }
  .margin({ top: 4, bottom: 4 }) 	// 消息间距
}

2、传输数据格式

  1. 因为要知道是谁发的消息就记录了一下角色,对比了一下QQ,想到昵称尽然没有,等待后续加工中…

  2. 写到json格式的数据,需要取返回消息的一个值,HarmonyOS next不在支持any了,语法检查有点严格哈,只好自定义类型了。

    需要哪个字段定义哪个好像也行,偷懒不可取哦

  3. 因为接口调用要将全部上下文消息整体发送过去,所以定义了个messages数组,还可以forearch展示用的也是这个数组

// 发送的每条消息类型
class message{	
  role: string;		// 发送角色
  content: string;	// 发送的消息	
    				// 如有更多需求,如:发消息人物昵称,可进行相应的设计
  constructor(role: string,  content: string) {
    this.role = role
    this.content = content
  }
}

// 根据ai反回的数据类型定义的接口类型,next不支持any类型
interface userInfo{
  id: string,
  object: string,
  created: string,
  result: string
  is_truncated: boolean,
  need_clear_history: boolean,
  finish_reason: string
}


// @State数据与页面双向数据绑定,当消息增加时页面刷新显示
@State messages: Array<message> = [
    new message("user","你好"),
    new message("assistant", "你好,请问有什么我可以帮助你的吗?无论你有什么问题或需要帮助,我都会尽力回答和协助你。请随时告诉我你的需求。")
];

三、输入框和发送

这里有个bug很难受,各位大佬,求评论,救救 TAT
#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

一个输入框,一个按钮发送,用预览器调试挺方便的,然后到了模拟机有个叫 键盘 的东东没考虑到。

这个东西尽然会把,消息给顶上去!消息少时就输入键盘一跳出来就白白了 (看不到消息了)
#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

研究了一下微信,消息少时输入时消息是顶格的,消息多时输入消息看到的最下放时最新消息,也就滚动到了看的见最下方

尝试解决:

  • 一、翻动我’‘可爱’'的官方文档 找到一个叫 Scroller 的可滚动容器组件的控制器,当它按下时,获取当前消息区域滚动条的位置,当它抬起时,让它回到按下时的位置,无效
  • 二、搜索其他语言解决办法:vue聊天页面在进入之后信息滑动到底部位置 然后还是翻动我’‘可爱’'的官方文档,不知道该怎么搜索,没找到相应的函数
  • 三、求救
// 可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。
myScroll: Scroller = new Scroller();
locat: OffsetResult = {xOffset: 0, yOffset: 0};

// 输入框和发送按钮
Row() {
  TextInput({ text: this.inputText, placeholder: '输入你想询问的问题' })
    .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('发送')
    .onClick(() => {
      // 发送消息并更新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接口调用

这个我会,我会使用axios,突然想到新版不知道能不能用,证明结果:axios已经适配鸿蒙HarmonyOS NEXT

OpenHarmony三方库中心仓 axios

import axios, {AxiosResponse} from '@ohos/axios'

这里调用的是文兴一言模型,要调用什么ai可查看其开发文档

const apiKey = "xxxxxx"
const secret = "xxxxxxxxxxx"

// 根据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}`)
}
// 根据token和所有消息发送一次请求,获取结果
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)
}

同时HarmonyOS next 内置了很多AI服务 哦

:textToSpeech (文本转语音) import { textToSpeech } from '@kit.CoreSpeechKit'; objectDetection(多目标识别)import { visionBase, objectDetection } from '@kit.CoreVisionKit;

具体查看官方文档:AI | 华为开发者联盟 (huawei.com)

五、总结

果然手搓UI界面得:一、先设计,二、懂布局
换句话说:创建UI界面时,首要步骤是进行精心的设计,其次则需要深入理解并掌握布局技巧。

HanmonyOS next入门很简单,但最好一个产品很不容易,欢迎来一起学习

最后附上效果图和源码地址

一、效果图

#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

二、目录结构

#HarmonyOS NEXT体验官#聊天UI效果与简单AI接口调用-鸿蒙开发者社区

三、源码地址

源码:https://atomgit.com/leaf-domain/chatUI

openharmony最新开发文档https://docs.openharmony.cn/pages/v5.0/zh-cn/device-dev/quick-start/quickstart-overview.md

最新工具:https://developer.huawei.com/consumer/cn/download

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-7-30 13:35:16修改
8
收藏 4
回复
举报
11条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

看上去产品已经相当完整了!

回复
2024-7-30 14:24:18
笨笨的婧婧
笨笨的婧婧

官方文档确实非常‘可爱’'的,哈哈


1
回复
2024-7-30 16:57:27
挨打小王子
挨打小王子

博主,创意撞车了,哈哈哈哈

回复
2024-7-31 14:46:11
挨打小王子
挨打小王子

你的键盘还能把输入框给顶上去,我在laval的开发者手机上跑,直接顶不上去

回复
2024-7-31 14:48:00
真庐山升龙霸
真庐山升龙霸

实用的学习思路

1
回复
2024-7-31 17:30:11
X叶域Q
X叶域Q 回复了 挨打小王子
你的键盘还能把输入框给顶上去,我在laval的开发者手机上跑,直接顶不上去

用的是什么组件,或者是什么逻辑,求思路TAT

回复
2024-8-1 10:36:09
第一小趴菜
第一小趴菜

回复
2024-8-1 11:52:30
挨打小王子
挨打小王子 回复了 X叶域Q
用的是什么组件,或者是什么逻辑,求思路TAT

我用的也是scroll,不过我还没弄这个输入框的问题,我现在在处理图片识别


回复
2024-8-1 14:38:14
皮皮虾233
皮皮虾233

赞持之以恒的作者

1
回复
2024-8-14 14:48:54
0aaron
0aaron

已经到api12了吗!

回复
2024-8-16 14:46:11
挨打小王子
挨打小王子

scroll的问题解决了吗?

我是用官方的办法,获取一下软键盘出现时的高度,然后对scroll的高度进行调整

效果还行,不会把聊天记录给顶走,我用的办法是用Scroll把输入框给顶下去,那我在调整软键盘的时候,只需要调整scroll的大小就可以了,而且采用.align(Alignment.Top),这样就不会被顶走了

回复
2024-8-20 09:39:27
回复
    相关推荐