纯血鸿蒙OS Next5.0 api12聊天实例|harmony-chat仿微信|ArkUI聊天app

最新重磅原创鸿蒙版Next5 API12仿微信app界面聊天室|arkts朋友圈

基于HarmonyOS NEXT5.0实战仿微信界面聊天App程序HarmonyWeChat。实现编辑器光标处输入文字+emo表情图片、gif动图、图片预览、红包、语音/位置UI、长按语音操作面板等功能。

在这里插入图片描述
harmony-chat聊天app包含了聊天、通讯录、我、朋友圈等模块。

在这里插入图片描述

版本框架

  • DevEco Studio 5.0.0 Release
    构建版本:5.0.3.906
  • HarmonyOS 5.0.0 Release SDK,基于OpenHarmony SDK Ohos_sdk_public 5.0.0.71 (API Version 12 Release)
  • commandline-tools-windows-x64-5.0.3.906

在这里插入图片描述
在这里插入图片描述
harmonyos arkui自定义弹框、实现微信朋友圈功能。

在这里插入图片描述
在这里插入图片描述

项目结构目录

使用DevEco Studio 5.0.3.906编辑器搭建聊天app框架。

在这里插入图片描述
在这里插入图片描述

HarmonyOS-Chat聊天项目已经更新到我的原创作品集,欢迎下载使用。
HarmonyOS Next5.0 API12 arkts+arkui仿微信app聊天

在这里插入图片描述

入口页面index.ets

在这里插入图片描述
在这里插入图片描述

// 自定义页面
@Builder customPage() {
  if(this.pageIndex === 0) {
    IndexPage()
  }else if(this.pageIndex === 1) {
    FriendPage()
  }else if(this.pageIndex === 2) {
    MyPage()
  }
}

build() {
  Navigation() {
    this.customPage()
  }
  .toolbarConfiguration(this.customToolBar)
  .height('100%')
  .width('100%')
  .backgroundColor($r('sys.color.background_secondary'))
  .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}

在这里插入图片描述

// 自定义底部菜单栏
@Builder customToolBar() {
  Row() {
    Row() {
      Badge({
        count: 8,
        style: {},
        position: BadgePosition.RightTop
      }) {
        Column({space: 2}) {
          SymbolGlyph($r('sys.symbol.ellipsis_message_fill'))
          Text('聊天').fontSize(12)
        }
      }
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.pageIndex = 0
    })

    Row() {
      Column({space: 2}) {
        SymbolGlyph($r('sys.symbol.person_2'))
        Text('通讯录').fontSize(12)
      }
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.pageIndex = 1
    })

    Row() {
      Badge({
        value: '',
        style: { badgeSize: 8, badgeColor: '#fa2a2d' }
      }) {
        Column({space: 2}) {
          SymbolGlyph($r('sys.symbol.person_crop_circle_fill_1'))
          Text('我').fontSize(12)
        }
      }
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.pageIndex = 2
    })
  }
  .height(56)
  .width('100%')
  .backgroundColor($r('sys.color.background_secondary'))
  .borderWidth({top: 1})
  .borderColor($r('sys.color.background_tertiary'))
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

鸿蒙os arkts自定义顶部标题栏

在这里插入图片描述
HMNavBar组件支持自定义返回键、标题/副标题、标题居中、背景色/背景图片/背景渐变色、标题颜色、搜索、右侧操作区等功能。

在这里插入图片描述
之前有分享一篇文章,感兴趣的可以去看看。
https://blog.csdn.net/yanxinyun1990/article/details/143397550

在这里插入图片描述

harmonyos实现验证码倒计时

在这里插入图片描述

Stack({alignContent: Alignment.End}) {
  TextInput({placeholder: '验证码'})
    .onChange((value) => {
      this.code = value
    })
  Button(`${this.codeText}`).enabled(!this.disabled).controlSize(ControlSize.SMALL).margin({right: 5})
    .onClick(() => {
      this.handleVCode()
    })
}
// 验证码参数
@State codeText: string = '获取验证码'
@State disabled: boolean = false
@State time: number = 60

// 获取验证码
handleVCode() {
  if(this.tel === '') {
    promptAction.showToast({ message: '请输入手机号' })
  }else if(!checkMobile(this.tel)) {
    promptAction.showToast({ message: '手机号格式错误' })
  }else {
    const timer = setInterval(() => {
      if(this.time > 0) {
        this.disabled = true
        this.codeText = `获取验证码(${this.time--})`
      }else {
        clearInterval(timer)
        this.codeText = '获取验证码'
        this.time = 5
        this.disabled = false
      }
    }, 1000)
  }
}

鸿蒙next实现下拉刷新/自定义弹框

在这里插入图片描述
下拉刷新逻辑

Refresh({
  refreshing: $$this.isRefreshing,
  builder: this.customRefreshTips
}) {
  List() {
    ForEach(this.queryData, (item: RecordArray) => {
      ListItem() {
        // ...
      }
      .stateStyles({pressed: this.pressedStyles, normal: this.normalStyles})
      .bindContextMenu(this.customCtxMenu, ResponseType.LongPress)
      .onClick(() => {
        // ...
      })
    }, (item: RecordArray) => item.cid.toString())
  }
  .height('100%')
  .width('100%')
  .backgroundColor('#fff')
  .divider({ strokeWidth: 1, color: '#f5f5f5', startMargin: 70, endMargin: 0 })
  .scrollBar(BarState.Off)
}
.pullToRefresh(true)
.refreshOffset(64)
// 当前刷新状态变更时触发回调
.onStateChange((refreshStatus: RefreshStatus) => {
  console.info('Refresh onStatueChange state is ' + refreshStatus)
  this.refreshStatus = refreshStatus
})
// 进入刷新状态时触发回调
.onRefreshing(() => {
  console.log('onRefreshing...')
  setTimeout(() => {
    this.isRefreshing = false
  }, 2000)
})

@State isRefreshing: boolean = false
@State refreshStatus: number = 1

// 自定义刷新tips
@Builder customRefreshTips() {
  Stack() {
    Row() {
      if(this.refreshStatus == 1) {
        SymbolGlyph($r('sys.symbol.arrow_down')).fontSize(24)
      }else if(this.refreshStatus == 2) {
        SymbolGlyph($r('sys.symbol.arrow_up')).fontSize(24)
      }else if(this.refreshStatus == 3) {
        LoadingProgress().height(24)
      }else if(this.refreshStatus == 4) {
        SymbolGlyph($r('sys.symbol.checkmark')).fontSize(24)
      }
      Text(`${
        this.refreshStatus == 1 ? '下拉刷新' :
          this.refreshStatus == 2 ? '释放更新' :
            this.refreshStatus == 3 ? '加载中...' :
              this.refreshStatus == 4 ? '完成' : ''
      }`).fontSize(16).margin({left:10})
    }
    .alignItems(VerticalAlign.Center)
  }
  .align(Alignment.Center)
  .clip(true)
  .constraintSize({minHeight:32})
  .width('100%')
}

长按菜单

在这里插入图片描述
.bindContextMenu(this.customCtxMenu, ResponseType.LongPress)

// 自定义长按右键菜单
@Builder customCtxMenu() {
  Menu() {
    MenuItem({
      content: '标为已读'
    })
    MenuItem({
      content: '置顶该聊天'
    })
    MenuItem({
      content: '不显示该聊天'
    })
    MenuItem({
      content: '删除'
    })
  }
}

下拉菜单

在这里插入图片描述
.bindMenu([ ... ])

Image($r('app.media.plus')).height(24).width(24)
  .bindMenu([
    {
      icon: $r('app.media.message_on_message'),
      value:'发起群聊',
      action: () => {}
    },
    {
      icon: $r('app.media.person_badge_plus'),
      value:'添加朋友',
      action: () => router.pushUrl({url: 'pages/views/friends/AddFriend'})
    },
    {
      icon: $r('app.media.line_viewfinder'),
      value:'扫一扫',
      action: () => {}
    },
    {
      icon: $r('app.media.touched'),
      value:'收付款',
      action: () => {}
    }
  ])

鸿蒙os自定义弹窗组件

在这里插入图片描述
在这里插入图片描述

// 标题(支持字符串|自定义组件)
@BuilderParam title: ResourceStr | CustomBuilder = BuilderFunction
// 内容(字符串或无状态组件内容)
@BuilderParam message: ResourceStr | CustomBuilder = BuilderFunction
// 响应式组件内容(自定义@Builder组件是@State动态内容)
@BuilderParam content: () => void = BuilderFunction
// 弹窗类型(android | ios | actionSheet)
@Prop type: string
// 是否显示关闭图标
@Prop closable: boolean
// 关闭图标颜色
@Prop closeColor: ResourceColor
// 是否自定义内容
@Prop custom: boolean
// 自定义操作按钮
@BuilderParam buttons: Array<ActionItem> | CustomBuilder = BuilderFunction
// 自定义退出弹窗
logoutController: CustomDialogController = new CustomDialogController({
  builder: HMPopup({
    type: 'android',
    title: '提示',
    message: '确定要退出当前登录吗?',
    buttons: [
      {
        text: '取消',
        color: '#999'
      },
      {
        text: '退出',
        color: '#fa2a2d',
        action: () => {
          router.replaceUrl({url: 'pages/views/auth/Login'})
        }
      }
    ]
  }),
  maskColor: '#99000000',
  cornerRadius: 12,
  width: '75%'
})

harmonyos聊天模块

在这里插入图片描述
在这里插入图片描述聊天核心模板

// 聊天模板 Q:282310962

Stack() {
  /**
   * 聊天主体(消息区/底部操作区)
   */
  Column() {
    /* 导航条 */
    HMNavBar({
      title: 'HarmonyOS Next 5.0',
      bgLinearGradient: { angle: 135, colors: [['#cc07c160', 0.2], ['#cc0a59f7', 1]] },
      fontColor: '#fff',
      actions: [
        {
          icon: $r('sys.symbol.more'),
          action: () => router.pushUrl({url: 'pages/views/chat/GroupInfo'})
        }
      ]
    })

    /* 渲染聊天消息 */
    Scroll(this.scroller) {
      Column({space: 15}) {
        ForEach(this.chatList, (item: ChatArray) => {
          // ...
        }, (item: ChatArray) => item.id)
      }
      // 倒叙显示
      .reverse(true)
      // .padding(15)
      .constraintSize({minHeight: '100%'})
      .width('100%')
    }
    // 聊天区翻转
    .rotate({angle: 180})
    .direction(Direction.Rtl)
    .padding(15)
    .width('100%')
    .layoutWeight(1)
    .scrollBar(BarState.On)
    .edgeEffect(EdgeEffect.Spring)
    .onScrollEdge((side: Edge) => {
      if(side === 0) {
        console.info('To the bottom edge')
      }else if(side === 2) {
        console.info('To the top edge')
      }
    })
    .onTouch(() => {
      this.handleChatAreaTouched()
    })

    /* 底部操作栏 */
    Row() {
      // ....
    }
    .width('100%')
    .backgroundColor('#f8f8f8')
  }
  .height('100%')
  .width('100%')
  .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

  /**
   * 录音主体(按住说话)
   */
  Column() {
    Stack({alignContent: Alignment.Bottom}) {
      // ...
    }
    .height('100%')
    .width('100%')
  }
  .visibility(this.voicePanelEnable ? Visibility.Visible : Visibility.None)
  .height('100%')
  .width('100%')
  .backgroundColor('#99000000')
  .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
.height('100%')
.width('100%')
.backgroundColor($r('sys.color.background_secondary'))
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

聊天消息区,进入页面滚动到聊天底部,采用翻转倒叙方式实现。

/* 底部操作栏 */
Row() {
  Column() {
    // 输入框模块
    Row({space: 10}) {
      Row() {
        SymbolGlyph($r('sys.symbol.mic_circle')).fontSize(24)
          .visibility(this.voiceEnable ? Visibility.None : Visibility.Visible)
        SymbolGlyph($r('sys.symbol.keyboard_circle')).fontSize(24)
          .visibility(!this.voiceEnable ? Visibility.None : Visibility.Visible)
      }
      .onClick(() => {
        this.voiceEnable = !this.voiceEnable
        this.footBarEnable = false
      })
      Row() {
        // 编辑器
        RichEditor({controller: this.richEditorController}).backgroundColor('#fff').borderRadius(4).caretColor('#0a59f7')
          .visibility(this.voiceEnable ? Visibility.None : Visibility.Visible)
        // 按住说话
        // 通过item[key]取到值的时候会报错,Indexed access is not supported for fields
        // 解决办法Object(item)[key]
        Text(`${Object(this.voiceTypeMap)[this.voiceType]}`).backgroundColor('#fff').borderRadius(4).fontSize(15).height(34).width('100%').textAlign(TextAlign.Center)
          .visibility(!this.voiceEnable ? Visibility.None : Visibility.Visible)
          .onTouch((event: TouchEvent) => {
            if(event) {
              if(event.type === TouchType.Down) {
                this.voiceType = 1
                this.voicePanelEnable = true
              }
              if(event.type === TouchType.Move) {
                ...

                // 触摸判断
                if(pos.y >= panY) {
                  this.voiceType = 1 // 松开发送
                }else if(pos.y < panY && pos.x < panX) {
                  this.voiceType = 2 // 左滑取消发送
                }else if(pos.y < panY && pos.x >= panX) {
                  this.voiceType = 3 // 右滑语音转文字
                }
              }
              if(event.type === TouchType.Up || event.type === TouchType.Cancel) {
                switch (this.voiceType) {
                  ...
                }
                this.voiceType = 0
              }
            }
          })
      }
      .layoutWeight(1)
      SymbolGlyph($r('sys.symbol.capture_smiles')).fontSize(24)
        .onClick(() => {
          this.handleEmoChooseState(0)
        })
      SymbolGlyph($r('sys.symbol.plus')).fontSize(24)
        .onClick(() => {
          this.handleEmoChooseState(1)
        })
      SymbolGlyph($r('sys.symbol.paperplane')).fontSize(24).fontColor(['#0a59f7'])
        .onClick(() => {
          this.handleSubmit()
        })
    }
    .padding(10)
    .alignItems(VerticalAlign.Center)

    // 表情/选择模块
    Column() {
      if(this.footBarIndex == 0) {
        // 表情区域
        this.renderEmoWidget()
      }else {
        // 选择区域
        this.renderChooseWidget()
      }
    }
    .height(308)
    .width('100%')
    .visibility(this.footBarEnable ? Visibility.Visible : Visibility.None)
  }
}
.width('100%')
.backgroundColor('#f8f8f8')

整个harmony-chat聊天项目涉及到的知识蛮多的,就暂分享到这里吧。

最后附上几个最新的实战项目,希望能帮助到大家。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyan_2018

你的鼓励将是我持续作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值