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

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

相关推荐
HwJack206 小时前
HarmonyOS APP开发玩转鸿蒙 HSP:打造高复用“乐高模块”的底层逻辑
华为·harmonyos
●VON13 小时前
28个Token重构鸿蒙App:企业级设计系统的搭建实践
华为·重构·harmonyos
求学中--15 小时前
AppStorage和LocalStorage有什么区别?鸿蒙全局状态管理方案选型指南
华为·harmonyos
求学中--17 小时前
鸿蒙状态管理一文通:@State/@Prop/@Link/@Provide四大装饰器,15分钟彻底搞懂
华为·harmonyos
nashane18 小时前
HarmonyOS 6学习:HWAsan监测开启后应用崩溃的终极解决方案
学习·华为·harmonyos·harmonyos 5
Exploring19 小时前
实测 Vibe Coding:快速开发 HarmonyOS 玩 Android 客户端
harmonyos
UnicornDev19 小时前
【Flutter x HarmonyOS 6】魔方计时APP——记录页面的UI设计
flutter·ui·华为·harmonyos·鸿蒙
Swift社区19 小时前
鸿蒙 PC + 手机 + 平板:一次真正的多端应用实战
智能手机·电脑·harmonyos
纤纡.20 小时前
HarmonyOS 鸿蒙 ArkTS 实战:从零开发生肖集卡抽奖小程序
华为·小程序·harmonyos·deveco studio
枫叶丹420 小时前
【HarmonyOS 6.0】Data Augmentation Kit端侧问答模型:本地化智能问答的技术演进
开发语言·华为·harmonyos