[Daily HarmonyOS Next Knowledge] Image Dragging, Animation Zoom, List Height, Back Button Listening, Divider Color
1. Can images in HarmonyOS Image components be dragged freely?
The Image component supports default dragging effects. When draggable
is set to true
, the component can be dragged. Refer to the document: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/image-V5
In HarmonyOS, the Image component natively supports long-press to trigger dragging events. Specifically, the dragging capability is achieved by setting the draggable
attribute. By default, draggable
is true
for Text, TextInput, TextArea, Hyperlink, Image, and RichEditor components, meaning they can be long-pressed and dragged. You can customize Image-related components to disable dragging for specific cases.
2. Why can't animation zoom be achieved after switching to a model in HarmonyOS?
When ListItem content is an object, direct comparison with ==
for the current item is invalid. Use the index to determine the clicked item instead. Modify as follows: (For other cases, refer to: https://gitee.com/harmonyos-cases/cases/tree/master/CommonAppDevelopment/feature/listexchange)
import curves from '@ohos.curves';
@Entry
@Component
struct model {
@State private arr: titleStrModel[] = []
@State dragItem: titleStrModel = new titleStrModel()
@State scaleItem: titleStrModel = new titleStrModel()
@State neighborItem: titleStrModel = new titleStrModel()
@State selectedIndex: number = -1
@State neighborScale: number = -1
private dragRefOffset: number = 0
@State offsetX: number = 0
@State offsetY: number = 0
private ITEM_INTV: number = 120
aboutToAppear(): void {
const d = new titleStrModel()
d.name = 'wewewe'
const b = new titleStrModel()
b.name = 'adadad'
const h = new titleStrModel()
h.name = 'dfdfdf'
this.arr.push(d)
this.arr.push(b)
this.arr.push(h)
}
// Determine if the item should be scaled based on the current index
scaleSelect(item: titleStrModel): void {
this.selectedIndex = this.arr.indexOf(item)
this.arr[this.selectedIndex].scale = 1.05
}
itemMove(index: number, newIndex: number): void {
let tmp = this.arr.splice(index, 1)
this.arr.splice(newIndex, 0, tmp[0])
}
build() {
Stack() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item: titleStrModel) => {
ListItem() {
Row() {
Text('' + item.name)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
Image($r('app.media.app_icon'))
.width(100)
.height(100)
}
.shadow(this.scaleItem.name == item.name ? { radius: 70, color: Color.Blue, offsetX: 0, offsetY: 0 } :
{ radius: 0, color: Color.Blue, offsetX: 0, offsetY: 0 })
.animation({ curve: Curve.Sharp, duration: 300 })
}
.margin({ left: 12, right: 12 })
.scale({ x: item.scale, y: item.scale })
.zIndex(this.dragItem.name == item.name ? 1 : 0)
.translate(this.dragItem.name == item.name ? { y: this.offsetY } : { y: 0 })
.gesture(
// The following combined gesture is sequentially recognized; the drag gesture will not trigger if the long-press gesture fails
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: true })
.onAction((event?: GestureEvent) => {
animateTo({ curve: Curve.Friction, duration: 300 }, () => {
this.scaleItem = item
this.scaleSelect(item)
})
})
.onActionEnd(() => {
animateTo({ curve: Curve.Friction, duration: 300 }, () => {
this.scaleItem = new titleStrModel()
})
}),
PanGesture({ fingers: 1, direction: null, distance: 0 })
.onActionStart(() => {
this.dragItem = item
this.dragRefOffset = 0
})
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY - this.dragRefOffset
// console.log('Y:' + this.offsetY.toString())
this.neighborItem = new titleStrModel()
let index = this.arr.indexOf(item)
let curveValue = curves.initCurve(Curve.Sharp)
let value: number = 0
// Calculate the scale of adjacent items based on displacement
if (this.offsetY < 0) {
value = curveValue.interpolate(-this.offsetY / this.ITEM_INTV)
this.neighborItem = this.arr[index - 1]
this.neighborScale = 1 - value / 20;
console.log('neighborScale:' + this.neighborScale.toString())
} else if (this.offsetY > 0) {
value = curveValue.interpolate(this.offsetY / this.ITEM_INTV)
this.neighborItem = this.arr[index + 1]
this.neighborScale = 1 - value / 20;
}
// Swap排序 based on displacement
if (this.offsetY > this.ITEM_INTV / 2) {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.offsetY -= this.ITEM_INTV
this.dragRefOffset += this.ITEM_INTV
this.itemMove(index, index + 1)
})
} else if (this.offsetY < -this.ITEM_INTV / 2) {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.offsetY += this.ITEM_INTV
this.dragRefOffset -= this.ITEM_INTV
this.itemMove(index, index - 1)
})
}
})
.onActionEnd((event: GestureEvent) => {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.dragItem = new titleStrModel()
this.neighborItem = new titleStrModel()
// Restore the scale after the animation ends
this.arr[this.arr.indexOf(this.scaleItem)].scale = 1
})
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150
}, () => {
this.scaleItem = new titleStrModel()
})
})
)
.onCancel(() => {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.dragItem = new titleStrModel()
this.neighborItem = new titleStrModel()
})
animateTo({
curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150
}, () => {
this.scaleItem = new titleStrModel()
})
})
)
})
}
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
}
}
class titleStrModel {
name: string = ''
scale: number = 1 // Add the scale attribute, each item has a corresponding scale value
}
3. Why doesn't setting List height to 100% display completely in HarmonyOS?
The issue is resolved by adding layoutWeight(1)
to both the child Column and List. Here’s the explanation:
Principle of layoutWeight: When the parent container's size is fixed, child elements with
layoutWeight
distribute the main-axis space proportionally based on their weights. For example, three elements withlayoutWeight(1)
each will divide the parent container's main-axis space in a 1:1:1 ratio, similar to.width('33%')
,.width('34%')
,.width('33%')
.Tabs behavior: Tabs by default occupy the full screen height, making explicit height calculations (e.g.,
TNAppUIData.getPageHeight()
) ineffective as they override the computed height.
4. How to listen for physical back button presses in HarmonyOS?
When a CustomDialog is open, pressing the physical back button should not close the dialog. Use onBackPress
, which triggers when the back button is clicked and only works for @Entry
-decorated custom components. Return true
to handle the back logic locally without routing; return false
(or no return value) to use default routing. Refer to: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-custom-component-lifecycle-V5#onbackpress
onBackPress?(): void | boolean
Example:
// xxx.ets
@Entry
@Component
struct IndexComponent {
@State textColor: Color = Color.Black;
onPageShow() {
this.textColor = Color.Blue;
console.info('IndexComponent onPageShow');
}
onPageHide() {
this.textColor = Color.Transparent;
console.info('IndexComponent onPageHide');
}
onBackPress() {
this.textColor = Color.Red;
console.info('IndexComponent onBackPress');
return true; // Handle back logic locally, prevent default routing
}
build() {
Column() {
Text('Hello World')
.fontColor(this.textColor)
.fontSize(30)
.margin(30)
}.width('100%')
}
}
5. How to match the color before startMargin in List divider with ListItem in HarmonyOS?
The built-in divider
attribute of List cannot meet this requirement. Customize the divider as follows:
build() {
Row() {
List() {
ListItem() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
Text('test')
Divider()
.color(Color.Red)
.strokeWidth(5)
.margin({ left: 50 })
}
}.backgroundColor(Color.Blue)
.height(100)
ListItem() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
Text('test')
Divider()
.color(Color.Red)
.strokeWidth(5)
.margin({ left: 50, bottom: 0 })
}
}.backgroundColor(Color.Orange)
.height(100)
ListItem() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
Text('test')
Divider()
.color(Color.Red)
.strokeWidth(5)
.margin({ left: 50 })
}
}.backgroundColor(Color.Yellow)
.height(100)
}
.width('100%').height('100%')
.backgroundColor(Color.Gray)
}
}
Top comments (0)