鸿蒙从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)
  }
}

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

相关推荐
轻口味1 小时前
【每日学点鸿蒙知识】Json字典问题、高度变化问题、开放测试版本问题、动态库单架构选择、WebView和H5交互
架构·json·harmonyos
轻口味11 小时前
【每日学点鸿蒙知识】hap安装报错、APP转移账号、import本地文件、远程包构建问题、访问前端页面方法
前端·华为·harmonyos
轻口味11 小时前
【每日学点鸿蒙知识】Web请求支持Http、PDF展示、APP上架应用搜索问题、APP备案不通过问题、滚动列表问题
前端·http·harmonyos
轻口味12 小时前
【每日学点鸿蒙知识】webview性能优化、taskpool、热更新、Navigation问题、调试时每次都卸载重装问题
javascript·list·harmonyos
kirk_wang15 小时前
Flutter适配HarmonyOS实践
flutter·华为·harmonyos
Jalor16 小时前
HarmonyOS NEXT | 一文搞懂 华为账号登录(获取UnionID/OpenID)
spring boot·flutter·harmonyos
carrie呀carrie18 小时前
HarmonyOS:删除多层ForEach循环渲染的复杂数据而导致的一系列问题
开发语言·harmonyos·鸿蒙
轻口味19 小时前
【每日学点鸿蒙知识】启动耗时分析、IDE报错、emitter内存泄漏、radio C API、SDK下载失败
c语言·华为·harmonyos
轻口味19 小时前
【每日学点鸿蒙知识】混淆配置、主线程处理大量数据、客户端拖拽效果、三方网站加载样式、List警告问题
华为·harmonyos
李游Leo19 小时前
自学记录HarmonyOS Next Image API 13:图像处理与传输的开发实践
图像处理·华为·harmonyos