HarmonyOS 鸿蒙Next中实现拖拽容器大小变化
HarmonyOS 鸿蒙Next中实现拖拽容器大小变化 以下功能应该怎么实现呀,没有思路。
【背景知识】
Grid组件是网格容器,其布局由行和列组成,可以通过设置layoutOptions参数,指定单元格做出不同的布局。组件可以通过设定手势处理,识别长按手势处理,识别长按、拖拽等手势并自定义响应动作。
PanGesture手势事件通过设置distance参数触发阈值,当滑动距离达到阈值时,通过事件回调参数GestureEvent可获取X/Y轴偏移量。onAreaChange是组件区域变化回调,当组件尺寸或位置发生改变时触发,可通过事件参数获取组件宽高、坐标等布局信息。
【解决方案】
- 可以通过设定手势,实现item组件手动拖拽放大,并设定Grid组件layoutOptions参数的irregularIndexes,搭配onGetIrregularSizeByIndex,在item组件尺寸变化后重新布局。示例代码如下:
@Entry
@Component
struct GridItemChangeSizeByDrag {
private gridData: number[] = [1, 2, 3, 4, 5, 6, 7, 8]
@State itemWidths: number[] = []
@State itemWidth: number = 0
@State itemHeight: number = 0
@State irregularIndexes: number[] = []
@State firstLayout: boolean = true
@State layoutOption: GridLayoutOptions = {
regularSize: [1, 1]
}
aboutToAppear(): void {
for (let i=0;i<this.gridData.length;i++) {
this.itemWidths.push(0)
}
}
build() {
Grid(undefined, this.layoutOption) {
ForEach(this.gridData, (num: number, index: number) => {
GridItem() {
Stack({ alignContent: Alignment.Start}) {
Column()
.width(this.itemWidths[index])
.height(this.itemHeight)
.backgroundColor(Color.Orange)
.gesture(
// 设置组合手势,当长按并拖拽时,改变item组件的宽度
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: true, duration: 300 })
.onAction(() => {}),
PanGesture()
.onActionUpdate((event) => {
let idx = this.irregularIndexes.indexOf(index)
if (idx === -1) {
this.itemWidths[index] = this.itemWidth + event.offsetX
} else {
this.itemWidths[index] = this.itemWidth * 2 + event.offsetX
}
})
.onActionEnd(() => {
// 判断拖拽偏移量,并根据偏移量,改变Grid布局选项的参数
if (this.itemWidths[index] < this.itemWidth * 1.5) {
this.itemWidths[index] = this.itemWidth
let idx = this.irregularIndexes.indexOf(index)
if (idx !== -1) {
this.irregularIndexes.splice(idx)
}
this.layoutOption = {
regularSize: [1, 1],
irregularIndexes: this.irregularIndexes,
onGetIrregularSizeByIndex: () => [1, 1] // 组件占布局的1行、1列
}
} else {
this.itemWidths[index] = this.itemWidth * 2
this.irregularIndexes.push(index)
this.layoutOption = {
regularSize: [1, 1],
irregularIndexes: this.irregularIndexes,
onGetIrregularSizeByIndex: () => [1, 2] // 组件占布局的1行、2列
}
}
})
)
)
Column() {
Image($r('app.media.startIcon'))
.height(24)
.objectFit(ImageFit.Contain)
Text(num.toString())
.fontSize('16fp')
.fontWeight(FontWeight.Bold)
}
.width(this.itemWidths[index] - 5) // 露出可拖拽组件的一边,用于拖拽放大
.backgroundColor(Color.Gray)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
.alignSelf(ItemAlign.Start)
.onAreaChange((oldValue, newValue) => {
if (this.firstLayout) {
// 仅在第一个item变化时收集item的宽、高
this.itemHeight = newValue.height as number
this.itemWidth = newValue.width as number
this.firstLayout = false
}
this.itemWidths[index] = newValue.width as number
})
})
}
.height(500)
.width('100%')
.padding(10)
.backgroundColor(Color.Pink)
.columnsTemplate('1fr 1fr')
.rowsGap(5)
.columnsGap(5)
.maxCount(2)
}
}
- 通过onAreaChange回调获取容器尺寸参数,结合被拖动组件的尺寸限制计算位移边界阈值,在PanGesture的onActionUpdate回调中实时计算并约束组件位置偏移量。示例代码如下:
@Entry
@Component
struct DraggableDialog {
@State offsetX: number = 0;
@State offsetY: number = 0;
@State startX: number = 0;
@State startY: number = 0;
@State outerContainerWidth: number = 0;
@State outerContainerHeight: number = 0;
private dialogWidth = 200;
private dialogHeight = 150;
private minX: number = 0;
private maxX: number = 0;
private minY: number = 0;
private maxY: number = 0;
build() {
Stack() {
// 弹框主体
Column() {
Text('可拖拽弹窗')
.fontSize(20)
.fontColor(Color.White)
.margin({ bottom: 10 })
Text('拖拽我移动位置')
.fontSize(16)
.fontColor(Color.White)
}
.width(this.dialogWidth)
.height(this.dialogHeight)
.backgroundColor(Color.Black)
.borderRadius(10)
.shadow({
radius: 8,
color: Color.Gray,
offsetX: 5,
offsetY: 5
})
.padding(20)
.position({ x: this.offsetX, y: this.offsetY })
.gesture(
PanGesture()
.onActionStart((event: GestureEvent) => {
this.startX = this.offsetX;
this.startY = this.offsetY;
})
.onActionUpdate((event: GestureEvent) => {
this.offsetX = this.startX + event.offsetX;
this.offsetY = this.startY + event.offsetY;
// 判断坐标是否超出区域限定,超出后重新赋值
if (this.offsetX <= this.minX) {
this.offsetX = this.minX;
}
if (this.offsetX >= this.maxX) {
this.offsetX = this.maxX;
}
if (this.offsetY <= this.minY) {
this.offsetY = this.minY;
}
if (this.offsetY >= this.maxY) {
this.offsetY = this.maxY;
}
})
)
// 页面背景文字
Text(`页面背景\nX:${this.offsetX}\nY:${this.offsetY}`)
.fontSize(30)
.margin({ top: 300 })
}
.width('100%')
.height('100%')
.backgroundColor(Color.Orange)
.onAreaChange((oldValue: Area, newValue: Area) => {
// 限定区域宽高
this.outerContainerWidth = Number(newValue.width);
this.outerContainerHeight = Number(newValue.height);
// 最大横纵坐标
this.maxX = this.outerContainerWidth - this.dialogWidth;
this.maxY = this.outerContainerHeight - this.dialogHeight;
})
}
}
【总结】
划分区域为上下左右四格,通过onAreaChange获取下组件变化后的xy的坐标,超过一定界限就通过设定手势处理的方法改变容器大小。
更多关于HarmonyOS 鸿蒙Next中实现拖拽容器大小变化的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中实现拖拽调整容器大小,可使用<PanGesture>
和<Column>
/<Row>
组件组合。通过监听PanGesture
的onActionUpdate
事件获取拖拽位移,动态修改容器的width
或height
属性。示例代码框架:
@Entry
@Component
struct ResizableContainer {
@State containerWidth: number = 200
build() {
Column() {
Row()
.width(this.containerWidth)
.height(100)
.backgroundColor(Color.Blue)
PanGesture({ distance: 5 })
.onActionUpdate((event: GestureEvent) => {
this.containerWidth += event.offsetX
})
.height(30)
.backgroundColor(Color.Gray)
}
}
}
核心是通过手势事件实时更新容器尺寸状态变量。
在HarmonyOS Next中实现拖拽容器大小变化,可以通过以下步骤实现:
- 使用PanGesture手势识别器监听拖拽操作
- 结合Column/Row和百分比布局实现动态尺寸调整
- 核心代码示例:
@Entry
@Component
struct ResizableContainer {
@State dividerPosition: number = 0.5 // 初始分割比例
build() {
Column() {
// 上部容器
Column()
.height(`${this.dividerPosition * 100}%`)
.backgroundColor(Color.Red)
// 可拖拽分割线
Row()
.height(20)
.width('100%')
.backgroundColor(Color.Gray)
.gesture(
PanGesture({ fingers: 1 })
.onActionUpdate((event: GestureEvent) => {
// 计算新位置并限制在0.1-0.9范围内
this.dividerPosition = Math.max(0.1,
Math.min(0.9,
this.dividerPosition + event.offsetY / 1000))
})
)
// 下部容器
Column()
.height(`${(1 - this.dividerPosition) * 100}%`)
.backgroundColor(Color.Blue)
}
.height('100%')
}
}
关键点说明:
- 使用PanGesture捕获拖拽手势
- 通过offsetY计算位置变化量
- 使用State变量动态更新容器高度
- 添加比例限制防止容器过小
可以根据实际需求调整布局方向(水平/垂直)和样式。