项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
1. 概述
在即时通讯应用中,聊天气泡是一个核心UI元素,它需要能够区分发送方和接收方的消息,并以不同的样式和位置显示。本教程将详细讲解如何使用HarmonyOS NEXT的Row组件创建反向排列的消息气泡,重点介绍reverse
属性的巧妙应用,帮助开发者构建出专业、美观的聊天界面。
2. 聊天气泡的设计原则
在设计聊天气泡时,需要考虑以下几个关键原则:
- 区分发送方和接收方:通常通过气泡的位置(左侧或右侧)和颜色来区分。
- 视觉一致性:气泡的形状、大小和样式应保持一致,只是位置和颜色有所不同。
- 适应内容长度:气泡应能够根据文本内容的长度自动调整大小。
- 清晰的视觉层次:消息之间应有足够的间距,形成清晰的视觉层次。
3. 案例分析:反向排列的消息气泡
本案例展示了如何创建一个简单的聊天界面,通过Row组件的reverse
属性实现消息气泡的左右排列,并通过不同的背景色区分发送方和接收方。
3.1 完整代码
@Component export struct ChatBubbleExample { private messages: string[] = ['你好!', '今天天气不错', 'HarmonyOS开发很高效'] build() { Column() { ForEach(this.messages, (msg:string, index) => { Row() { Text(msg) .padding({ left: 20,right:20, top: 12, bottom: 12 }) .backgroundColor(index % 2 === 0 ? 0x007DFF : 0xFFFFFF) .fontColor(index % 2 === 0 ? 0xFFFFFF : 0x000000) .borderRadius(24) } .margin({bottom: 12}) .reverse(index % 2 === 1) .justifyContent(index % 2 === 0 ? FlexAlign.End : FlexAlign.Start) }, (msg:string) => msg) } .width('100%') .padding(24) } }
3.2 代码详解
3.2.1 组件声明与数据定义
@Component export struct ChatBubbleExample { private messages: string[] = ['你好!', '今天天气不错', 'HarmonyOS开发很高效']
这部分代码声明了一个名为ChatBubbleExample
的自定义组件,并定义了一个私有数组messages
,包含三条示例消息。在实际应用中,这些消息通常来自数据库或网络请求。
3.2.2 外层容器设置
Column() { // 子组件 } .width('100%') .padding(24)
这部分代码创建了一个Column容器,用于垂直排列消息气泡。Column容器的属性设置如下:
属性 |
值 |
说明 |
width |
'100%' |
容器宽度为父容器的100% |
padding |
24 |
容器四周内边距为24vp |
这些设置确保了聊天界面有足够的空间显示消息,并且与屏幕边缘保持适当的距离。
3.2.3 消息循环渲染
ForEach(this.messages, (msg:string, index) => { // 单条消息的渲染逻辑 }, (msg:string) => msg)
这部分代码使用ForEach
组件循环渲染消息数组中的每一条消息。ForEach
接收三个参数:
- 要循环的数组:
this.messages
- 渲染函数:
(msg:string, index) => { ... }
,接收当前项和索引作为参数 - 唯一键函数:
(msg:string) => msg
,用于标识每一项,优化重渲染性能
在渲染函数中,我们为每条消息创建一个Row容器和一个Text组件。
3.2.4 单条消息的Row容器
Row() { Text(msg) .padding({ left: 20,right:20, top: 12, bottom: 12 }) .backgroundColor(index % 2 === 0 ? 0x007DFF : 0xFFFFFF) .fontColor(index % 2 === 0 ? 0xFFFFFF : 0x000000) .borderRadius(24) } .margin({bottom: 12}) .reverse(index % 2 === 1) .justifyContent(index % 2 === 0 ? FlexAlign.End : FlexAlign.Start)
这部分代码为每条消息创建一个Row容器,并在其中放置一个Text组件显示消息内容。Row容器的属性设置如下:
属性 |
值 |
说明 |
margin |
{bottom: 12} |
底部外边距为12vp,使消息之间有足够的间距 |
reverse |
index % 2 === 1 |
根据索引奇偶性决定是否反转子组件排列顺序 |
justifyContent |
index % 2 === 0 ? FlexAlign.End : FlexAlign.Start |
根据索引奇偶性决定子组件在主轴上的对齐方式 |
这些设置实现了消息气泡的左右排列:偶数索引的消息(发送方)靠右对齐,奇数索引的消息(接收方)靠左对齐。
3.2.5 消息文本组件
Text(msg) .padding({ left: 20,right:20, top: 12, bottom: 12 }) .backgroundColor(index % 2 === 0 ? 0x007DFF : 0xFFFFFF) .fontColor(index % 2 === 0 ? 0xFFFFFF : 0x000000) .borderRadius(24)
这部分代码创建了一个Text组件,显示消息内容。Text组件的属性设置如下:
属性 |
值 |
说明 |
padding |
{ left: 20, right: 20, top: 12, bottom: 12 } |
设置内边距,使文本不会贴近气泡边缘 |
backgroundColor |
index % 2 === 0 ? 0x007DFF : 0xFFFFFF |
根据索引奇偶性设置背景色:偶数为蓝色,奇数为白色 |
fontColor |
index % 2 === 0 ? 0xFFFFFF : 0x000000 |
根据索引奇偶性设置文字颜色:偶数为白色,奇数为黑色 |
borderRadius |
24 |
设置边框圆角为24vp,使文本呈现为气泡形状 |
这些设置实现了不同样式的消息气泡:发送方的消息是蓝色背景、白色文字,接收方的消息是白色背景、黑色文字。
4. reverse属性的深入解析
reverse
属性是Row组件的一个重要特性,它可以反转子组件的排列顺序,在聊天气泡这种需要左右排列的场景中非常有用。
4.1 reverse属性的工作原理
当reverse
属性设置为true
时,Row组件会将子组件按照从右到左的顺序排列,而不是默认的从左到右。这相当于将整个Row容器水平翻转。
.reverse(index % 2 === 1) // 奇数索引的消息反转排列
在本案例中,我们根据消息的索引奇偶性来决定是否反转排列:
- 偶数索引(index % 2 === 0):不反转,从左到右排列
- 奇数索引(index % 2 === 1):反转,从右到左排列
4.2 reverse与justifyContent的配合使用
在聊天气泡场景中,reverse
属性通常与justifyContent
属性配合使用,以实现消息的左右对齐。
.reverse(index % 2 === 1) .justifyContent(index % 2 === 0 ? FlexAlign.End : FlexAlign.Start)
这种配合使用的逻辑如下:
消息类型 |
索引 |
reverse |
justifyContent |
效果 |
发送方 |
偶数 |
false |
FlexAlign.End |
消息靠右对齐 |
接收方 |
奇数 |
true |
FlexAlign.Start |
消息靠左对齐 |
通过这种配合,我们可以实现发送方消息靠右、接收方消息靠左的效果,同时保持气泡的形状和样式一致。
5. 聊天气泡的样式优化
为了提升聊天气泡的视觉效果,我们可以进行以下优化:
5.1 气泡形状优化
通过调整borderRadius
属性,可以创建更符合直觉的气泡形状:
// 发送方气泡(右侧) .borderRadius({ topLeft: 24, topRight: 24, bottomLeft: 24, bottomRight: 4 // 右下角圆角较小,形成尖角效果 }) // 接收方气泡(左侧) .borderRadius({ topLeft: 24, topRight: 24, bottomLeft: 4, // 左下角圆角较小,形成尖角效果 bottomRight: 24 })
5.2 气泡阴影效果
添加阴影可以增强气泡的立体感:
.shadow({ radius: 4, color: 0x33000000, offsetX: 2, offsetY: 2 })
5.3 文本内容优化
对于长文本,可以设置最大宽度和自动换行:
Text(msg) .maxLines(0) // 不限制行数,自动换行 .maxWidth('70%') // 最大宽度为容器的70%
6. 聊天界面的扩展功能
基于本案例的基本结构,我们可以扩展更多功能:
6.1 消息时间戳
在每条消息下方添加时间戳:
Row() { Column() { Text(msg) // 气泡样式 Text('10:30') // 时间戳 .fontSize(12) .fontColor(0x999999) .margin({ top: 4 }) .alignSelf(index % 2 === 0 ? ItemAlign.End : ItemAlign.Start) } }
6.2 消息状态指示
为发送方消息添加状态指示(已发送、已读等):
Row() { Column() { Text(msg) // 气泡样式 if (index % 2 === 0) { // 只为发送方消息添加状态 Row() { Text('已读') .fontSize(12) .fontColor(0x999999) Image($r('app.media.read')) // 已读图标 .width(12) .height(12) } .margin({ top: 4 }) .alignSelf(ItemAlign.End) } } }
6.3 头像显示
在消息旁边添加用户头像:
Row() { if (index % 2 === 1) { // 接收方消息,左侧显示头像 Image($r('app.media.avatar')) .width(36) .height(36) .borderRadius(18) .margin({ right: 8 }) } Text(msg) // 气泡样式 if (index % 2 === 0) { // 发送方消息,右侧显示头像 Image($r('app.media.my_avatar')) .width(36) .height(36) .borderRadius(18) .margin({ left: 8 }) } }
7. 聊天组件的封装与复用
为了提高代码复用性,可以将聊天气泡封装为独立组件:
@Component export struct ChatBubble { message: string isSender: boolean timestamp?: string status?: string avatar?: Resource build() { Row() { if (!this.isSender && this.avatar) { Image(this.avatar) .width(36) .height(36) .borderRadius(18) .margin({ right: 8 }) } Column() { Text(this.message) .padding({ left: 20, right: 20, top: 12, bottom: 12 }) .backgroundColor(this.isSender ? 0x007DFF : 0xFFFFFF) .fontColor(this.isSender ? 0xFFFFFF : 0x000000) .borderRadius({ topLeft: 24, topRight: 24, bottomLeft: this.isSender ? 24 : 4, bottomRight: this.isSender ? 4 : 24 }) .maxWidth('70%') if (this.timestamp) { Text(this.timestamp) .fontSize(12) .fontColor(0x999999) .margin({ top: 4 }) .alignSelf(this.isSender ? ItemAlign.End : ItemAlign.Start) } if (this.isSender && this.status) { Text(this.status) .fontSize(12) .fontColor(0x999999) .margin({ top: 4 }) .alignSelf(ItemAlign.End) } } if (this.isSender && this.avatar) { Image(this.avatar) .width(36) .height(36) .borderRadius(18) .margin({ left: 8 }) } } .width('100%') .margin({ bottom: 16 }) .justifyContent(this.isSender ? FlexAlign.End : FlexAlign.Start) } }
然后在聊天界面中使用这个组件:
@Entry @Component struct ChatPage { private messages: Array<{ content: string, isSender: boolean, timestamp: string, status?: string, avatar?: Resource }> = [ { content: '你好!', isSender: true, timestamp: '10:30', status: '已读', avatar: $r('app.media.my_avatar') }, { content: '你好,有什么可以帮到你?', isSender: false, timestamp: '10:31', avatar: $r('app.media.avatar') } ] build() { Column() { ForEach(this.messages, (item, index) => { ChatBubble({ message: item.content, isSender: item.isSender, timestamp: item.timestamp, status: item.status, avatar: item.avatar }) }, (item, index) => index.toString()) } .width('100%') .padding(16) } }
8. 总结
本教程详细讲解了如何使用HarmonyOS NEXT的Row组件创建反向排列的消息气泡,重点介绍了reverse
属性的巧妙应用。通过本案例,我们学习了:
- 聊天气泡的设计原则
- Row组件的基本用法和参数设置
reverse
属性的工作原理及其与justifyContent
的配合使用- 聊天气泡的样式优化技巧
- 聊天界面的扩展功能
- 聊天组件的封装与复用
掌握这些知识点后,你可以设计出美观、易用、专业的聊天界面,提升应用的用户体验。在实际开发中,可以根据具体需求调整气泡样式、布局和功能,创建符合应用设计风格的聊天界面。