写点什么

HarmonyOS 5.0 应用开发——仿微信聊天界面

作者:高心星
  • 2024-12-09
    江苏
  • 本文字数:3485 字

    阅读完需:约 11 分钟

HarmonyOS 5.0应用开发——仿微信聊天界面

【高心星出品】


仿微信聊天界面

闲暇之余开发了一个基于 HarmonyOS5.0 的仿微信聊天界面,里面主要用到了 ArkUI 的技术。


List 布局:列表是一种复杂的容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求(如通讯录、音乐列表、购物清单等)。


Grid 布局:网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力,子组件占比控制能力,是一种重要自适应布局,其使用场景有九宫格图片展示、日历、计算器等。


Swiper 布局:Swiper 本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。通常,在一些应用首页显示推荐的内容时,需要用到轮播显示的能力。


LazyForeach 懒加载:LazyForEach 从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了 LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。

运行效果:


开发步骤:

项目结构


聊天标题

聊天标题是一个水平布局,包括左边的箭头,用户名称和功能图片。其中左边的箭头是由线性布局旋转之后外边框绘制而来。



 titlebar() {    Row() {      if (this.badgevalue > 0) {        //  生成箭头        this.arrow(ArrowType.LEFT)        Badge({          value: '' + this.badgevalue,          position: BadgePosition.Right,          style: { badgeSize: 24, badgeColor: Color.Gray, borderColor: Color.Gray }        }) {          Text(' ')        }      } else {        this.arrow(ArrowType.LEFT)      }      Blank()      if (this.title) {        Text(this.title).fontSize(22).fontColor(Color.Black)      }      Blank()      Image($r('app.media.sangedian')).width('6%').aspectRatio(1)

} .width('100%') .padding('5%') .border({ width: { bottom: 1 }, color: this.divdercolor }) .margin({ bottom: 5 }) } //绘制左右箭头 @Builder arrow(type: ArrowType) { if (type == ArrowType.LEFT) { Row() .rotate({ angle: -45 }) .border({ width: { top: 2, left: 2 }, color: Color.Black }) .width('5%') .height('5%') .aspectRatio(1) .margin({ right: 20 }) } else if (type == ArrowType.RIGHT) { Row() .rotate({ angle: 45 }) .border({ width: { top: 2, left: 2 }, color: Color.Black }) .width('5%') .height('5%') .aspectRatio(1) .margin({ right: 20 }) } }
复制代码
聊天内容

聊天内容整体是一个 list,里面的 listitem 会根据消息来源来进行调整,发出的消息采取右侧布局,接受的消息左侧布局。



// 消息展示区域  @Builder  content() {    List() {      LazyForEach(this.chats, (item: ChatInfo) => {        ListItem() {          if (item.type == ChatType.TIME) {            this.itemtime(item)          } else {            this.itemmsg(item)          }        }      })    }.layoutWeight(1)  }  // 发消息和收消息的子布局  @Builder  itemmsg(chat: ChatInfo) {    if (chat.from === currentuser) {      //发出的消息      Row() {        Text(chat.message)          .padding({            left: '4%',            right: '4%',            top: '2%',            bottom: '2%'          })          .backgroundColor('rgba(200,200,200,0.5)')          .fontSize(18)          .borderRadius({            topLeft: 10,            bottomLeft: 10,            topRight: 15,            bottomRight: 15          })        Image($r('app.media.Bellboy'))          .width(35)          .height(35)          .objectFit(ImageFit.Fill)          .borderRadius(8)          .margin({ left: 10 })      }.width('100%')      .padding('2%')      .justifyContent(FlexAlign.End)    } else {      //接受的消息      Row() {        Image($r('app.media.girl')).width(35).height(35).objectFit(ImageFit.Fill).borderRadius(8)        Text(chat.message)          .padding({            left: '4%',            right: '4%',            top: '2%',            bottom: '2%'          })          .backgroundColor('rgba(200,200,200,0.5)')          .fontSize(18)          .borderRadius({            topLeft: 15,            bottomLeft: 15,            topRight: 10,            bottomRight: 10          })          .margin({ left: 10 })      }.width('100%')      .padding('2%')    }  }  // 展示时间的子布局  @Builder  itemtime(msg: ChatInfo) {    Column() {      Text(msg.time).fontSize(16).fontColor(Color.Gray)
}.width('100%').padding('3%') }
复制代码
发消息布局

发消息布局采取的是水平线性布局,从左至右分别是语音图标,输入框,图标和新增图标。



 // 发消息布局  @Builder  msgbar() {    Column() {      Row({ space: 10 }) {        Image($r('app.media.voicemessage')).width(40).height(40).objectFit(ImageFit.Fill)        TextInput().layoutWeight(1).borderRadius(10).backgroundColor(Color.White)        Image($r('app.media.smile')).width(40).height(40).objectFit(ImageFit.Fill)        Image($r('app.media.add')).width(40).height(40).objectFit(ImageFit.Fill)          .onClick(() => {            animateTo({curve:curves.springMotion()},()=>{              this.showhidebar = !this.showhidebar
}) }) }.padding('3%').width('100%') .border({ width: { top: 1 }, color: this.divdercolor })
// 隐藏栏 this.hidebar() }.width('100%') }
复制代码
隐藏功能卡片布局

隐藏功能卡片区是用了 swiper 布局里面嵌套了 Grid 网格布局。



  @Builder  hidebar() {    if (this.showhidebar) {      Swiper() {        this.gongnengpage(gongnengs.slice(0, 8))        this.gongnengpage(gongnengs.slice(8))      }      .border({width:{top:1},color:this.divdercolor})    }  }
// 功能页面 @Builder gongnengpage(gongnengs: GongnengType[]) { Grid() { ForEach(gongnengs, (gongneng: GongnengType) => { GridItem() { this.card(gongneng) } }) }.columnsTemplate('1fr 1fr 1fr 1fr') .width('100%') .height('30%')
.rowsGap('10%') .margin({top:'10%'}) } // 功能也的功能卡片 @Builder card(gongneng: GongnengType) { Column() { Row() { Image(gongneng.icon) .width(40) .height(40) .objectFit(ImageFit.Fill) }.width(60).height(60).backgroundColor(Color.White).justifyContent(FlexAlign.Center) .borderRadius(8) Text(gongneng.name).fontSize(14).margin({top:10}) }.margin({ bottom: '5%' }) }
复制代码
数据源的填充

填充消息的时候还要考虑,消息之间的时间间隔。消息之间时间间隔如果比较小就不会显示时间,如果时间间隔比较大,就需要增加一个时间显示列表项。



// 如果上下两个消息之间时间间隔超过1个小时 增加一个时间  adddata(data: ChatInfo) {    if (this.datas.length > 0) {      let time = data.time      let time2 = this.datas[this.datas.length-1].time      let hour = Number.parseInt(time.slice(time.indexOf(' ') + 1, time.indexOf(':')))      let hour2 = Number.parseInt(time2.slice(time.indexOf(' ') + 1, time.indexOf(':')))      if (Math.abs(hour - hour2) >= 1) {        this.datas.push({ type: ChatType.TIME, time: data.time })      }    }    this.datas.push(data)  }
复制代码

项目仓库 Gitee

仓库地址: https://gitee.com/gxx01/zwechatroom/

发布于: 刚刚阅读数: 3
用户头像

高心星

关注

天将降大任于斯人也,必先苦其心志。 2024-10-17 加入

华为开发者专家(HDE)。 10年教学经验,兼任多家科技公司技术顾问。先后从事JavaEE项目开发、Python爬虫、HarmonyOS移动应用开发等课程的教学工作。参与开发《鸿蒙应用开发基础》和《鸿蒙项目实战》等课程。

评论

发布
暂无评论
HarmonyOS 5.0应用开发——仿微信聊天界面_arkui_高心星_InfoQ写作社区