HarmonyOS ArkUI之聊天列表滑動(dòng)刪除(TS)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
簡(jiǎn)介
本項(xiàng)目基于ArkUI中TS擴(kuò)展的聲明式開(kāi)發(fā)范式,關(guān)于語(yǔ)法和概念直接看官網(wǎng)官方文檔地址:
基于TS擴(kuò)展的聲明式開(kāi)發(fā)范式1、基于TS擴(kuò)展的聲明式開(kāi)發(fā)范式2
本文介紹列表滑動(dòng)刪除:
列表中只允許滑出其中一項(xiàng)
如果有打開(kāi)的項(xiàng),點(diǎn)擊或滑動(dòng)其他項(xiàng)都會(huì)關(guān)閉打開(kāi)的項(xiàng)
點(diǎn)擊刪除,刷新列表界面
ArKUI系列文章
- 【HarmonyOS ArkUI之仿微信朋友圈圖片預(yù)覽】
- 【HarmonyOS ArkUI之仿微信圖片選擇】
- 【HarmonyOS ArkUI之自定義組件側(cè)滑菜單(JS)】
- 【HarmonyOS ArkUI之聊天列表滑動(dòng)刪除(TS)】
效果演示

主要知識(shí)點(diǎn)
可滑動(dòng)的容器組件(Scroll)、觸摸事件(onTouch)
實(shí)現(xiàn)思路
我把界面精簡(jiǎn)了一下,減少代碼量,幫助更好的理解主要邏輯。

1、item布局
主要使用scroll包裹內(nèi)容,scroll設(shè)置為橫向滑動(dòng)(部分代碼)
- .....
- Scroll() {
- Row() {
- Text('內(nèi)容數(shù)據(jù)')
- .width('100%').height(65)
- Button() {
- Text('刪除')
- }
- .width(100).height(65)
- }
- }.scrollable(ScrollDirection.Horizontal) // 設(shè)置為橫向滑動(dòng)
- .....
2、Scroll容器
給Scroll容器綁定滑動(dòng)組件的控制器,只用到其中的一個(gè)方法:滑動(dòng)到指定位置 scrollTo
- scrollTo(
- value: {
- xOffset: number | string, yOffset: number | string, animation?
- : { duration: number, curve: Curve }
- }
- );
看源碼得知可以設(shè)置動(dòng)畫(huà)時(shí)間,注意:時(shí)間目前好像不能設(shè)置300毫秒以上,往下設(shè)置可以 (部分代碼)
- .....
- // 初始化控制器
- private scroller= new Scroller()
- Scroll(scroller) { // 控制器綁定到滑動(dòng)容器中
- Row() {
- Text('內(nèi)容數(shù)據(jù)')
- .width('100%').height(65)
- Button() {
- Text('刪除')
- }
- .width(100).height(65)
- }
- }.scrollable(ScrollDirection.Horizontal)
- Button() {
- Text('點(diǎn)擊回到原位')
- }.onClick(()=>{
- scroller.scrollTo({ xOffset: 0, yOffset: 0, animation: { duration: 200, curve: Curve.Linear } })
- })
- .....
3、設(shè)置觸摸事件
根據(jù)移動(dòng)的偏移量,判斷大于刪除布局寬度的一半則:打開(kāi)刪除布局(部分代碼)
- .....
- // 初始化控制器
- private scroller= new Scroller()
- // 按下的x軸坐標(biāo)
- private downX = 0
- // 刪除按鈕的寬度
- private deleteWidth = 100
- Scroll(scroller) { // 控制器綁定到滑動(dòng)容器中
- Row() {
- Text('內(nèi)容數(shù)據(jù)')
- .width('100%').height(65)
- Button() {
- Text('刪除')
- }
- .width(this.deleteWidth).height(65)
- }
- }.scrollable(ScrollDirection.Horizontal)
- .onTouch((event: TouchEvent) => { // 觸摸事件
- // 根據(jù)觸摸類型判斷
- switch (event.type) {
- case TouchType.Down: // 觸摸按下
- // 記錄按下的x軸坐標(biāo)
- this.downX = event.touches[0].x
- break
- case TouchType.Up: // 觸摸抬起
- // 觸摸抬起,根據(jù)x軸總偏移量,判斷是否打開(kāi)刪除
- let xOffset = event.touches[0].x - this.downX
- // 滑到目標(biāo)x軸的位置
- var toxOffset = 0
- // 偏移量超過(guò)刪除按鈕一半且左滑,設(shè)置打開(kāi)
- if (Math.abs(xOffset) > vp2px(this.deleteWidth) / 2 && xOffset < 0) {
- // 刪除布局寬度
- toxOffset = vp2px(this.deleteWidth)
- }
- // 滑動(dòng)指定位置,設(shè)置動(dòng)畫(huà)
- item.scroller.scrollTo({ xOffset: toxOffset, yOffset: 0,
- animation: { duration: 300, curve: Curve.Linear } })
- // 重置按下的x軸坐標(biāo)
- this.downX = 0
- break
- }
- })
- .....
4、使用列表加載
需要主要的點(diǎn):
- 需要給每個(gè)item綁定控制器,這樣才能控制對(duì)應(yīng)的item打開(kāi)或關(guān)閉
- 打開(kāi)的item記錄一下數(shù)據(jù),點(diǎn)擊內(nèi)容或刪除、滑動(dòng)其他item:如果有帶打開(kāi)的item,進(jìn)行關(guān)閉
以下是完整代碼,可直接粘貼運(yùn)行使用。
- class TestData {
- content: string
- scroller: Scroller
- constructor(content: string, scroller: Scroller) {
- this.content = content
- this.scroller = scroller
- }
- }
- @Entry
- @Component
- struct SlidingDeleteList {
- // 刪除按鈕的寬度
- private deleteWidth = 100
- // 按下的x軸坐標(biāo)
- private downX = 0
- // 已經(jīng)打開(kāi)刪除的數(shù)據(jù)
- private openDeleteData: TestData = null
- // 測(cè)試數(shù)據(jù)
- @State private listData: Array<TestData> = [
- { content: '內(nèi)容數(shù)據(jù)1', scroller: new Scroller() }, { content: '內(nèi)容數(shù)據(jù)2', scroller: new Scroller() },
- { content: '內(nèi)容數(shù)據(jù)3', scroller: new Scroller() }, { content: '內(nèi)容數(shù)據(jù)4', scroller: new Scroller() },
- { content: '內(nèi)容數(shù)據(jù)5', scroller: new Scroller() }, { content: '內(nèi)容數(shù)據(jù)6', scroller: new Scroller() },
- { content: '內(nèi)容數(shù)據(jù)7', scroller: new Scroller() }, { content: '內(nèi)容數(shù)據(jù)8', scroller: new Scroller() },
- ]
- @Builder CustomItem(item:TestData) {
- Scroll(item.scroller) {
- Row() {
- Text(item.content)
- .width('100%').height(65)
- .fontSize(16).textAlign(TextAlign.Center)
- .onClick(() => {
- // 如果刪除按鈕打開(kāi),關(guān)閉刪除按鈕且返回
- if (this.openDeleteData != null) {
- this.openDeleteData.scroller.scrollTo({ xOffset: 0, yOffset: 0,
- animation: { duration: 100, curve: Curve.Linear } })
- this.openDeleteData = null
- return
- }
- console.log('========點(diǎn)擊內(nèi)容=========')
- })
- Button() {
- Text('刪除')
- .fontSize(15)
- .fontColor(Color.White)
- }
- .type(ButtonType.Normal)
- .width(this.deleteWidth).height(65)
- .backgroundColor(Color.Red)
- .onClick(() => {
- // 刪除當(dāng)前數(shù)據(jù)
- this.listData.splice(this.listData.indexOf(item), 1)
- // 關(guān)閉刪除按鈕
- if (this.openDeleteData != null) {
- this.openDeleteData.scroller.scrollTo({ xOffset: 0, yOffset: 0,
- animation: { duration: 100, curve: Curve.Linear } })
- this.openDeleteData = null
- }
- console.log('========點(diǎn)擊刪除=========')
- })
- }
- }.scrollable(ScrollDirection.Horizontal)
- .onTouch((event: TouchEvent) => { // 觸摸事件
- // 判斷是否有打開(kāi)刪除組件,有則關(guān)閉
- if (this.openDeleteData != null && this.openDeleteData != item) {
- this.openDeleteData.scroller.scrollTo({ xOffset: 0, yOffset: 0,
- animation: { duration: 100, curve: Curve.Linear } })
- }
- // 根據(jù)觸摸類型判斷
- switch (event.type) {
- case TouchType.Down: // 觸摸按下
- // 記錄按下的x軸坐標(biāo)
- this.downX = event.touches[0].x
- break
- case TouchType.Up: // 觸摸抬起
- // 觸摸抬起,根據(jù)x軸總偏移量,判斷是否打開(kāi)刪除
- let xOffset = event.touches[0].x - this.downX
- // 防止消費(fèi)點(diǎn)擊事件
- if (xOffset == 0) {
- return
- }
- // 滑到x軸的位置
- var toxOffset = 0
- // 開(kāi)啟刪除的對(duì)象置為null
- this.openDeleteData = null;
- // 偏移量超過(guò)刪除按鈕一半且左滑,設(shè)置打開(kāi)
- if (Math.abs(xOffset) > vp2px(this.deleteWidth) / 2 && xOffset < 0) {
- // 刪除布局寬度
- toxOffset = vp2px(this.deleteWidth)
- this.openDeleteData = item
- }
- // 滑動(dòng)指定位置,設(shè)置動(dòng)畫(huà)
- item.scroller.scrollTo({ xOffset: toxOffset, yOffset: 0,
- animation: { duration: 300, curve: Curve.Linear } })
- // 重置按下的x軸坐標(biāo)
- this.downX = 0
- break
- }
- })
- }
- build() {
- Column() {
- List() {
- ForEach(this.listData, item => {
- ListItem() {
- this.CustomItem(item)
- }
- }, item => item.toString())
- }.divider({ color: '#f1efef', strokeWidth: 1 })
- }
- .width('100%')
- .height('100%')
- }
- }
結(jié)尾
因?yàn)锳rkUI聲明式開(kāi)發(fā),是鴻蒙新出的東西,API還不是那么完善,后續(xù)跟進(jìn)官網(wǎng)更新。以下是需要優(yōu)化點(diǎn):
ArkUI中的TS沒(méi)有JS中的新出的插槽概念,要不然直接封裝成組件,提供兩個(gè)對(duì)外的接口,一個(gè)傳入內(nèi)容布局、一個(gè)操作布局,就像Android的組件庫(kù)一樣,使用者不需要知道內(nèi)部實(shí)現(xiàn)。
每天進(jìn)步一點(diǎn)點(diǎn)、需要付出努力億點(diǎn)點(diǎn)。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)