[Daily HarmonyOS Next Knowledge] Custom Pop-up Closure, List Sorting Crash, Persistent Storage, Collapsible List, Staking
1. HarmonyOS custom dialog cannot call the close function to close in its own struct?
If you new CustomDialogController at the beginning of page construction, the internal close function can be executed. If it is dynamic, such as clicking a button to new CustomDialogController and assign it to a variable, the internal close cannot be called.
In ArkUI, CustomDialogController is a page-level component whose lifecycle is bound to the page. The use of CustomDialogController in the page is managed by the page's lifecycle, such as initialization after the page is loaded and destruction when the page exits. If created in build, it cannot be guaranteed to be initialized and destroyed at the correct lifecycle stage, resulting in unexpected behavior. In addition, some properties and methods of CustomDialogController are related to the page's lifecycle. For example, the build method is used to build UI components in the page, while the lifecycle-related logic cannot be reused in the function. It is not initialized before build and loses the binding relationship with the page.
2. cppCrash occurs when the HarmonyOS list component drags and sorts subcomponents?
You can refer to the following demo, and the list follows the sliding code when dragged to the bottom:
import curves from '@ohos.curves';
import Curves from '@ohos.curves'
// xxx.ets
@Entry
@Component
struct ListItemExample {
@State private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
@State dragItem: number = -1
@State scaleItem: number = -1
@State neighborItem: number = -1
@State neighborScale: number = -1
private dragRefOffset: number = 0
@State offsetX: number = 0
@State offsetY: number = 0
private ITEM_INTV: number = 120
@State slide:boolean = false
private listScroller: ListScroller = new ListScroller()
// list component specifications
private listArea: Area = {
width: 0,
height: 0,
position: {},
globalPosition: {}
}
scaleSelect(item: number): number {
if (this.scaleItem == item) {
return 1.05
} else if (this.neighborItem == item) {
return this.neighborScale
} else {
return 1
}
}
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, scroller: this.listScroller }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.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 })
}
.margin({ left: 12, right: 12 })
.scale({ x: this.scaleSelect(item), y: this.scaleSelect(item) })
.zIndex(this.dragItem == item ? 1 : 0)
.translate(this.dragItem == item ? { y: this.offsetY } : { y: 0 })
.gesture(
// The following combined gestures are sequentially recognized, and the drag gesture will not be triggered if the long-press gesture event is not triggered normally
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: true })
.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.dragRefOffset = 0
})
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY - this.dragRefOffset
// console.log('Y:' + this.offsetY.toString())
this.neighborItem = -1
let index = this.arr.indexOf(item)
let curveValue = Curves.initCurve(Curve.Sharp)
let value: number = 0
// Calculate the scaling 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;
}
// Prevent offsetY from triggering out-of-bounds logic
if (index === 0) {
this.offsetY = Math.max(0, this.offsetY)
} else if (index === this.arr.length - 1) {
this.offsetY = Math.min(0, this.offsetY)
}
// Get finger information
let fingerInfo = event.fingerList[0]
let clickPercentY = (fingerInfo.globalY - Number(this.listArea.globalPosition
3. Which is better for HarmonyOS user login information persistent storage, PersistentStorage or preference?
For storing user login tokens, is it better to use @ohos.data.preferences
or PersistentStorage?
Persistent storage of user login information can be done using PersistentStorage or Preference, and the specific choice depends on the specific needs and usage scenarios.
-
PersistentStorage is used to persistently store selected AppStorage attributes to ensure that these attributes remain consistent when the application restarts. Its main features include:
- Persistence: Save attributes in AppStorage to the device disk to ensure that data is still available after the application restarts.
- Bidirectional synchronization: PersistentStorage establishes bidirectional synchronization with attributes in AppStorage to ensure data consistency and synchronization.
- Limitations: Support simple types (such as number, string, boolean, etc.) and objects that can be reconstructed by JSON.stringify() and JSON.parse(), but do not support nested objects.
-
Preference is used to store user preference data and is suitable for data that needs to be read and written frequently. Its main features include:
- Reading and writing: Data can be read and written quickly, suitable for scenarios requiring efficient reading and writing.
- Lightweight: Occupy less storage space, suitable for storing simple key-value pair data.
- Sharing: Multiple HAP packages can share Preference data and communicate through ApplicationContext.
Selection suggestions:
- Data type: If the data to be stored is complex, including nested objects or arrays, PersistentStorage is recommended.
- Reading and writing needs: If data needs to be read and written frequently and the data is simple, Preference is recommended.
- Application scenarios: If you need to maintain the consistency of specific attributes when the application restarts, PersistentStorage is recommended. If you need to store user preference data, Preference is recommended.
In summary, the choice between PersistentStorage and Preference to store user login information depends on the specific data type, reading and writing needs, and application scenarios.
4. HarmonyOS implements a list component similar to ExpandList that can be collapsed and opened?
Reference code:
interface IRowItem {
id?: number;
title?: string;
name1?: string;
name2?: string;
name3?: string;
flag?: boolean;
type?: string;
onFlagChange?: () => void;
}
@Entry
@Component
struct CollapseAndExpandDemo {
@Provide("flag") flag: boolean = false
private onFlagChange = () => {
animateTo({
duration: 650,
curve: Curve.Smooth
}, () => {
this.flag = !this.flag;
})
}
build() {
Column() {
Row() {
Image($r("app.media.ic_public_back")).width(20).height(20)
Text('Title').fontSize(18).fontWeight(FontWeight.Bold).margin({ left: 10 })
}.width('100%').margin({ bottom: 30 })
Column() {
RowItem({ props: { title: 'All Types', name1: 'Romance Novels', name2: 'Mystery Suspense', name3: 'Emotional Family' } })
RowItem({
props: { name1: 'Fantasy Novels', name2: 'Thriller/Horror', name3: 'Martial Arts Novels', type: 'DOWN', onFlagChange: this.onFlagChange
}
})
// Directly call the collapse and expand component
CollapseAndExpand({
items: [
{ id: 0, name1: 'Science Fiction Novels', name2: 'Officialdom', name3: 'History/Fictional' },
{ id: 1, name1: 'Workplace Business War', name2: 'Military Spy War', name3: 'World Masterpieces' },
{ id: 2, name1: 'Social Novels', name2: 'Local Novels', name3: 'Modern and Contemporary China' },
{ id: 3, name1: 'Chinese Classics', name2: 'Foreign Novels', name3: 'Novel Works Collection', type: 'UP', onFlagChange: this.onFlagChange }
],
})
RowItem({ props: { title: 'All Prices', name1: 'Free', name2: 'Special Price', name3: 'VIP' } })
RowItem({ props: { title: 'By Popularity', name1: 'By Latest', name2: 'By Favorites', name3: 'By Word Count' } })
}.width('100%')
}
.height('100%')
.padding({ top: 30, right: 30, left: 30 })
}
}
@Component
struct RowItem {
private props: IRowItem;
@Consume("flag") flag: boolean
build() {
Flex() {
Text(this.props.title)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.fontColor(Color.Red)
.margin({ right: 10 })
Flex({ alignItems: ItemAlign.Center }) {
Text(this.props.name1).fontSize(14).margin({ right: 10 })
Text(this.props.name2).fontSize(14).margin({ right: 10 })
Text(this.props.name3).fontSize(14).margin({ right: 10 })
if (!this.flag && this.props.type === 'DOWN' || this.flag && this.props.type === 'UP') {
Image($r("app.media.ic_public_arrow_down_0"))
.width(16)
.height(16)
.objectFit(ImageFit.Contain)
.rotate({ angle: !this.flag && this.props.type === 'DOWN' ? 0 : 180 }) // Rotate 180° after clicking the expand button to display the collapse button
.onClick(() => this.props.onFlagChange())
.transition({ type: TransitionType.All, opacity: 0 })
}
}
.layoutWeight(3)
}.width('100%').height(16).margin({ top: 15 })
}
}
@Component
struct CollapseAndExpand {
private items: IRowItem[] = [];
@Consume("flag") flag: boolean;
build() {
Column() {
ForEach(this.items, (item: IRowItem) => {
RowItem({ props: item })
}, (item: IRowItem) => item.id.toString())
}
.width('100%')
.clip(true)
.height(this.flag ? 130 : 0)
}
}
5. HarmonyOS login logic judgment?
Is there an API implementation similar to View-layer aspect-oriented programming (AOP)? Now there is a requirement that all page clicks need to first determine whether the user is logged in. If logged in, the jump function can be performed. If not logged in, jump to the login page.
Refer to the document: https://developer.huawei.com/consumer/cn/doc/best-practices-V5/bpta-application-aspect-programming-design-V5
Aspect-oriented programming (AOP) is a technology that realizes the unified maintenance of program functions through pre-compilation and dynamic proxy during runtime. The core idea of AOP is to separate the concerns of the program and insert code into the program to realize cross-cutting concerns, so as to isolate each part of the business logic, reduce the coupling between them, improve the maintainability and reusability of the program, and improve the development efficiency.
In AOP, developers can encapsulate cross-cutting concerns by defining aspects without directly modifying the business logic code. This method requires adding functions without modifying the source code, and is often used to separate business code from non-business code, such as parameter verification, log recording, performance statistics and other non-business code, so as to achieve better code decoupling.
HarmonyOS mainly realizes aspect-oriented programming through the staking mechanism and provides the Aspect class, including addBefore, addAfter and replace interfaces. These interfaces can perform pre-staking, post-staking and replacement implementations on class methods at runtime, providing developers with more flexible operation methods. In specific business scenarios, different requirements may require different埋点 functions and log records. By calling the addBefore, addAfter and replace interfaces, various function enhancements and customized requirements for class methods can be realized:
- For method verification requirements, parameter verification can be carried out before or after method execution to ensure the legitimacy of parameters, return values, etc.
- For the requirements of method execution time and frequency statistics, we can insert statistical logic before and after method execution to record the execution time and frequency of the method. Through the combined use of addBefore and addAfter interfaces, the monitoring and statistics of method execution can be easily realized, providing data support for performance optimization.
- For the requirements of replacing classes, we can use the replace interface of AOP to dynamically replace the implementation logic of the original method. This flexible replacement mechanism can realize the replacement or enhancement of method functions without changing the original method call, providing convenience for the function extension of the project.
- For the requirement of obtaining the target package name information when拉起 the application, we can obtain the target package name information and record the log when the application starts. By calling the addBefore interface when the application starts, the monitoring and recording of the application startup process can be realized, providing help for application performance optimization and fault troubleshooting.
Staking principle: The principles of addBefore, addAfter and replace interfaces are based on the ECMAScript semantics of class, that is, the static methods of the class are the attributes of the class, and the instance methods of the class are the attributes of the prototype object of the class.
Top comments (0)