最近一直捣鼓 纯血鸿蒙os框架,花了一个月高速开发了一款基于 HarmonyOS Next5.0 api12克隆微信app界面聊天室项目模板Harmony-Chatroom。
HarmonyOS-WeChat是一个基于纯血鸿蒙OS Next5.0 API12实战开发的聊天应用程序,模仿了微信的界面和功能,提供了聊天、通讯录、我、朋友圈等模块。这个项目使用了ArkUI和ArkTS技术栈,实现了类似微信的消息UI布局、输入框光标处插入文字、emoji表情图片/GIF动图、图片预览、红包、语音/位置UI、长按语音面板等功能。
使用版本
DevEco Studio 5.0.3.906
HarmonyOS 5.0.0 API12 Release SDK
commandline-tools-windows-x64-5.0.3.906
目前harmony-chat聊天app项目已经同步到我的原创作品集。
HarmonyOS-Next5.0-API12仿微信app聊天程序
项目结构目录
HarmonyOS-Chat项目的框架结构是基于 DevEco Studio 5.0.3.906编码工具构建。项目中所有的顶部标题导航栏都是自定义封装的ArkUI组件实现的。
https://developer.huawei.com/consumer/cn/deveco-studio/
开发和学习资源
对于想要快速入门到进阶开发HarmonyOS应用的开发者,建议先阅读官方文档,然后再找一个实战项目案例进行练习。华为鸿蒙os开发官网提供了HarmonyOS开发设计规范和ArkUI方舟UI框架的相关资料,这些都是宝贵的开发资源。
- 鸿蒙os官网
https://developer.huawei.com/consumer/cn/
- ArkUI方舟UI框架
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/arkui-declarative-comp-V5
harmonyos登录/注册模板|60s倒计时验证码
/**
* 登录模板
* @author andy
*/
import { router, promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Login {
@State name: string = ''
@State pwd: string = ''
// 提交
handleSubmit() {
if(this.name === '' || this.pwd === '') {
promptAction.showToast({ message: '账号或密码不能为空' })
}else {
// 登录接口逻辑...
promptAction.showToast({ message: '登录成功' })
setTimeout(() => {
router.replaceUrl({ url: 'pages/Index' })
}, 2000)
}
}
build() {
Column() {
Column({space: 10}) {
Image('pages/assets/images/logo.png').height(50).width(50)
Text('HarmonyOS-Chat').fontSize(18).fontColor('#0a59f7')
}
.margin({top: 50})
Column({space: 15}) {
TextInput({placeholder: '请输入账号'})
.onChange((value) => {
this.name = value
})
TextInput({placeholder: '请输入密码'}).type(InputType.Password)
.onChange((value) => {
this.pwd = value
})
Button('登录').height(45).width('100%')
.linearGradient({ angle: 135, colors: [['#0a59f7', 0.1], ['#07c160', 1]] })
.onClick(() => {
this.handleSubmit()
})
}
.margin({top: 30})
.width('80%')
Row({space: 15}) {
Text('忘记密码').fontSize(14).opacity(0.5)
Text('注册账号').fontSize(14).opacity(0.5)
.onClick(() => {
router.pushUrl({url: 'pages/views/auth/Register'})
})
}
.margin({top: 20})
}
.height('100%')
.width('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
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)
}
}
harmonyos arkui自定义导航条
项目中顶部导航条使用ArkUI范式开发的增强版导航条组件。
HarmonyOS NEXT 5.0自定义增强版导航栏组件|鸿蒙ArkUI自定义标题栏
基于HarmonyOS自定义弹窗组件
// 标题(支持字符串|自定义组件)
@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
通过如下方式调用。
// 自定义退出弹窗
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%'
})
聊天功能模块
// 聊天模板 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')
uniapp-vue3-oadmin手机后台实例|vite5.x+uniapp多端仿ios管理系统
tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序
Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用
Electron32-ViteOS桌面版os系统|vue3+electron+arco客户端OS管理模板
Vite5+Electron聊天室|electron31跨平台仿微信EXE客户端|vue3聊天程序
HarmonyOS-Chat是一个展示如何在纯血鸿蒙OS Next5.0 API12上使用ArkUI和ArkTS开发聊天应用的实例。它提供了丰富的聊天功能,对于开发者来说,这是一个很好的学习和参考资源。