HarmonyOS ArkUI之聊天列表滑動刪除(TS)
簡介
本項目基于ArkUI中TS擴展的聲明式開發(fā)范式,關于語法和概念直接看官網(wǎng)官方文檔地址:
基于TS擴展的聲明式開發(fā)范式1、基于TS擴展的聲明式開發(fā)范式2
本文介紹列表滑動刪除:
列表中只允許滑出其中一項
如果有打開的項,點擊或滑動其他項都會關閉打開的項
點擊刪除,刷新列表界面
ArKUI系列文章
- 【HarmonyOS ArkUI之仿微信朋友圈圖片預覽】
- 【HarmonyOS ArkUI之仿微信圖片選擇】
- 【HarmonyOS ArkUI之自定義組件側滑菜單(JS)】
- 【HarmonyOS ArkUI之聊天列表滑動刪除(TS)】
效果演示

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

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