HarmonyOS鸿蒙Next中list嵌套grid,grid的item有手势拖拽,如何设置可以拖拽超出grid的大小
HarmonyOS鸿蒙Next中list嵌套grid,grid的item有手势拖拽,如何设置可以拖拽超出grid的大小
代码如下,如何让子组件的拖拽范围可以超出父组件的边界大小,由于业务需求,网格大小不固定,所以不能使用官方提供的定onItemDragStart和onItemDrop,
只能使用通用手势拖拽。
import { GridPage } from './GridPage'
@ComponentV2
struct ListViewPage {
@Local listData: string[] = []
aboutToAppear(): void {
for (let index = 0; index < 9; index++) {
this.listData.push('itemIndex=' + index)
}
}
build() {
List() {
Repeat(this.listData)
.each((obj: RepeatItem<string>) => {
ListItem() {
Column() {
Text(obj.item).padding(10)
GridPage()
}
}
})
.virtualScroll()
.key((item: string) => item)
}
.height('100%')
.width('100%')
}
}
import { curves } from '@kit.ArkUI';
@Component
export struct GridPage {
//元素数组
@State numbers: number[] = [];
@State row: number = 4;
//元素数组中最后一个元素的索引
@State lastIndex: number = 0;
@State dragItem: number = -1;
@State scaleItem: number = -1;
@State item: number = -1;
@State offsetX: number = 0;
@State offsetY: number = 0;
// row 设置网格列数
private str: string = '';
private dragRefOffsetx: number = 0;
private dragRefOffsety: number = 0;
private FIX_VP_X: number = 108;
private FIX_VP_Y: number = 120;
aboutToAppear() {
for (let i = 1; i <= 8; i++) {
this.numbers.push(i);
}
this.lastIndex = this.numbers.length - 1;
// 多列
for (let i = 0; i < this.row; i++) {
this.str = this.str + '1fr ';
}
}
itemMove(index: number, newIndex: number): void {
if (!this.isDraggable(newIndex)) {
return;
}
let tmp = this.numbers.splice(index, 1);
this.numbers.splice(newIndex, 0, tmp[0]);
}
//向下滑
down(index: number): void {
// 指定固定GridItem不响应事件
if (!this.isDraggable(index + this.row)) {
return;
}
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
// 多列
this.itemMove(index, index + this.row);
}
//向下滑(右下角为空)
down2(index: number): void {
if (!this.isDraggable(index + 3)) {
return;
}
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + 3);
}
//向上滑
up(index: number): void {
if (!this.isDraggable(index - this.row)) {
return;
}
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - this.row);
}
//向左滑
left(index: number): void {
if (!this.isDraggable(index - 1)) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.itemMove(index, index - 1);
}
//向右滑
right(index: number): void {
if (!this.isDraggable(index + 1)) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.itemMove(index, index + 1);
}
//向右下滑
lowerRight(index: number): void {
if (!this.isDraggable(index + this.row + 1)) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + this.row + 1);
}
//向右上滑
upperRight(index: number): void {
if (!this.isDraggable(index - (this.row - 1))) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - (this.row - 1));
}
//向左下滑
lowerLeft(index: number): void {
if (!this.isDraggable(index + (this.row - 1))) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + (this.row - 1));
}
//向左上滑
upperLeft(index: number): void {
if (!this.isDraggable(index - (this.row + 1))) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - (this.row + 1));
}
//通过元素的索引,控制对应元素是否能移动排序
isDraggable(index: number): boolean {
return index > -1; //恒成立,所有元素均可移动排序
}
build() {
Grid() {
ForEach(this.numbers, (item: number) => {
GridItem() {
Text(item + '').fontSize(16).width('100%').textAlign(TextAlign.Center).height(100).borderRadius(10).backgroundColor(0xFFFFFF).shadow(this.scaleItem == item ? {
radius: 70,
color: '#15000000',
offsetX: 0,
offsetY: 0
} :
{
radius: 0,
color: '#15000000',
offsetX: 0,
offsetY: 0
})
.animation({
curve: Curve.Sharp,
duration: 300
})
}
.onAreaChange((_oldVal, newVal) => {
// 多列
this.FIX_VP_X = Math.round(newVal.width as number);
this.FIX_VP_Y = Math.round(newVal.height as number);
})
// 指定固定GridItem不响应事件
.hitTestBehavior(this.isDraggable(this.numbers.indexOf(item)) ? HitTestMode.Default : HitTestMode.None)
.scale({ x: this.scaleItem === item ? 1.05 : 1, y: this.scaleItem === item ? 1.05 : 1 })
.zIndex(this.dragItem === item ? 1 : 0)
.translate(this.dragItem === item ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
.padding(10)
.gesture(
//以下组合手势为顺序识别,当长按手势事件未正常触发时则不会触发拖动手势事件
GestureGroup(GestureMode.Sequence,
LongPressGesture({
repeat: true,
duration: 50
})//控制触发拖动的长按事件的时间,默认500毫秒,设置小于0为默认值,这里设置为50毫秒
.onAction((_event?: GestureEvent) => {
animateTo({
curve: Curve.Friction,
duration: 300
}, () => {
this.scaleItem = item;
});
})
.onActionEnd(() => {
animateTo({
curve: Curve.Friction,
duration: 300
}, () => {
this.scaleItem = -1;
});
}),
PanGesture({
fingers: 1,
direction: null,
distance: 0
})
.onActionStart(() => {
this.dragItem = item;
this.dragRefOffsetx = 0;
this.dragRefOffsety = 0;
})
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY - this.dragRefOffsety;
this.offsetX = event.offsetX - this.dragRefOffsetx;
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
let index = this.numbers.indexOf(this.dragItem);
// 44 宽度一半 减间距
if (this.offsetY >= this.FIX_VP_Y / 2 &&
(this.offsetX <= this.FIX_VP_X / 2 && this.offsetX >= -this.FIX_VP_X / 2)
&& (index + this.row <= this.lastIndex)) {
//向下滑
this.down(index);
} else if (this.offsetY <= -this.FIX_VP_Y / 2 &&
(this.offsetX <= this.FIX_VP_X / 2 && this.offsetX >= -this.FIX_VP_X / 2)
&& index - this.row >= 0) {
//向上滑
this.up(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 &&
(this.offsetY <= this.FIX_VP_Y / 2 && this.offsetY >= -this.FIX_VP_Y / 2)
&& !(((index - (this.row - 1)) % this.row === 0) || index === this.lastIndex)) {
// ) {
//向右滑
this.right(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 &&
(this.offsetY <= this.FIX_VP_Y / 2 && this.offsetY >= -this.FIX_VP_Y / 2)
&& !(index % this.row === 0)) {
//向左滑
this.left(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& ((index + this.row + 1 <= this.lastIndex && !((index - (this.row - 1)) % this.row === 0)) ||
!((index - (this.row - 1)) % this.row === 0))) {
//向右下滑
this.lowerRight(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2
&& !((index - this.row < 0) || ((index - (this.row - 1)) % this.row === 0))) {
//向右上滑
this.upperRight(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& (!(index % this.row === 0) && (index + (this.row - 1) <= this.lastIndex))) {
//向左下滑
this.lowerLeft(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2
&& !((index <= this.row - 1) || (index % this.row === 0))) {
//向左上滑
this.upperLeft(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& (index === this.lastIndex)) {
//向右下滑(右下角为空)
this.down2(index);
}
});
})
.onActionEnd(() => {
animateTo({
curve: curves.interpolatingSpring(0, 1, 400, 38)
}, () => {
this.dragItem = -1;
});
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17),
delay: 150
}, () => {
this.scaleItem = -1;
});
})
)
.onCancel(() => {
animateTo({
curve: curves.interpolatingSpring(0, 1, 400, 38)
}, () => {
this.dragItem = -1;
});
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17)
}, () => {
this.scaleItem = -1;
});
})
)
}, (item: number) => item.toString())
}
.editMode(true)
.scrollBar(BarState.Off)
// 多列
.columnsTemplate(this.str)
.width('100%')
.backgroundColor('#0D182431')
.padding({ top: 5 })
}
}
更多关于HarmonyOS鸿蒙Next中list嵌套grid,grid的item有手势拖拽,如何设置可以拖拽超出grid的大小的实战教程也可以访问 https://www.itying.com/category-93-b0.html
5 回复
【背景知识】
Grid组件的默认值为true,不允许GridItem子组件超Grid范围。
【解决方案】
Grid组件的默认值为true,为Grid组件设置clip属性值为false值即可。
示例代码:
import { curves } from '@kit.ArkUI'
@ComponentV2
struct ListViewPage {
@Local listData: string[] = []
aboutToAppear(): void {
for (let index = 0; index < 9; index++) {
this.listData.push('itemIndex=' + index)
}
}
build() {
List() {
Repeat(this.listData)
.each((obj: RepeatItem<string>) => {
ListItem() {
Column() {
Text(obj.item).padding(10)
GridPage()
}
}
})
.virtualScroll()
.key((item: string) => item)
}
.height('100%')
.width('100%')
}
}
@Component
export struct GridPage {
//元素数组
@State numbers: number[] = [];
@State row: number = 4;
//元素数组中最后一个元素的索引
@State lastIndex: number = 0;
@State dragItem: number = -1;
@State scaleItem: number = -1;
@State item: number = -1;
@State offsetX: number = 0;
@State offsetY: number = 0;
// row 设置网格列数
private str: string = '';
private dragRefOffsetx: number = 0;
private dragRefOffsety: number = 0;
private FIX_VP_X: number = 108;
private FIX_VP_Y: number = 120;
aboutToAppear() {
for (let i = 1; i <= 8; i++) {
this.numbers.push(i);
}
this.lastIndex = this.numbers.length - 1;
// 多列
for (let i = 0; i < this.row; i++) {
this.str = this.str + '1fr ';
}
}
itemMove(index: number, newIndex: number): void {
if (!this.isDraggable(newIndex)) {
return;
}
let tmp = this.numbers.splice(index, 1);
this.numbers.splice(newIndex, 0, tmp[0]);
}
//向下滑
down(index: number): void {
// 指定固定GridItem不响应事件
if (!this.isDraggable(index + this.row)) {
return;
}
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
// 多列
this.itemMove(index, index + this.row);
}
//向下滑(右下角为空)
down2(index: number): void {
if (!this.isDraggable(index + 3)) {
return;
}
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + 3);
}
//向上滑
up(index: number): void {
if (!this.isDraggable(index - this.row)) {
return;
}
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - this.row);
}
//向左滑
left(index: number): void {
if (!this.isDraggable(index - 1)) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.itemMove(index, index - 1);
}
//向右滑
right(index: number): void {
if (!this.isDraggable(index + 1)) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.itemMove(index, index + 1);
}
//向右下滑
lowerRight(index: number): void {
if (!this.isDraggable(index + this.row + 1)) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + this.row + 1);
}
//向右上滑
upperRight(index: number): void {
if (!this.isDraggable(index - (this.row - 1))) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffsetx += this.FIX_VP_X;
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - (this.row - 1));
}
//向左下滑
lowerLeft(index: number): void {
if (!this.isDraggable(index + (this.row - 1))) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffsety += this.FIX_VP_Y;
this.itemMove(index, index + (this.row - 1));
}
//向左上滑
upperLeft(index: number): void {
if (!this.isDraggable(index - (this.row + 1))) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffsetx -= this.FIX_VP_X;
this.offsetY += this.FIX_VP_Y;
this.dragRefOffsety -= this.FIX_VP_Y;
this.itemMove(index, index - (this.row + 1));
}
//通过元素的索引,控制对应元素是否能移动排序
isDraggable(index: number): boolean {
return index > -1; //恒成立,所有元素均可移动排序
}
build() {
Grid() {
ForEach(this.numbers, (item: number) => {
GridItem() {
Text(item + '')
.fontSize(16)
.width('100%')
.textAlign(TextAlign.Center)
.height(100)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
.shadow(this.scaleItem == item ? {
radius: 70,
color: '#15000000',
offsetX: 0,
offsetY: 0
} :
{
radius: 0,
color: '#15000000',
offsetX: 0,
offsetY: 0
})
.animation({
curve: Curve.Sharp,
duration: 300
})
}
.onAreaChange((_oldVal, newVal) => {
// 多列
this.FIX_VP_X = Math.round(newVal.width as number);
this.FIX_VP_Y = Math.round(newVal.height as number);
})
// 指定固定GridItem不响应事件
.hitTestBehavior(this.isDraggable(this.numbers.indexOf(item)) ? HitTestMode.Default : HitTestMode.None)
.scale({ x: this.scaleItem === item ? 1.05 : 1, y: this.scaleItem === item ? 1.05 : 1 })
.zIndex(this.dragItem === item ? 1 : 0)
.translate(this.dragItem === item ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
.padding(10)
.gesture(
//以下组合手势为顺序识别,当长按手势事件未正常触发时则不会触发拖动手势事件
GestureGroup(GestureMode.Sequence,
LongPressGesture({
repeat: true,
duration: 50
})//控制触发拖动的长按事件的时间,默认500毫秒,设置小于0为默认值,这里设置为50毫秒
.onAction((_event?: GestureEvent) => {
animateTo({
curve: Curve.Friction,
duration: 300
}, () => {
this.scaleItem = item;
});
})
.onActionEnd(() => {
animateTo({
curve: Curve.Friction,
duration: 300
}, () => {
this.scaleItem = -1;
});
}),
PanGesture({
fingers: 1,
direction: null,
distance: 0
})
.onActionStart(() => {
this.dragItem = item;
this.dragRefOffsetx = 0;
this.dragRefOffsety = 0;
})
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY - this.dragRefOffsety;
this.offsetX = event.offsetX - this.dragRefOffsetx;
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
let index = this.numbers.indexOf(this.dragItem);
// 44 宽度一半 减间距
if (this.offsetY >= this.FIX_VP_Y / 2 &&
(this.offsetX <= this.FIX_VP_X / 2 && this.offsetX >= -this.FIX_VP_X / 2)
&& (index + this.row <= this.lastIndex)) {
//向下滑
this.down(index);
} else if (this.offsetY <= -this.FIX_VP_Y / 2 &&
(this.offsetX <= this.FIX_VP_X / 2 && this.offsetX >= -this.FIX_VP_X / 2)
&& index - this.row >= 0) {
//向上滑
this.up(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 &&
(this.offsetY <= this.FIX_VP_Y / 2 && this.offsetY >= -this.FIX_VP_Y / 2)
&& !(((index - (this.row - 1)) % this.row === 0) || index === this.lastIndex)) {
// ) {
//向右滑
this.right(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 &&
(this.offsetY <= this.FIX_VP_Y / 2 && this.offsetY >= -this.FIX_VP_Y / 2)
&& !(index % this.row === 0)) {
//向左滑
this.left(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& ((index + this.row + 1 <= this.lastIndex && !((index - (this.row - 1)) % this.row === 0)) ||
!((index - (this.row - 1)) % this.row === 0))) {
//向右下滑
this.lowerRight(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2
&& !((index - this.row < 0) || ((index - (this.row - 1)) % this.row === 0))) {
//向右上滑
this.upperRight(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& (!(index % this.row === 0) && (index + (this.row - 1) <= this.lastIndex))) {
//向左下滑
this.lowerLeft(index);
} else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2
&& !((index <= this.row - 1) || (index % this.row === 0))) {
//向左上滑
this.upperLeft(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& (index === this.lastIndex)) {
//向右下滑(右下角为空)
this.down2(index);
}
});
})
.onActionEnd(() => {
animateTo({
curve: curves.interpolatingSpring(0, 1, 400, 38)
}, () => {
this.dragItem = -1;
});
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17),
delay: 150
}, () => {
this.scaleItem = -1;
});
})
)
.onCancel(() => {
animateTo({
curve: curves.interpolatingSpring(0, 1, 400, 38)
}, () => {
this.dragItem = -1;
});
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17)
}, () => {
this.scaleItem = -1;
});
})
)
}, (item: number) => item.toString())
}
.editMode(true)
.scrollBar(BarState.Off)
// 多列
.columnsTemplate(this.str)
.width('100%')
// 设置clip属性,允许子组件超出范围
.clip(false)
.backgroundColor('#0D182431')
.padding({ top: 5 })
}
}
更多关于HarmonyOS鸿蒙Next中list嵌套grid,grid的item有手势拖拽,如何设置可以拖拽超出grid的大小的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
标题
这是段落一。
这是段落二。
在HarmonyOS Next中实现GridItem拖拽超出Grid边界的效果,可以通过以下方式实现:
- 使用通用手势方案时,需要为GridItem设置
clip(false)
属性来取消父容器的裁剪限制:
GridItem() {
// 内容
}
.clip(false) // 关键设置
.gesture(
// 拖拽手势代码
)
- 在PanGesture的onActionUpdate回调中,直接更新offsetX/offsetY而不做边界检查:
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY - this.dragRefOffsety;
this.offsetX = event.offsetX - this.dragRefOffsetx;
// 移除原有的位置判断逻辑
})
- 确保父容器Grid也有足够的空间:
Grid()
.width('100%')
.height('100%') // 确保Grid有足够高度
.layoutWeight(1) // 在List中占满剩余空间
这种方案通过禁用裁剪+自由位移的方式,可以实现拖拽元素突破Grid边界的效果。注意要处理好手势结束时元素的归位逻辑。