前面两篇我们介绍了
今天我们学习下
- 自定义titleBar
- 底部输入框交互
- 页面整体布局
老规矩先看看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布局将底部分成两个部分,一个左边的输入框+声音推按,一个右边的发送按钮,左边的输入框是由三部分组成,将输入框自定义了
- 背景圆角
- 原生输入框
- 右侧图片
讲完UI我们将下交互
- 这里我们定义了@State(state是用于状态管理的,相当于通过定义这个变量跟视图绑定后,当数据变化时,视图的数据跟着变化) mHeight的控件的高度,当输入框输入文字大于两行时,我们通过TextArea的onAreaChange方法来获取输入框的高度,从而将这个高度赋值给外面的父view,这样能使得容器的高度随着TextArea内容高度变化而变化;
- 我们定义了@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)
}
}
下一节预告,我们将添加暗黑主题