HarmonyOS ArkUI之聊天列表滑动删除(TS) 原创 精华

中软HOS小鸿
发布于 2021-11-25 14:14
浏览
9收藏

作者:梁青松

简介

本项目基于ArkUI中TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:

基于TS扩展的声明式开发范式1基于TS扩展的声明式开发范式2

本文介绍列表滑动删除:

效果演示

HarmonyOS ArkUI之聊天列表滑动删除(TS)-鸿蒙开发者社区

主要知识点

可滑动的容器组件(Scroll)触摸事件(onTouch)

实现思路

我把界面精简了一下,减少代码量,帮助更好的理解主要逻辑。

HarmonyOS ArkUI之聊天列表滑动删除(TS)-鸿蒙开发者社区

1、item布局

主要使用scroll包裹内容,scroll设置为横向滑动(部分代码)

.....
Scroll() {
      Row() {
        Text('内容数据')
          .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('内容数据')
          .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、设置触摸事件

根据移动的偏移量,判断大于删除布局宽度的一半则:打开删除布局(部分代码)

.....
// 初始化控制器
private scroller= new Scroller()
// 按下的x轴坐标
private downX = 0
// 删除按钮的宽度
private deleteWidth = 100

Scroll(scroller) { // 控制器绑定到滑动容器中
      Row() {
        Text('内容数据')
          .width('100%').height(65)

        Button() {
          Text('删除')
        }
        .width(this.deleteWidth).height(65)
      }
 }.scrollable(ScrollDirection.Horizontal)
.onTouch((event: TouchEvent) => { // 触摸事件
      // 根据触摸类型判断
      switch (event.type) {
        case TouchType.Down: // 触摸按下
          // 记录按下的x轴坐标
          this.downX = event.touches[0].x
          break
        case TouchType.Up: // 触摸抬起
          // 触摸抬起,根据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记录一下数据,点击内容或删除、滑动其他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
  // 已经打开删除的数据
  private openDeleteData: TestData = null
  // 测试数据
  @State private listData: Array<TestData> = [
    { content: '内容数据1', scroller: new Scroller() }, { content: '内容数据2', scroller: new Scroller() },
    { content: '内容数据3', scroller: new Scroller() }, { content: '内容数据4', scroller: new Scroller() },
    { content: '内容数据5', scroller: new Scroller() }, { content: '内容数据6', scroller: new Scroller() },
    { content: '内容数据7', scroller: new Scroller() }, { content: '内容数据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(() => {
          // 删除当前数据
          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 } })
      }

      // 根据触摸类型判断
      switch (event.type) {
        case TouchType.Down: // 触摸按下
          // 记录按下的x轴坐标
          this.downX = event.touches[0].x
          break
        case TouchType.Up: // 触摸抬起
          // 触摸抬起,根据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声明式开发,是鸿蒙新出的东西,API还不是那么完善,后续跟进官网更新。以下是需要优化点:

  • ArkUI中的TS没有JS中的新出的插槽概念,要不然直接封装成组件,提供两个对外的接口,一个传入内容布局、一个操作布局,就像Android的组件库一样,使用者不需要知道内部实现。

每天进步一点点、需要付出努力亿点点。

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-11-25 15:13:23修改
9
收藏 9
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

在认识楼主之前,真没想到聊天功能有这么多细节。

1
回复
2021-11-25 14:55:32
中软HOS小鸿
中软HOS小鸿 回复了 红叶亦知秋
在认识楼主之前,真没想到聊天功能有这么多细节。

哈哈哈,把所有的内容组装在一起可以做很多事情了

1
回复
2021-11-25 15:14:08
物联风景
物联风景

好帖子,支持

1
回复
2021-11-25 15:53:41
Whyalone
Whyalone

感谢分享ArkUI相关内容,据说12月中旬会更新一波,不知道大佬知不知道会更新什么内容?

回复
2021-11-26 10:16:52
回复
    相关推荐