头图

【高心星出品】

仿微信聊天界面

闲暇之余开发了一个基于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/


高心星
1 声望1 粉丝

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