[Daily HarmonyOS Next Knowledge] Span Issues, Component Identification Attributes, Property Animation Callbacks, Text-Image Mixing, Relative Layout Problems
1. HarmonyOS Ellipsis Display Issues with Two Spans (Chinese Followed by Overflowing Text)?
When using two spans where the first contains Chinese characters and the second is extremely long, the Ellipsis feature may display incorrectly.
Solution: Set the line break rule using .wordBreak(WordBreak.BREAK_ALL)
.
Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-text-V5#ZH-CN_TOPIC_0000001893209429__wordbreak11
2. How to Obtain Component Identification Attributes in HarmonyOS?
Use getInspectorByKey
+ getInspectorByKey(id: string): string
to retrieve all attributes of a component with a specified ID (excluding child component information).
Reference code example:
// xxx.ets
import { IntentionCode } from '@ohos.multimodalInput.intentionCode'
class Utils {
static rect_left: number;
static rect_top: number;
static rect_right: number;
static rect_bottom: number;
static rect_value: Record<string, number>;
// Get the rectangular coordinates of a component
static getComponentRect(key: string): Record<string, number> {
let strJson = getInspectorByKey(key);
let obj: Record<string, string> = JSON.parse(strJson);
console.info("[getInspectorByKey] Current component obj is: " + JSON.stringify(obj));
let rectInfo: string[] = JSON.parse('[' + obj.$rect + ']');
console.info("[getInspectorByKey] rectInfo is: " + rectInfo);
Utils.rect_left = JSON.parse('[' + rectInfo[0] + ']')[0];
Utils.rect_top = JSON.parse('[' + rectInfo[0] + ']')[1];
Utils.rect_right = JSON.parse('[' + rectInfo[1] + ']')[0];
Utils.rect_bottom = JSON.parse('[' + rectInfo[1] + ']')[1];
return Utils.rect_value = {
"left": Utils.rect_left, "top": Utils.rect_top, "right": Utils.rect_right, "bottom": Utils.rect_bottom
};
}
}
@Entry
@Component
struct IdExample {
@State text: string = '';
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button() {
Text('onKeyTab').fontSize(25).fontWeight(FontWeight.Bold);
}.margin({ top: 20 }).backgroundColor('#0D9FFB')
.onKeyEvent(() => {
this.text = "onKeyTab";
});
Button() {
Text('click to start').fontSize(25).fontWeight(FontWeight.Bold);
}.margin({ top: 20 })
.onClick(() => {
console.info(getInspectorByKey("click"));
console.info(JSON.stringify(getInspectorTree()));
this.text = "Button 'click to start' is clicked";
setTimeout(() => {
sendEventByKey("longClick", 11, ""); // Send long click event to component with id "longClick"
}, 2000);
}).id('click');
Button() {
Text('longClick').fontSize(25).fontWeight(FontWeight.Bold);
}.margin({ top: 20 }).backgroundColor('#0D9FFB')
.gesture(
LongPressGesture().onActionEnd(() => {
console.info('long clicked');
this.text = "Button 'longClick' is longclicked";
setTimeout(() => {
let rect = Utils.getComponentRect('onTouch'); // Get coordinates of component with id "onTouch"
let touchPoint: TouchObject = {
id: 1,
type: TouchType.Down,
x: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to component's top-left
y: rect.top + (rect.bottom - rect.top) / 2, // Vertical coordinate relative to component's top-left
screenX: rect.left + (rect.right - rect.left) / 2, // Deprecated in API10, use windowX instead
screenY: rect.left + (rect.right - rect.left) / 2, // Deprecated in API10, use windowY instead
windowX: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to app window's top-left
windowY: rect.left + (rect.right - rect.left) / 2, // Vertical coordinate relative to app window's top-left
displayX: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to device screen's top-left
displayY: rect.left + (rect.right - rect.left) / 2, // Vertical coordinate relative to device screen's top-left
};
sendTouchEvent(touchPoint); // Send touch down event
touchPoint.type = TouchType.Up;
sendTouchEvent(touchPoint); // Send touch up event
}, 2000);
})).id('longClick');
Button() {
Text('onTouch').fontSize(25).fontWeight(FontWeight.Bold);
}.type(ButtonType.Capsule).margin({ top: 20 })
.onClick(() => {
console.info('onTouch is clicked');
this.text = "Button 'onTouch' is clicked";
setTimeout(() => {
let rect = Utils.getComponentRect('onMouse'); // Get coordinates of component with id "onMouse"
let mouseEvent: MouseEvent = {
button: MouseButton.Left,
action: MouseAction.Press,
x: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to component's top-left
y: rect.top + (rect.bottom - rect.top) / 2, // Vertical coordinate relative to component's top-left
screenX: rect.left + (rect.right - rect.left) / 2, // Deprecated in API10, use windowX instead
screenY: rect.left + (rect.right - rect.left) / 2, // Deprecated in API10, use windowY instead
windowX: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to app window's top-left
windowY: rect.left + (rect.right - rect.left) / 2, // Vertical coordinate relative to app window's top-left
displayX: rect.left + (rect.right - rect.left) / 2, // Horizontal coordinate relative to device screen's top-left
displayY: rect.left + (rect.right - rect.left) / 2, // Vertical coordinate relative to device screen's top-left
stopPropagation: () => {},
timestamp: 1,
target: {
area: {
width: 1,
height: 1,
position: {
x: 1,
y: 1
},
globalPosition: {
x: 1,
y: 1
}
}
},
source: SourceType.Mouse,
pressure: 1,
tiltX: 1,
tiltY: 1,
sourceTool: SourceTool.Unknown
};
sendMouseEvent(mouseEvent); // Send mouse event
}, 2000);
}).id('onTouch');
Button() {
Text('onMouse').fontSize(25).fontWeight(FontWeight.Bold);
}.margin({ top: 20 }).backgroundColor('#0D9FFB')
.onMouse(() => {
console.info('onMouse');
this.text = "Button 'onMouse' in onMouse";
setTimeout(() => {
let keyEvent: KeyEvent = {
type: KeyType.Down,
keyCode: 2049,
keyText: 'tab',
keySource: 4,
deviceId: 0,
metaKey: 0,
timestamp: 0,
stopPropagation: () => {},
intentionCode: IntentionCode.INTENTION_DOWN
};
sendKeyEvent(keyEvent); // Send key event
}, 2000);
}).id('onMouse');
Text(this.text).fontSize(25).padding(15);
}
.width('100%').height('100%');
}
}
3. Does HarmonyOS Property Animation Support Callbacks During Execution?
Property animations do not support callbacks during execution. However, Animator animations support the onFrame
callback to return the current animation progress.
4. How to Arrange Images and Text in HarmonyOS?
To achieve a layout where images and text are arranged such that the second line starts with an image from the leftmost position:
Solution: Add .wordBreak(WordBreak.BREAK_ALL)
to the Text component.
5. HarmonyOS RelativeContainer Component Limitations Preventing Desired UI Effects?
The RelativeContainer component has limitations for certain UI requirements:
-
Left element occupying remaining width: Use
layoutWeight
property. -
Element B's height depends on Element A's adaptive height: Use
onAreaChange
event to dynamically set B's height. - Unable to remove default Indicator margins: Swiper's built-in indicator style has fixed dimensions. Adjust component margins or create a custom indicator.
- Getting maximum height of Swiper content: Currently no API to get dimensions of non-visible Swiper components. Set a maximum height instead.
Reference demo:
@Entry
@Component
struct Index {
private swiperController: SwiperController = new SwiperController();
@State rightHeight: Length = 80;
@State swiperData: string[] = ['', ''];
@State currentIndex: number = 0;
build() {
Column() {
Text('近期需关注')
.textAlign(TextAlign.Start)
.fontSize(20)
.width('100%');
Text('宝宝能独坐啦').margin({ bottom: 10 })
.textAlign(TextAlign.Start)
.width('100%');
Row() {
Column() {
Swiper(this.swiperController) {
Column() {
Text('疫苗对比1')
.fontSize('20');
Text('#流感疫苗选三价还是四价?')
.textAlign(TextAlign.Start)
.width('100%');
}
Column() {
Text('疫苗对比2')
.fontSize('20');
Text('#2流感疫苗选三价还是四价?流感疫苗选三价还是四价?流感疫苗选三价还是四价?2');
}
}
.indicator(false)
.onChange(index => {
this.currentIndex = index;
});
// Custom indicator
Row({ space: 20 }) {
ForEach(this.swiperData, (item: string, index: number) => {
Shape() {
Rect().width(12).height(5).radius(5).fill(index !== this.currentIndex ? Color.Black : Color.Red)
.fillOpacity(0.6);
}
});
}
.margin({ top: 10 });
}
.layoutWeight(1) // Occupy remaining width
.backgroundColor(Color.Green)
.onAreaChange((newA: Area, oldA: Area) => {
this.rightHeight = oldA.height;
})
.padding(10);
Row() {
Column() {
Image($r('app.media.app_icon'))
.width(30)
.height(30);
Text("记头围");
}
Column() {
Image($r('app.media.app_icon'))
.width(30)
.height(30);
Text("记体重");
}
.margin({ left: 10 });
}
.backgroundColor(Color.Green)
.padding({ top: '20.00vp', right: '10.00vp', bottom: '20.00vp', left: '10.00vp' })
.margin({ left: 10 })
.height(this.rightHeight);
}
.margin({ bottom: 10 });
}
.width('100%')
.padding(10)
.backgroundColor(Color.Gray);
}
}
Top comments (0)