鸿蒙从0搭建Chatgpt App客户端,第三篇之聊天页面搭建输入框

前面两篇我们介绍了

  1. 自定义头像
  2. 聊天列表

今天我们学习下

  1. 自定义titleBar
  2. 底部输入框交互
  3. 页面整体布局

老规矩先看看chatgpt官方的样式

下图是我实现的样子

键盘输入动画图

下面我们先讲解下自定义titleBar

js 复制代码
@Preview
@Component
export default struct TitleComponent {
  build() {
    Row() {
      //左边区域
      Row() {
        Image($r('app.media.ic_m'))
          .width(18)
          .margin({ right: 16 })
        Text("ChatGPT 3.5").fontSize(18)
      }
      //右边区域
      Row() {
        Image($r('app.media.ic_new'))
          .width(28)
          .margin({ right: 16 })
        Image($r('app.media.ic_more'))
          .width(20)
      }
    }.justifyContent(FlexAlign.SpaceBetween)
    .width('100%').padding(16)
  }
}

代码很简单,一个row控件包裹两个子row控件,每个row控件包括对应的内容控件,都是前面两篇讲过的控件,下面我们着重讲解下底部的输入框控件,要想实现gpt控件原生的TextArea控件肯定实现不了,我们需要做下处理,下面上代码

js 复制代码
@Preview
@Component
export default struct InputComponent {
  mController: TextAreaController = new TextAreaController()
  @State mHeight: number = 55;//底部控件初始高度
  @State isFirst: boolean = true;//是否是第一次显示
  @State showVoice: boolean = true;//是否显示右侧声音图片
  hTemp = 55

  build() {
    RelativeContainer() {
      RelativeContainer() {
        //背景圆角
        Rect({ height: this.mHeight, width: '100%', radius: 20 })
          .id("bg")
          .fill($r('app.color.inputGray'))
          .antiAlias(true)

        //输入框多行
        TextArea({ placeholder: "Message",
          controller: this.mController })
          .caretColor(Color.Black)
          .textAlign(TextAlign.Start)
          .backgroundColor(Color.Transparent) // 背景设置透明
          .onAreaChange((o, n) => {
            if (o.height == 0) {
              this.mHeight = this.hTemp
              this.isFirst = false
            } else {
              this.mHeight = (n.height as number) + 20
            }
          })
          .onChange((t) => {
            if (t.length > 0) {
              this.showVoice = false
            } else {
              this.showVoice = true
            }
          })
          .alignRules({
            left: { anchor: '__container__', align: HorizontalAlign.Start },
            right: { anchor: '__container__', align: HorizontalAlign.End },
            center: { anchor: '__container__', align: VerticalAlign.Center },
          })
          .layoutWeight(1)
          .id("left")
        if (this.showVoice) {
          //声音图片
          Image($r('app.media.ic_voice'))
            .width(28).margin({ left: 16, right: 16 }).id("right")
            .alignRules({
              center: { anchor: '__container__', align: VerticalAlign.Center },
              right: { anchor: '__container__', align: HorizontalAlign.End },
            })
        }


      }
      .id("left_input")
      .width('100%')
      .height('100%')
      .alignRules({
        center: { anchor: '__container__', align: VerticalAlign.Center },
        left: { anchor: '__container__', align: HorizontalAlign.Start },
        right: { anchor: 'right_send', align: HorizontalAlign.Start },
      })
      .margin({ left: 16, right: 12 })
      // 发送按钮
      Row() {
        Stack() {
          Circle({
            width: 40, height: 40
          }).fill(this.getSendBgColor(!this.showVoice)).antiAlias(true)

          if (this.showVoice) {
            Image($r('app.media.ic_send'))
              .width(24)
          } else {
            Image($r('app.media.ic_send_white'))
              .width(24)
          }

        }

      }
      .margin({ right: 16 })
      .alignRules({
        center: { anchor: '__container__', align: VerticalAlign.Center },
        right: { anchor: '__container__', align: HorizontalAlign.End },
      })
      .id("right_send")
    }.width('100%').height(this.mHeight)
  }

  getSendBgColor(black: boolean): ResourceColor {
    if (black) {
      return Color.Black
    }
    return $r('app.color.inputGray')
  }
}

首页用了RelativeContainer布局将底部分成两个部分,一个左边的输入框+声音推按,一个右边的发送按钮,左边的输入框是由三部分组成,将输入框自定义了

  1. 背景圆角
  2. 原生输入框
  3. 右侧图片

讲完UI我们将下交互

  1. 这里我们定义了@State(state是用于状态管理的,相当于通过定义这个变量跟视图绑定后,当数据变化时,视图的数据跟着变化) mHeight的控件的高度,当输入框输入文字大于两行时,我们通过TextArea的onAreaChange方法来获取输入框的高度,从而将这个高度赋值给外面的父view,这样能使得容器的高度随着TextArea内容高度变化而变化;
  2. 我们定义了@State showVoice: boolean = true;//是否显示右侧声音图片,当用户输入文字时右侧声音图片消失,发送按钮变高亮

最后我们看下组合在一起的代码

js 复制代码
import ChatList from '../view/ChatListComponent';
import InputComponent from '../view/InputComponent';
import TitleComponent from '../view/TitleComponent';

@Entry
@Component
struct Index {
  build() {
    Stack() {
      Column() {
        TitleComponent()
        RelativeContainer() {
          ChatList()
            .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top },
              bottom: { anchor: 'input', align: VerticalAlign.Top } })
            .id('content')

        }.layoutWeight(1).backgroundColor(Color.Pink)
      }.alignItems(HorizontalAlign.Start).height("100%")

      InputComponent().margin({ bottom: 16 })
    }.alignContent(Alignment.BottomStart)
  }
}

下一节预告,我们将添加暗黑主题

相关推荐
lqj_本人1 小时前
鸿蒙next版开发:分析JS Crash(进程崩溃)
华为·harmonyos
Harmony_QI2 小时前
鸿蒙北向开发环境安装指南
华为·harmonyos·鸿蒙
lqj_本人11 小时前
鸿蒙next版开发:相机开发-适配不同折叠状态的摄像头变更(ArkTS)
数码相机·华为·harmonyos
呆萌很18 小时前
HCIP-HarmonyOS Application Developer 习题(二十二)
harmonyos
郝晨妤19 小时前
[HarmonyOS]简单说一下鸿蒙架构
华为·架构·harmonyos·鸿蒙
二流小码农21 小时前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
Kousi1 天前
AlphabetIndexer组件,鸿蒙开发
前端·javascript·harmonyos
我爱鸿蒙开发1 天前
ArkTS的进阶语法(函数补充与正则表达式)
前端·harmonyos
爱健身的程序员1 天前
鸿蒙应用开发--状态管理
前端·harmonyos