[Daily HarmonyOS Next Knowledge] Global Font, Non-null Assertion, Component Dialog Usage, List Scroll to Top, Soft Keyboard Control
1、HarmonyOS Global Font
- How to globally replace the system default font with a custom font without setting it for each page or component?
- How to globally set the app font to not change with system font size and weight?
1. Global Font
1.1 To use a custom font globally, register it in the onWindowStageCreate
lifecycle of EntryAbility.ets
via the windowStage.loadContent
callback:
import font from '@ohos.font';
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/registerFont', (err, data) => {
if (err.code) {
return;
}
font.registerFont({
familyName: 'Beacon',
familySrc: $rawfile('font/Beacon.otf')
})
font.registerFont({
familyName: 'SF',
familySrc: $rawfile('font/SF-Pro-Text-Italic.ttf')
})
});
}
1.2 In registerFont.ets
, use the registered familyName
in the page:
@Entry
@Component
struct registerFont {
@State message: string = 'test'
build() {
Column() {
Text(this.message)
.align(Alignment.Center)
.fontSize(50)
.fontFamily('Beacon')
Text(this.message)
.align(Alignment.Center)
.fontSize(50)
.fontFamily('SF')
}.width('100%')
}
}
Reference link: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-font-V5
- Currently, only font sizes set in
fp
(font pixel) units change with the system. To prevent changes, usevp
units instead. By default, 1 fp = 1 vp. If the user scales the system font, 1 fp = 1 vp * scale factor. - Pixel units: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-pixel-units-V5
- Font pixel unit (fp): https://developer.huawei.com/consumer/cn/doc/design-guides/design-layout-basics-0000001795579413#section1964738154814
- Use dynamic attributes or custom classes implementing the
AttributeModifier
interface to define fixed styles for Text components. Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-universal-attributes-attribute-modifier-V5
2、What is the role of the exclamation mark after a variable in HarmonyOS?
What does the exclamation mark after a variable do? Multiple exclamation marks can be added without errors. What's the difference between one and multiple exclamation marks?
Non-null assertion operator: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/introduction-to-arkts-V5
3、HarmonyOS API12: Component usage scenario for Dialog
There are two pages, A and B. Page A pops up a CustomDialog, pushes to page B, and returns to page A to refresh the dialog data. Current issues:
- The dialog is closed before entering page B (if not closed, it remains when entering page B).
- The dialog is reopened when returning from page B to A (unfriendly to users due to repeated openings).
Expectation: The dialog should not be closed before entering B, and should not be reopened after returning to A.
Question: Why does the dialog have a higher hierarchy than pages? The dialog on page A should belong to A, but why does it persist when entering page B?
Use NavDestinationMode.DIALOG
to implement custom popups. Refer to the demo:
interface RouterParams {
name?: string,
onPop?: (data: PopInfo) => void
}
// Encapsulated routing utility class with custom popup registration
class AppRouter {
private static instance = new AppRouter();
private pathStack: NavPathStack = new NavPathStack();
public static getInstance(): AppRouter {
return AppRouter.instance;
}
public getPathStack(): NavPathStack {
return this.pathStack;
}
private pushPath(name: string): void {
this.pathStack.pushPath({ name: name })
}
public static push(name: string): void {
AppRouter.instance.pushPath(name);
}
public static openDialog(name: string, params?: RouterParams): void {
AppRouter.instance.pathStack.pushPath({
name: name, param: params, onPop: (data: PopInfo) => {
if (params?.onPop) {
params.onPop!(data);
}
}
});
}
public static pop(): void {
AppRouter.instance.pathStack.pop();
}
}
@Component
// NavDestinationMode.DIALOG
struct DefaultDialog {
build() {
NavDestination() {
Stack({ alignContent: Alignment.Center }) {
Column() {
}
.width("100%")
.height("100%")
.backgroundColor('rgba(0,0,0,0.5)')
.transition(
TransitionEffect.OPACITY.animation({
duration: 300,
curve: Curve.Friction
})
)
.onClick(() => {
AppRouter.pop();
})
Column() {
Text("dialogA")
.fontColor(Color.White)
Button("push pageC", { stateEffect: true, type: ButtonType.Capsule })
.onClick(() => {
AppRouter.push("pageC")
})
}
.width("50%")
.height("30%")
.backgroundColor('#ffae2d2d')
.transition(
TransitionEffect.scale({ x: 0, y: 0 }).animation({
duration: 300,
curve: Curve.Friction
})
)
}
.width("100%")
.height("100%")
}
.mode(NavDestinationMode.DIALOG)
.hideTitleBar(true)
}
}
@Component
struct PageA {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Button('push dialogA', { stateEffect: true, type: ButtonType.Capsule })
.onClick(() => {
AppRouter.openDialog("DefaultDialog");
}).margin(10)
}.title('PageA')
}
}
@Component
struct PageC {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('pageC')
.margin(10)
Button('返回', { stateEffect: true, type: ButtonType.Capsule })
.onClick(() => {
AppRouter.pop();
}).margin(10)
}
}.mode(NavDestinationMode.STANDARD)
}
}
@Entry
@Component
struct pageRoot01 {
@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()
// Visibility control set to non-occupying
@State visible: Visibility = Visibility.None
@Builder
PagesMap(name: string) {
if (name == 'pageA') {
PageA()
} else if (name == 'pageC') {
PageC()
} else if (name == 'DefaultDialog') {
DefaultDialog()
}
}
build() {
// Register NavPathStack in the root page
Navigation(AppRouter.getInstance().getPathStack()) {
Stack({ alignContent: Alignment.Center }) {
Column() {
Button('push PageA', { stateEffect: true, type: ButtonType.Capsule })
.onClick(() => {
AppRouter.push('pageA')
})
}
}
.height("100%")
.width("100%")
}
.hideTitleBar(true)
.navDestination(this.PagesMap)
}
}
4、HarmonyOS Scroller cannot scroll to the top, and the bottom input box pushes the page up
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
private list: string[] = []
private scroller = new Scroller()
aboutToAppear(): void {
for (let i = 0; i < 200; i++) {
this.list.push(`选项${i}`)
}
}
build() {
Column() {
Text('我是固定标题')
.fontColor(Color.White)
.backgroundColor(Color.Red)
List({ scroller: this.scroller }) {
ForEach(this.list, (item: string) => {
ListItem() {
Text(item)
.padding(10)
}
})
}
.layoutWeight(1)
.onAppear(() => {
this.scroller.scrollEdge(Edge.Bottom)
this.scroller.scrollToIndex(this.list.length - 1)
this.scroller.scrollToIndex(this.list.length)
})
TextInput()
}
.height('100%')
.width('100%')
}
}
Issues:
- After loading data, the list needs to scroll to the bottom (chat page scenario). Using
this.scroller.scrollEdge
andthis.scroller.scrollToIndex
does not scroll to the bottom. - With a bottom input box, the entire page is pushed up when focused. How to fix the top control and allow normal list scrolling?
Solutions:
- Use
initialIndex: this.list.length - 1
:
List({ scroller: this.scroller, initialIndex: this.list.length - 1 }) {
ForEach(this.list, (item: string) => {
ListItem() {
Text(item)
.padding(10)
}
})
}
- Set
windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE)
. Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-universal-attributes-expand-safe-area-V5#getkeyboardavoidmode
5、HarmonyOS methods to actively pop up and close the soft keyboard
When entering a search page, the soft keyboard needs to be actively popped up before searching and closed after searching.
@Entry
@Component
struct TextInputPage {
controller: TextInputController = new TextInputController()
@State inputValue: string = ""
build() {
RelativeContainer() {
TextInput({ controller: this.controller, text: this.inputValue })
.key('TextInput')
.margin(10)
.border({ width: 1 })
.height('48vp')
.defaultFocus(true)
}
.height('100%')
.width('100%')
}
}
Top comments (0)