[Daily HarmonyOS Next Knowledge] Opening Half-Screen via Routing, Custom Grid, Gestures, etc.
1. How to open a half-screen via routing?
Use component navigation (Navigation) uniformly and refer to the page display type NavDestinationMode.DIALOG in the article: Component Navigation.
2. Are there alternative solutions for setting Grid onItemDragStart time and universal examples for multi-column Grid Items?
The default onItemDragStart time for Grid dragging is 170 milliseconds. Currently, the onItemDragStart time cannot be set directly. You can customize the Grid and use the duration of the LongPressGesture in the long-press gesture to control the long-press trigger time for GridItem dragging.
import { curves } from '@kit.ArkUI';
@Entry
@Component
struct Page {
// Element array
@State numbers: number[] = []
// row sets the number of grid columns
private str: string = ''
@State row: number = 4
// Index of the last element in the element array
@State lastIndex: number = 0;
@State dragItem: number = -1
@State scaleItem: number = -1
@State item: number = -1
private dragRefOffsetx: number = 0
private dragRefOffsety: number = 0
@State offsetX: number = 0
@State offsetY: number = 0
private FIX_VP_X: number = 108
private FIX_VP_Y: number = 120
aboutToAppear() {
for (let i = 1; i <= 110; i++) {
this.numbers.push(i)
}
this.lastIndex = this.numbers.length - 1
// Multi-column
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])
}
// Swipe down
down(index: number): void {
// Specify that fixed GridItems do not respond to events
if (!this.isDraggable(index + this.row)) {
return
}
this.offsetY -= this.FIX_VP_Y
this.dragRefOffsety += this.FIX_VP_Y
// Multi-column
this.itemMove(index, index + this.row)
}
// Swipe down (empty lower right corner)
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)
}
// Swipe up
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)
}
// Swipe left
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)
}
// Swipe right
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)
}
// Swipe down-right
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)
}
// Swipe up-right
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))
}
// Swipe down-left
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))
}
// Swipe up-left
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))
}
// Control whether an element can be moved and sorted by its index
isDraggable(index: number): boolean {
return index > -1 // Always true, all elements can be moved and sorted
// return index > 1 // Elements 1 and 2 are fixed and cannot be moved or sorted
}
build() {
Column() {
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) => {
// Multi-column
this.FIX_VP_X = Math.round(newVal.width as number)
this.FIX_VP_Y = Math.round(newVal.height as number)
})
// Specify that fixed GridItems do not respond to events
.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(
// The following combined gestures are recognized in sequence. If the long-press gesture event is not triggered normally, the drag gesture event will not be triggered.
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: true, duration: 50 }) // Control the long-press event time for triggering dragging, default 500ms. Setting less than 0 uses the default; here set to 50ms
.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 Half width minus spacing
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)) {
// Swipe down
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) {
// Swipe up
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)) {
// ) {
// Swipe right
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)) {
// Swipe left
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))) {
// Swipe down-right
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))) {
// Swipe up-right
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))) {
// Swipe down-left
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))) {
// Swipe up-left
this.upperLeft(index)
} else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2
&& (index == this.lastIndex)) {
// Swipe down-right (empty lower right corner)
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())
}
.width('90%')
.editMode(true)
.scrollBar(BarState.Off)
// Multi-column
.columnsTemplate(this.str)
}.width('100%').height('100%').backgroundColor('#0D182431').padding({ top: 5 })
}
}
3. When List, Scroll, Swiper, Web, etc., are nested, there is a sliding conflict. For example, nesting a horizontally sliding Swipe in a List, and nesting a Web in the Swipe, when setting gesture priority, the Web cannot interact with the List.
The problem may be caused by sliding conflicts when the List component is nested with the Web component. Touch test control can be used to avoid this situation.
Reference document: Multi-Level Gesture Events.
4. How to disable the function of closing a custom popup by clicking on empty space?
Set the autoCancel of the custom popup to false to disable closing the popup by clicking on empty space.
Reference document: @ohos.promptAction (Popup).
5. For basic gestures of single-tap and double-tap, how to recognize only double-tap without recognizing single-tap?
Use the exclusive recognition mode of the combined gesture GestureGroup. The double-tap event should be placed before the single-tap event. Exclusive recognition is based on the order of arrangement. If the single-tap event is placed first, only the single-tap event will be recognized.
Reference document: Combined Gestures.
@Entry
@Component
struct TapGestureExample {
build() {
Column() {
Text('Click twice').fontSize(28)
.gesture(GestureGroup(GestureMode.Exclusive,
// Note: Double-tap is placed first
TapGesture({ count: 2 })
.onAction((event?: GestureEvent) => {
console.log('TapGesture 2')
}),
TapGesture({ count: 1 })
.onAction((event?: GestureEvent) => {
console.log('TapGesture 1')
})
)
)
}
}
}
Top comments (0)