【高心星出品】
仿微信聊天介面
閒暇之餘開發了一個基於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/