🎯 案例集合List:ListItem侧滑(删除、收藏)
🌍 案例集合List
🚪 最近开启了学员班级点我欢迎加入(学习相关知识可获得定制礼盒)
🏷️ 效果图
🏷 原生

🏷 自定义

📖 参考
🧩 拆解
- 原生ListItem组件自带侧滑
javascript
/**
* 用户类
*/
@ObservedV2
class UserInfo {
@Trace label: string = ''
@Trace collect: boolean = false
id: number = 0
changeCollect() {
this.collect = !this.collect
}
}
/**
* 模拟数据
*/
const listData: string[] = ['购物', '体育', '服装', '军事', '电商', '娱乐', '科技']
@ComponentV2
export struct ListSlideDelete {
/**
* 模拟数据
*/
@Local mockData: UserInfo[] = []
aboutToAppear(): void {
this.InitData()
}
/**
* 初始化数据
*/
InitData() {
for (let i = 0; i < listData.length; i++) {
let myInfo: UserInfo = new UserInfo()
myInfo.label = listData[i]
myInfo.collect = false
myInfo.id = i
this.mockData.push(myInfo)
}
}
/**
* 删除列表数据项
* @param idx
*/
delListItem(idx: number) {
this.mockData.splice(idx, 1)
}
@Builder
listItemEnd(item: UserInfo, idx: number) {
Row({ space: 5 }) {
Text('收藏')
.width(50)
.aspectRatio(1)
.fontSize(20)
.fontWeight(FontWeight.Regular)
.borderRadius(50)
.backgroundColor(item.collect ? Color.Orange : Color.Gray)
.textAlign(TextAlign.Center)
.onClick(() => item.changeCollect())
Text('删除')
.width(50)
.aspectRatio(1)
.fontSize(20)
.fontWeight(FontWeight.Regular)
.borderRadius(50)
.backgroundColor('#80ff8b8b')
.textAlign(TextAlign.Center)
.onClick(() => this.delListItem(idx))
}
.padding(5)
.justifyContent(FlexAlign.Center)
}
build() {
Column() {
List({ space: 20 }) {
ForEach(this.mockData, (item: UserInfo, idx: number) => {
ListItem() {
Text(item.label)
.width('100%')
.height(100)
.fontSize(20)
.borderRadius(20)
.textAlign(TextAlign.Center)
.backgroundColor('#4dfdddcb')
}
.swipeAction(
{
end: {
builder: () => this.listItemEnd(item, idx),
// onAction: () => this.delListItem(idx), TODO:放开:侧滑到最左侧也可删除
}
}
)
}, (item: UserInfo) => item.id.toString())
}
.layoutWeight(1)
.scrollBar(BarState.Off) // 关闭滚动条
.edgeEffect(EdgeEffect.None) // 关闭滚动动效
}
.width('100%')
.height('100%')
.padding(15)
}
}
- 自定义ListItem组件自带侧滑
javascript
/**
* 用户类
*/
@ObservedV2
class UserInfo {
@Trace label: string = ''
@Trace collect: boolean = false
@Trace translate: number = 0
id: number = 0
changeCollect() {
this.collect = !this.collect
}
}
/**
* 模拟数据
*/
const listData: string[] = ['购物', '体育', '服装', '军事', '电商', '娱乐', '科技']
@ComponentV2
export struct CustomListSlideDelete {
/**
* 模拟数据
*/
@Local mockData: UserInfo[] = []
/**
* 最大滑动距离(listItemEnd组件宽度)
*/
private maxTranslate: number = -110
/**
* 滑动距离的一半
*/
private halfOfTheDistance: number = -65
/**
* 最小滑动距离
*/
private defTranslate: number = 0
aboutToAppear(): void {
this.InitData()
}
/**
* 初始化数据
*/
InitData() {
for (let i = 0; i < listData.length; i++) {
let myInfo: UserInfo = new UserInfo()
myInfo.label = listData[i]
myInfo.collect = false
myInfo.id = i
myInfo.translate = 0
this.mockData.push(myInfo)
}
}
@Builder
listItemEnd(item: UserInfo, idx: number) {
Row({ space: 5 }) {
Text('收藏')
.width(50)
.aspectRatio(1)
.fontSize(20)
.fontWeight(FontWeight.Regular)
.borderRadius(50)
.backgroundColor(item.collect ? Color.Orange : Color.Gray)
.textAlign(TextAlign.Center)
.onClick(() => item.changeCollect())
Text('删除')
.width(50)
.aspectRatio(1)
.fontSize(20)
.fontWeight(FontWeight.Regular)
.borderRadius(50)
.backgroundColor('#80ff8b8b')
.textAlign(TextAlign.Center)
.onClick(() => this.mockData.splice(idx, 1))
}
.padding({ left: 5 })
.width(110)
.height('100%')
.justifyContent(FlexAlign.Center)
}
build() {
Column() {
List({ space: 20 }) {
ForEach(this.mockData, (item: UserInfo, idx: number) => {
ListItem() {
Row() {
Row() {
Text(item.label)
.fontSize(20)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
this.listItemEnd(item, idx)
}
.width('100%')
.height(100)
.borderRadius(20)
.backgroundColor('#4dfdddcb')
.translate({ x: item.translate })
.animation({ duration: 200, curve: Curve.EaseOut })
// 支持手动滑动手势
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionUpdate((event: GestureEvent) => {
// 左负数, 右正数字
const offsetX = event.offsetX
// 当前listItem的滑动
const curTranslate = item.translate
// 默认滑动值
const defTranslate = this.defTranslate
// 最大滑动值
const maxTranslate = this.maxTranslate
// 规避左右滑动的时候符合最小 最大滑动值,其余情况不滑动
if (curTranslate === defTranslate && offsetX > 0 || curTranslate === maxTranslate && offsetX < 0) {
return
}
// 只允许在0 ~ -110滑动
if (curTranslate <= defTranslate && curTranslate >= maxTranslate) {
// 小于0 认为在向左滑动
if (offsetX < 0) {
if (-(Math.abs(curTranslate) + Math.abs(event.offsetX)) <= -110) {
// 小于-110的时候给-110
this.mockData[idx].translate = maxTranslate
return
}
this.mockData[idx].translate = -(Math.abs(curTranslate) + Math.abs(event.offsetX))
}
// 认为在向右滑动
else {
if (curTranslate + event.offsetX >= 0) {
// 大于0的时候给0
this.mockData[idx].translate = defTranslate
return
}
this.mockData[idx].translate = curTranslate + event.offsetX
}
}
})
.onActionEnd(() => {
// 滑动结束后自动调整状态(超过一半则完全展开,否则收起)
if (this.mockData[idx].translate < this.halfOfTheDistance) {
this.mockData[idx].translate = this.maxTranslate
return
}
this.mockData[idx].translate = this.defTranslate
})
)
}
}, (item: UserInfo) => item.id.toString())
}
.layoutWeight(1)
.scrollBar(BarState.Off) // 关闭滚动条
.edgeEffect(EdgeEffect.None) // 关闭滚动动效
}
.width('100%')
.height('100%')
.padding(15)
}
}
🌸🌼🌺