RichEditor
The <RichEditor> is a component that supports interactive text editing and mixture of text and imagery.
NOTE
This component is supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version.
Drag effects such as floating for the <RichEditor> component can only be implemented through the onDragStart event.
Child Components
Not supported
APIs
RichEditor(value: RichEditorOptions)
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorOptions | Yes | Options for initializing the component. |
Attributes
The universal attributes are supported.
NOTE
The default value of the clip attribute is true. The align attribute supports only the start, center, and end options.
Name | Type | Description |
---|---|---|
customKeyboard | CustomBuilder | Custom keyboard. NOTE When a custom keyboard is set, activating the text box opens the specified custom component, instead of the system input method. The custom keyboard's height can be set through the height attribute of the custom component's root node, and its width is fixed at the default value. The custom keyboard is displayed on top of the current page, without compressing or raising the page. The custom keyboard cannot obtain focus, but it blocks gesture events. By default, the custom keyboard is closed when the input component loses the focus. When a custom keyboard is set, the text box does not support camera input, even when the device supports. |
bindSelectionMenu | { spantype: RichEditorSpanType, content: CustomBuilder, responseType: ResponseType | RichEditorResponseType11+, options?: SelectionMenuOptions } |
Custom context menu on text selection. Default value: { spanType: RichEditorSpanType.TEXT responseType: ResponseType.LongPress Other: null } |
copyOptions | CopyOptions | Whether copy and paste is allowed for text content. Default value: CopyOptions.LocalDevice NOTE If copyOptions is not set to CopyOptions.None, long-pressing the text content displays the context menu. If a custom context menu is defined through bindSelectionMenu or other approaches, it will be displayed. If copyOptions is set to CopyOptions.None, copy and paste is not allowed. |
enableDataDetector11+ | boolean | Whether to enable text recognition. Default value: false NOTE The recognized entity is in the following style settings: fontColor: Color.Blue decoration: { type: TextDecorationType.Underline, color: Color.Blue } For this API to work, the target device must provide the text recognition capability. When enableDataDetector is set to true and dataDetectorConfig is not set, all types of entities are recognized by default. When copyOptions is set to CopyOptions.None, this API does not take effect. This API does not work for the node text of addBuilderSpan. |
dataDetectorConfig11+ | { types: TextDataDetectorType, onDetectResultUpdate: (callback:(result: string) => void) } |
Text recognition configuration. Default value: { types: [ ], onDetectResultUpdate: null } NOTE This API must be used together with enableDataDetector. It takes effect only when enableDataDetector is set to true. types: types of entities that can be recognized from text. Values null and [] indicate that all types of entities can be recognized. onDetectResultUpdate: callback invoked when text recognition succeeds. result: text recognition result, in JSON format. |
Events
In addition to the universal events, the following events are supported.
Name | Description |
---|---|
onReady(callback: () => void) | Triggered when initialization of the component is completed. |
onSelect(callback: (value: RichEditorSelection) => void) | Triggered when content is selected. If a mouse device is used for selection, this event is triggered when the mouse button is released. If a finger is used for selection, this event is triggered when the finger is released. - value: information about all selected spans. |
aboutToIMEInput(callback: (value: RichEditorInsertValue) => boolean) | Triggered when content is about to be entered in the input method. - value: content to be entered in the input method. |
onIMEInputComplete(callback: (value: RichEditorTextSpanResult) => void) | Triggered when text input in the input method is complete. - value: text span information after text input is completed. |
aboutToDelete(callback: (value: RichEditorDeleteValue) => boolean) | Triggered when content is about to be deleted in the input method. - value: information about the text span where the content to be deleted is located. |
onDeleteComplete(callback: () => void) | Triggered when deletion in the input method is completed. |
onPaste11+(callback: (event?: PasteEvent) => void) | Triggered when the paste is about to be completed. NOTE The default system paste and drag behaviors are available for text only. You can use this API to overwrite the default system behaviors so that both images and text can be pasted. |
RichEditorInsertValue
Describes the text to be inserted.
Name | Type | Mandatory | Description |
---|---|---|---|
insertOffset | number | Yes | Offset of the text to be inserted. |
insertValue | string | Yes | Content of the text to be inserted. |
RichEditorDeleteValue
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | Yes | Offset of the text to be deleted. |
direction | RichEditorDeleteDirection | Yes | Direction of the delete operation. |
length | number | Yes | Length of the content to be deleted. |
richEditorDeleteSpans | Array<RichEditorTextSpanResult | RichEditorImageSpanResult> | Yes | Information about the text or image spans to be deleted. |
RichEditorDeleteDirection
Enumerates the directions of the delete operation.
Name | Description |
---|---|
BACKWARD | Backward. |
FORWARD | Forward. |
RichEditorTextSpanResult
Provides the text span information.
Name | Type | Mandatory | Description |
---|---|---|---|
spanPosition | RichEditorSpanPosition | Yes | Span position. |
value | string | Yes | Text span content. |
textStyle | RichEditorTextStyleResult | Yes | Text span style. |
offsetInSpan | [number, number] | Yes | Start and end positions of the valid content in the text span. |
valueResource11+ | Resource | No | Content of the <SymbolSpan> component. |
SymbolSpanStyle11+ | RichEditorSymbolSpanStyle | No | Style of the <SymbolSpan> component. |
RichEditorSpanPosition
Provides the span position information.
Name | Type | Mandatory | Description |
---|---|---|---|
spanIndex | number | Yes | Span index. |
spanRange | [number, number] | Yes | Start and end positions of the span content in the <RichEditor> component. |
RichEditorSpanType
Provides the span type information.
Name | Type | Mandatory | Description |
---|---|---|---|
TEXT | number | Yes | Text span. |
IMAGE | number | Yes | Image span. |
MIXED | number | Yes | Mixed span, which contains both text and imagery. |
RichEditorTextStyleResult
Provides the text span style information returned by the backend.
Name | Type | Mandatory | Description |
---|---|---|---|
fontColor | ResourceColor | Yes | Font color. |
fontSize | number | Yes | Font size. |
fontStyle | FontStyle | Yes | Font style. |
fontWeight | number | Yes | Font weight. |
fontFamily | string | Yes | Font family. |
decoration | { type: TextDecorationType, color?: ResourceColor } |
Yes | Style and color of the text decorative line. |
RichEditorImageSpanResult
Provides the image span style information returned by the backend.
Name | Type | Mandatory | Description |
---|---|---|---|
size | [number, number] | Yes | Width and height of the image. |
verticalAlign | ImageSpanAlignment | Yes | Vertical alignment mode of the image. |
objectFit | ImageFit | Yes | Scale mode of the image. |
RichEditorOptions
Defines the options for initializing the <RichEditor> component.
Name | Type | Mandatory | Description |
---|---|---|---|
controller | RichEditorController | Yes | Controller for the <RichEditor> component. |
RichEditorController
Implements the controller for the <RichEditor> component.
Objects to Import
controller: RichEditorController = new RichEditorController()
getCaretOffset
getCaretOffset(): number
Obtains the current cursor position.
Return value
Type | Description |
---|---|
number | Cursor position. |
setCaretOffset
setCaretOffset(offset: number): boolean
Sets the cursor position.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | Yes | Offset of the cursor. If the value is out of the text range, the setting fails. |
Return value
Type | Description |
---|---|
boolean | Whether the cursor position is set successfully. |
addTextSpan
addTextSpan(value: string, options?: RichEditorTextSpanOptions): number
Adds a text span.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | string | Yes | Text content. |
options | RichEditorTextSpanOptions | No | Text options. |
Return value
Type | Description |
---|---|
number | Position of the added text span. |
addImageSpan
addImageSpan(value: PixelMap | ResourceStr, options?: RichEditorImageSpanOptions): number
Adds an image span.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | PixelMap|ResourceStr | Yes | Image content. |
options | RichEditorImageSpanOptions | No | Image options. |
Return value
Type | Description |
---|---|
number | Position of the added image span. |
addBuilderSpan11+
addBuilderSpan(value: CustomBuilder, options?: RichEditorBuilderSpanOptions): number
NOTE
- This API adds a builder span to take up space in the layout. It calls the system measure method to calculate the actual length, width, and position.
- You can use RichEditorBuilderSpanOptions to set the index of the builder in the <RichEditor> component (with one character as the unit).
- The builder span is not focusable or draggable. It supports some universal attributes and can take up space in the layout or be deleted as an image span. Its length equals a character.
- The builder span does not allow for custom menus set through bindSelectionMenu.
- The information about the builder span cannot be obtained through getSpans, getSelection, onSelect, or aboutToDelete.
- The builder span cannot be updated using updateSpanStyle or updateParagraphStyle.
- Copying or pasting the builder span does not take effect.
- The layout constraints of the builder span are passed in from the <RichEditor> component. If the size of the outermost component in the builder span is not set, the size of the <RichEditor> is used as the value of maxSize.
- The gesture event mechanism of the builder span is the same as the universal gesture event mechanism. If transparent transmission is not set in the builder, only the child components in the builder respond.
The following universal attributes are supported: size, padding, margin, aspectRatio, borderStyle, borderWidth, borderColor, borderRadius, backgroundColor, backgroundBlurStyle, opacity, blur, backdropBlur, shadow, grayscale, brightness, saturate, contrast, invert, sepia, hueRotate, colorBlend, sphericalEffect, lightUpEffect, pixelStretchEffect, linearGradientBlur, clip, mask, foregroundBlurStyle, accessibilityGroup, accessibilityText, accessibilityDescription, accessibilityLevel
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | CustomBuilder | Yes | Custom component. |
options | RichEditorBuilderSpanOptions | No | Builder options. |
Return value
Type | Description |
---|---|
number | Position of the added builder span. |
addSymbolSpan11+
addSymbolSpan(value: Resource, options?: RichEditorSymbolSpanOptions ): number
Adds a symbol span to the <Richeditor> component.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | Resource | Yes | Content of the symbol span. |
options | RichEditorSymbolSpanOptions | No | Options of the symbol span. |
Return value
Type | Description |
---|---|
number | Position of the added symbol span. |
getTypingStyle11+
getTypingStyle(): RichEditorTextStyle
Obtains the preset typing style.
Return value
Type | Description |
---|---|
RichEditorTextStyle | Preset typing style. |
setTypingStyle11+
setTypingStyle(value: RichEditorTextStyle): void
Sets the typing style.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorTextStyle | Yes | Typing style to set. |
updateSpanStyle
updateSpanStyle(value: RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions | RichEditorUpdateSymbolSpanStyleOptions): void
Updates the text or image span style.
If only part of a span is updated, the span is split into multiple spans based on the updated part and the non-updated part.
Calling this API will not close the custom context menu on selection by default.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions | RichEditorUpdateSymbolSpanStyleOptions11+ | Yes | Text or image span style options. |
updateParagraphStyle11+
updateParagraphStyle(value: RichEditorParagraphStyleOptions): void
Updates the paragraph style.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorParagraphStyleOptions | Yes | Information about the paragraph style. |
getSpans
getSpans(value?: RichEditorRange): Array<RichEditorTextSpanResult| RichEditorImageSpanResult>
Obtains span information.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorRange | No | Range of the target span. |
Return value
Type | Description |
---|---|
Array<RichEditorTextSpanResult | RichEditorImageSpanResult> | Text and image span information. |
deleteSpans
deleteSpans(value?: RichEditorRange): void
Deletes the text and image spans in a specified range.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorRange | No | Range of the target spans. If this parameter is left empty, all text and image spans will be deleted. |
getParagraphs11+
getParagraphs(value?: RichEditorRange): Array<RichEditorParagraphResult>
Obtains the specified paragraphs.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
value | RichEditorRange | No | Range of the paragraphs to obtain. |
Return value
Type | Description |
---|---|
Array<RichEditorParagraphResult> | Information about the selected paragraphs. |
closeSelectionMenu
closeSelectionMenu(): void
Closes the custom or default context menu on selection.
setSelection11+
setSelection(selectionStart: number, selectionEnd: number)
Sets the range of the current text selection. The selected text is highlighted.
If both selectionStart and selectionEnd are set to -1, all text is selected.
If the context menu with a handle is displayed before this API is called, calling this API will change the menu position, without closing the menu.
If the context menu without a selection handle is displayed before this API is called, calling this API will not close the context menu, nor change the menu position.
If no context menu is displayed before the API is called, calling this API will not display the context menu.
If this API is called when the text box is not focused, the selected effect is not displayed.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
selectionStart | number | Yes | Start position of the text selection. |
selectionEnd | number | Yes | End position of the text selection. |
getSelection11+
getSelection(): RichEditorSelection
Obtains the current text selection.
Return value
Type | Description |
---|---|
RichEditorSelection | Provides information about the selected content. |
RichEditorSelection
Provides information about the selected content.
Name | Type | Mandatory | Description |
---|---|---|---|
selection | [number, number] | Yes | Range of the selected. |
spans | Array<RichEditorTextSpanResult| RichEditorImageSpanResult> | Yes | Span information. |
RichEditorUpdateTextSpanStyleOptions
Defines the text span style options.
Name | Type | Mandatory | Description |
---|---|---|---|
start | number | No | Start position of the span whose style needs to be updated. If this parameter is left empty or set to a negative value, the value 0 will be used. |
end | number | No | End position of the span whose style needs to be updated. If this parameter is left empty or set to a value beyond the range, the end of the span will be used as the end position. |
textStyle | RichEditorTextStyle | Yes | Text style. |
NOTE
If the value of start is greater than that of end, the value 0 will be used as start and the end of the span as end.
RichEditorUpdateImageSpanStyleOptions
Defines the image span style options.
Name | Type | Mandatory | Description |
---|---|---|---|
start | number | No | Start position of the span whose style needs to be updated. If this parameter is left empty or set to a negative value, the value 0 will be used. |
end | number | No | End position of the span whose style needs to be updated. If this parameter is left empty or set to a value beyond the range, the end of the span will be used as the end position. |
imageStyle | RichEditorImageSpanStyle | Yes | Image style. |
NOTE
If the value of start is greater than that of end, the value 0 will be used as start and the end of the span as end.
RichEditorUpdateSymbolSpanStyleOptions11+
Defines the symbol span style options.
Name | Type | Mandatory | Description |
---|---|---|---|
start | number | No | Start position of the span whose style needs to be updated. If this parameter is left empty or set to a negative value, the value 0 will be used. |
end | number | No | End position of the span whose style needs to be updated. If this parameter is left empty or set to a value beyond the range, the end of the span will be used as the end position. |
symbolStyle | RichEditorSymbolSpanStyle | Yes | Style of the symbol span. |
NOTE
If the value of start is greater than that of end, the value 0 will be used as start and the end of the span as end.
RichEditorParagraphStyleOptions11+
Describes the paragraph style options.
Name | Type | Mandatory | Description |
---|---|---|---|
start | number | No | Start position of the paragraph whose style needs to be updated. If this parameter is left empty or set to a negative value, the value 0 will be used. |
end | number | No | End position of the paragraph whose style needs to be updated. If this parameter is left empty or set to a negative value or any value beyond the range, the end of the paragraph will be used as the end position. |
style | RichEditorParagraphStyle | Yes | Paragraph style. |
NOTE
If the value of start is greater than that of end, the value 0 will be used as start and the end of the span as end.
RichEditorParagraphStyle11+
Describes the paragraph style.
Name | Type | Mandatory | Description |
---|---|---|---|
textAlign | TextAlign | No | Horizontal alignment mode of the text. |
leadingMargin | Dimension | LeadingMarginPlaceholder | No | Indent of the paragraph. This parameter cannot be set in percentage. |
LeadingMarginPlaceholder11+
Describes the leading margin placeholder.
Name | Type | Mandatory | Description |
---|---|---|---|
pixelMap | PixelMap | Yes | Image content. |
size | [Dimension, Dimension] | Yes | Image size. This parameter cannot be set in percentage. |
RichEditorParagraphResult11+
Describes the returned paragraph information.
Name | Type | Mandatory | Description |
---|---|---|---|
style | RichEditorParagraphStyle | Yes | Paragraph style. |
range | [number, number] | Yes | Range of the paragraph. |
RichEditorTextSpanOptions
Describes the options for adding a text span.
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | No | Position of the text span to be added. If this parameter is left empty, the span will be added to the end of all text strings. If the value is less than 0, the span will be placed at the beginning of the text strings. If the value is greater than the text string length, the span is placed at the end of the text strings. |
style | RichEditorTextStyle | No | Style of the text span to be added. If this parameter is left empty, the default text style will be used. |
paragraphStyle11+ | RichEditorParagraphStyle | No | Paragraph style. |
gesture11+ | RichEditorGesture | No | Behavior-triggered callback. If this parameter is left empty, only the default system behavior is supported. |
RichEditorTextStyle
Provides the text style information.
Name | Type | Mandatory | Description |
---|---|---|---|
fontColor | ResourceColor | No | Font color. Default value: Color.Black |
fontSize | Length | No | Font size. If Length is of the number type, the unit fp is used. The default value is 16. The value cannot be a percentage. Since API version 9, this API is supported in ArkTS widgets. |
fontStyle | FontStyle | No | Font style. Default value: FontStyle.Normal |
fontWeight | FontWeight | number | string | No | Font weight. For the number type, the value ranges from 100 to 900, at an interval of 100. A larger value indicates a heavier font weight. The default value is 400. For the string type, only strings of the number type are supported, for example, "400", "bold", "bolder", "lighter", "regular", and "medium", which correspond to the enumerated values in FontWeight. Default value: FontWeight.Normal |
fontFamily | ResourceStr | number | string | No | Font family. The HarmonyOS Sans font and register custom fonts are supported. Default font: 'HarmonyOS Sans' |
decoration | { type: TextDecorationType, color?: ResourceColor } |
No | Style and color of the text decorative line. Default value: { type: TextDecorationType.None, color: Color.Black } |
textShadow11+ | ShadowOptions | Array<ShadowOptions> | Yes | Text shadow. It supports input parameters in an array to implement multiple text shadows. NOTE This API does not work with the fill attribute or coloring strategy. |
RichEditorImageSpanOptions
Defines the options for adding an image span.
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | No | Position of the image span to be added. If this parameter is left empty, the span will be added to the end of all text strings. If the value is less than 0, the span will be placed at the beginning of the text strings. If the value is greater than the text string length, the span is placed at the end of the text strings. |
imageStyle | RichEditorImageSpanStyle | No | Image style. If this parameter is left empty, the default image style will be used. |
gesture11+ | RichEditorGesture | No | Behavior-triggered callback. If this parameter is left empty, only the default system behavior is supported. |
RichEditorImageSpanStyle
Provides the image span style information.
Name | Type | Mandatory | Description |
---|---|---|---|
size | [Dimension, Dimension] | No | Width and height of the image. |
verticalAlign | ImageSpanAlignment | No | Vertical alignment mode of the image. Default value: ImageSpanAlignment.BASELINE |
objectFit | ImageFit | No | Scale mode of the image. Default value: ImageFit.Cover |
layoutStyle11+ | { margin ?: Dimension | Margin, borderRadius ?: Dimension | BorderRadiuses } |
No | Image layout style. |
RichEditorSymbolSpanOptions11+
Describes the options for adding a symbol span.
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | No | Position of the symbol span to be added. If this parameter is left empty, the span will be added to the end of all text strings. If the value is less than 0, the span will be placed at the beginning of the text strings. If the value is greater than the text string length, the span is placed at the end of the text strings. |
style | RichEditorSymbolSpanStyle | No | Style of the symbol span to be added. If this parameter is left empty, the default text style will be used. |
RichEditorSymbolSpanStyle11+
Provides the symbol span style information.
Name | Type | Mandatory | Description |
---|---|---|---|
fontColor | Array<ResourceColor> | No | Colors of the <SymbolGlyph> component. Default value: depending on the rendering strategy. |
fontSize | number | string | Resource | No | Size of the <SymbolGlyph> component. Default value: system default value |
fontWeight | FontWeight | number | string | No | Font weight of the <SymbolGlyph> component. For the number type, the value ranges from 100 to 900, at an interval of 100. A larger value indicates a heavier font weight. The default value is 400. For the string type, only strings of the number type are supported, for example, "400", "bold", "bolder", "lighter", "regular", and "medium", which correspond to the enumerated values in FontWeight. Default value: FontWeight.Normal |
renderingStrategy | SymbolRenderingStrategy | No | Rendering strategy of the <SymbolGlyph> component. Default value: SymbolRenderingStrategy.SINGLE NOTE For the resources referenced in $r('sys.symbol.ohos_*'), only ohos_trash_circle, ohos_folder_badge_plus, and ohos_lungs support the MULTIPLE_COLOR modes. |
effectStrategy | SymbolEffectStrategy | No | Symbol effect of the <SymbolGlyph> component. Default value: SymbolEffectStrategy.NONE NOTE For the resources referenced in $r('sys.symbol.ohos_*'), only ohos_wifi supports the hierarchical effect. |
RichEditorBuilderSpanOptions11+
Defines the options for adding an image span.
Name | Type | Mandatory | Description |
---|---|---|---|
offset | number | No | Position of the builder span to be added. If this parameter is left empty, the builder span will be added to the end of all text strings.rings. |
RichEditorRange
Provides the span range information.
Name | Type | Mandatory | Description |
---|---|---|---|
start | number | No | Start position. If this parameter is left empty or set to a negative value, the value 0 will be used. |
end | number | No | End position. If this parameter is left empty or set to a value beyond the range, the very end will be used as the end position. |
SelectionMenuOptions11+
Provides the selection range information.
Name | Type | Mandatory | Description |
---|---|---|---|
onAppear | ?(() => void) | No | Callback invoked when the custom context menu on selection is displayed. |
onDisappear | ?(() => void) | No | Callback invoked when the custom context menu on selection is closed. |
PasteEvent11+
Defines the custom paste event.
Name | Type | Mandatory | Description |
---|---|---|---|
preventDefault | ?(() => void) | No | Custom paste event. When set, it will overwrite the system paste event. |
RichEditorGesture11+
Defines the behavior-triggered callbacks.
onClick11+
onClick(callback: (event?: ClickEvent) => void)
Called when a click is complete.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
event | ClickEvent | No | Click event. |
onLongPress11+
onLongPress(callback: (event?: GestureEvent) => void )
Called when a long press is complete.
Parameters
Name | Type | Mandatory | Description |
---|---|---|---|
event | GestureEvent | No | Long press event. |
Example
Example 1
// xxx.ets
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller };
private start: number = -1;
private end: number = -1;
@State message: string = "[-1, -1]"
@State content: string = ""
build() {
Column() {
Column() {
Text("selection range:").width("100%")
Text() {
Span(this.message)
}.width("100%")
Text("selection content:").width("100%")
Text() {
Span(this.content)
}.width("100%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("20%")
Row() {
Button ("Update Style: Bold").onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
end: this.end,
textStyle:
{
fontWeight: FontWeight.Bolder
}
})
})
Button("Obtain Selection").onClick(() => {
this.content = "";
this.controller.getSpans({
start: this.start,
end: this.end
}).forEach(item => {
if(typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined'){
this.content += (item as RichEditorImageSpanResult).valueResourceStr;
this.content += "\n"
} else {
if(typeof(item as RichEditorTextSpanResult)['symbolSpanStyle'] != 'undefined') {
this.content += (item as RichEditorTextSpanResult).symbolSpanStyle?.fontSize;
this.content += "\n"
}else {
this.content += (item as RichEditorTextSpanResult).value;
this.content += "\n"
}
}
})
})
Button("Delete Selection").onClick(() => {
this.controller.deleteSpans({
start: this.start,
end: this.end
})
this.start = -1;
this.end = -1;
this.message = "[" + this.start + ", " + this.end + "]"
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("10%")
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan("012345",
{
style:
{
fontColor: Color.Orange,
fontSize: 30
}
})
this.controller.addSymbolSpan($r("sys.symbol.ohos_trash"),
{
style:
{
fontSize: 30
}
})
this.controller.addImageSpan($r("app.media.icon"),
{
imageStyle:
{
size: ["57px", "57px"]
}
})
this.controller.addTextSpan("56789",
{
style:
{
fontColor: Color.Black,
fontSize: 30
}
})
})
.onSelect((value: RichEditorSelection) => {
this.start = value.selection[0];
this.end = value.selection[1];
this.message = "[" + this.start + ", " + this.end + "]"
})
.aboutToIMEInput((value: RichEditorInsertValue) => {
console.log("---------------------- aboutToIMEInput ----------------------")
console.log("insertOffset:" + value.insertOffset)
console.log("insertValue:" + value.insertValue)
return true;
})
.onIMEInputComplete((value: RichEditorTextSpanResult) => {
console.log("---------------------- onIMEInputComplete ---------------------")
console.log("spanIndex:" + value.spanPosition.spanIndex)
console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
console.log("value:" + value.value)
})
.aboutToDelete((value: RichEditorDeleteValue) => {
console.log("---------------------- aboutToDelete --------------------------")
console.log("offset:" + value.offset)
console.log("direction:" + value.direction)
console.log("length:" + value.length)
value.richEditorDeleteSpans.forEach(item => {
console.log("---------------------- item --------------------------")
console.log("spanIndex:" + item.spanPosition.spanIndex)
console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
if (typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
console.log("image:" + (item as RichEditorImageSpanResult).valueResourceStr)
} else {
console.log("text:" + (item as RichEditorTextSpanResult).value)
}
})
return true;
})
.onDeleteComplete(() => {
console.log("---------------------- onDeleteComplete ------------------------")
})
.borderWidth(1)
.borderColor(Color.Green)
.width("100%")
.height("30%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
Example 2
// xxx.ets
@Entry
@Component
struct RichEditorExample {
controller: RichEditorController = new RichEditorController()
// Create a custom keyboard component.
@Builder CustomKeyboardBuilder() {
Column() {
Grid() {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#'], (item: number | string) => {
GridItem() {
Button(item + "")
.width(110).onClick(() => {
this.controller.addTextSpan(item + '', {
offset: this.controller.getCaretOffset(),
style:
{
fontColor: Color.Orange,
fontSize: 30
}
})
this.controller.setCaretOffset(this.controller.getCaretOffset() + item.toString().length)
})
}
})
}.maxCount(3).columnsGap(10).rowsGap(10).padding(5)
}.backgroundColor(Color.Gray)
}
build() {
Column() {
RichEditor({ controller: this.controller })
// Bind the custom keyboard.
.customKeyboard(this.CustomKeyboardBuilder()).margin(10).border({ width: 1 })
.height(200)
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
}
}
}
Example 3
// xxx.ets
import pasteboard from '@ohos.pasteboard'
import { BusinessError } from '@ohos.base';
export interface SelectionMenuTheme {
imageSize: number;
buttonSize: number;
menuSpacing: number;
editorOptionMargin: number;
expandedOptionPadding: number;
defaultMenuWidth: number;
imageFillColor: Resource;
backGroundColor: Resource;
iconBorderRadius: Resource;
containerBorderRadius: Resource;
cutIcon: Resource;
copyIcon: Resource;
pasteIcon: Resource;
selectAllIcon: Resource;
shareIcon: Resource;
translateIcon: Resource;
searchIcon: Resource;
arrowDownIcon: Resource;
iconPanelShadowStyle: ShadowStyle;
iconFocusBorderColor: Resource;
}
export const defaultTheme: SelectionMenuTheme = {
imageSize: 24,
buttonSize: 48,
menuSpacing: 8,
editorOptionMargin: 1,
expandedOptionPadding: 3,
defaultMenuWidth: 256,
imageFillColor: $r('sys.color.ohos_id_color_primary'),
backGroundColor: $r('sys.color.ohos_id_color_dialog_bg'),
iconBorderRadius: $r('sys.float.ohos_id_corner_radius_default_m'),
containerBorderRadius: $r('sys.float.ohos_id_corner_radius_card'),
cutIcon: $r("sys.media.ohos_ic_public_cut"),
copyIcon: $r("sys.media.ohos_ic_public_copy"),
pasteIcon: $r("sys.media.ohos_ic_public_paste"),
selectAllIcon: $r("sys.media.ohos_ic_public_select_all"),
shareIcon: $r("sys.media.ohos_ic_public_share"),
translateIcon: $r("sys.media.ohos_ic_public_translate_c2e"),
searchIcon: $r("sys.media.ohos_ic_public_search_filled"),
arrowDownIcon: $r("sys.media.ohos_ic_public_arrow_down"),
iconPanelShadowStyle: ShadowStyle.OUTER_DEFAULT_MD,
iconFocusBorderColor: $r('sys.color.ohos_id_color_focused_outline'),
}
@Entry
@Component
struct SelectionMenu {
@State message: string = 'Hello World'
@State textSize: number = 40
@State sliderShow: boolean = false
@State start: number = -1
@State end: number = -1
@State colorTransparent: Color = Color.Transparent
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller }
private iconArr: Array<Resource> =
[$r('app.media.icon'), $r("app.media.icon"), $r('app.media.icon'),
$r("app.media.icon"), $r('app.media.icon')]
@State iconBgColor: ResourceColor[] = new Array(this.iconArr.length).fill(this.colorTransparent)
@State pasteEnable: boolean = false
@State visibilityValue: Visibility = Visibility.Visible
@State textStyle: RichEditorTextStyle = {}
private fontWeightTable: string[] = ["100", "200", "300", "400", "500", "600", "700", "800", "900", "bold", "normal", "bolder", "lighter", "medium", "regular"]
private theme: SelectionMenuTheme = defaultTheme;
aboutToAppear() {
if (this.controller) {
let richEditorSelection = this.controller.getSelection()
if (richEditorSelection) {
let start = richEditorSelection.selection[0]
let end = richEditorSelection.selection[1]
if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) {
this.visibilityValue = Visibility.None
} else {
this.visibilityValue = Visibility.Visible
}
}
}
let sysBoard = pasteboard.getSystemPasteboard()
if (sysBoard && sysBoard.hasDataSync()) {
this.pasteEnable = true
} else {
this.pasteEnable = false
}
}
build() {
Column() {
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } })
})
.onSelect((value: RichEditorSelection) => {
if (value.selection[0] == -1 && value.selection[1] == -1) {
return
}
this.start = value.selection[0]
this.end = value.selection[1]
})
.bindSelectionMenu(RichEditorSpanType.TEXT, this.panel, ResponseType.LongPress, { onDisappear: () => {
this.sliderShow = false
}})
.bindSelectionMenu(RichEditorSpanType.TEXT, this.panel, ResponseType.RightClick, { onDisappear: () => {
this.sliderShow = false
}})
.borderWidth(1)
.borderColor(Color.Red)
.width(200)
.height(200)
}.width('100%').backgroundColor(Color.White)
}.height('100%')
}
PushDataToPasteboard(richEditorSelection: RichEditorSelection) {
let sysBoard = pasteboard.getSystemPasteboard()
let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, '')
if (richEditorSelection.spans && richEditorSelection.spans.length > 0) {
let count = richEditorSelection.spans.length
for (let i = count - 1; i >= 0; i--) {
let item = richEditorSelection.spans[i]
if ((item as RichEditorTextSpanResult)?.textStyle) {
let span = item as RichEditorTextSpanResult
let style = span.textStyle
let data = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_PLAIN, span.value.substring(span.offsetInSpan[0], span.offsetInSpan[1]))
let prop = pasteData.getProperty()
let temp: Record<string, Object> = {
'color': style.fontColor,
'size': style.fontSize,
'style': style.fontStyle,
'weight': this.fontWeightTable[style.fontWeight],
'fontFamily': style.fontFamily,
'decorationType': style.decoration.type,
'decorationColor': style.decoration.color
}
prop.additions[i] = temp;
pasteData.addRecord(data)
pasteData.setProperty(prop)
}
}
}
sysBoard.clearData()
sysBoard.setData(pasteData).then(() => {
console.info('SelectionMenu copy option, Succeeded in setting PasteData.');
this.pasteEnable = true;
}).catch((err: BusinessError) => {
console.error('SelectionMenu copy option, Failed to set PasteData. Cause:' + err.message);
})
}
PopDataFromPasteboard(richEditorSelection: RichEditorSelection) {
let start = richEditorSelection.selection[0]
let end = richEditorSelection.selection[1]
if (start == end && this.controller) {
start = this.controller.getCaretOffset()
end = this.controller.getCaretOffset()
}
let moveOffset = 0
let sysBoard = pasteboard.getSystemPasteboard()
sysBoard.getData((err, data) => {
if (err) {
return
}
let count = data.getRecordCount()
for (let i = 0; i < count; i++) {
const element = data.getRecord(i);
let tex: RichEditorTextStyle = {
fontSize: 16,
fontColor: Color.Black,
fontWeight: FontWeight.Normal,
fontFamily: "HarmonyOS Sans",
fontStyle: FontStyle.Normal,
decoration: { type: TextDecorationType.None, color: "#FF000000" }
}
if (data.getProperty() && data.getProperty().additions[i]) {
const tmp = data.getProperty().additions[i] as Record<string, Object | undefined>;
if (tmp.color) {
tex.fontColor = tmp.color as ResourceColor;
}
if (tmp.size) {
tex.fontSize = tmp.size as Length | number;
}
if (tmp.style) {
tex.fontStyle = tmp.style as FontStyle;
}
if (tmp.weight) {
tex.fontWeight = tmp.weight as number | FontWeight | string;
}
if (tmp.fontFamily) {
tex.fontFamily = tmp.fontFamily as ResourceStr;
}
if (tmp.decorationType && tex.decoration) {
tex.decoration.type = tmp.decorationType as TextDecorationType;
}
if (tmp.decorationColor && tex.decoration) {
tex.decoration.color = tmp.decorationColor as ResourceColor;
}
if (tex.decoration) {
tex.decoration = { type: tex.decoration.type, color: tex.decoration.color }
}
}
if (element && element.plainText && element.mimeType === pasteboard.MIMETYPE_TEXT_PLAIN && this.controller) {
this.controller.addTextSpan(element.plainText,
{
style: tex,
offset: start + moveOffset
}
)
moveOffset += element.plainText.length
}
}
if (this.controller) {
this.controller.setCaretOffset(start + moveOffset)
this.controller.closeSelectionMenu()
}
if (start != end && this.controller) {
this.controller.deleteSpans({ start: start + moveOffset, end: end + moveOffset })
}
})
}
@Builder
panel() {
Column() {
this.iconPanel()
if (!this.sliderShow) {
this.SystemMenu()
} else {
this.sliderPanel()
}
}.width(256)
}
@Builder iconPanel() {
Column() {
Row({ space: 2 }) {
ForEach(this.iconArr, (item:Resource, index ?: number) => {
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Image(item).fillColor(this.theme.imageFillColor).width(24).height(24).focusable(true).draggable(false)
}
.borderRadius(this.theme.iconBorderRadius)
.width(this.theme.buttonSize)
.height(this.theme.buttonSize)
.onClick(() => {
if (index as number == 0) {
this.sliderShow = false
if (this.controller) {
let selection = this.controller.getSelection();
let spans = selection.spans
spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
let span = item as RichEditorTextSpanResult
this.textStyle = span.textStyle
let start = span.offsetInSpan[0]
let end = span.offsetInSpan[1]
let offset = span.spanPosition.spanRange[0]
if (this.textStyle.fontWeight != 11) {
this.textStyle.fontWeight = FontWeight.Bolder
} else {
this.textStyle.fontWeight = FontWeight.Normal
}
this.controller.updateSpanStyle({
start: offset + start,
end: offset + end,
textStyle: this.textStyle
})
}
})
}
} else if (index as number == 1) {
this.sliderShow = false
if (this.controller) {
let selection = this.controller.getSelection();
let spans = selection.spans
spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
let span = item as RichEditorTextSpanResult
this.textStyle = span.textStyle
let start = span.offsetInSpan[0]
let end = span.offsetInSpan[1]
let offset = span.spanPosition.spanRange[0]
if (this.textStyle.fontStyle == FontStyle.Italic) {
this.textStyle.fontStyle = FontStyle.Normal
} else {
this.textStyle.fontStyle = FontStyle.Italic
}
this.controller.updateSpanStyle({
start: offset + start,
end: offset + end,
textStyle: this.textStyle
})
}
})
}
} else if (index as number == 2) {
this.sliderShow = false
if (this.controller) {
let selection = this.controller.getSelection();
let spans = selection.spans
spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
let span = item as RichEditorTextSpanResult
this.textStyle = span.textStyle
let start = span.offsetInSpan[0]
let end = span.offsetInSpan[1]
let offset = span.spanPosition.spanRange[0]
if (this.textStyle.decoration) {
if (this.textStyle.decoration.type == TextDecorationType.Underline) {
this.textStyle.decoration.type = TextDecorationType.None
} else {
this.textStyle.decoration.type = TextDecorationType.Underline
}
} else {
this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
}
this.controller.updateSpanStyle({
start: offset + start,
end: offset + end,
textStyle: this.textStyle
})
}
})
}
} else if (index as number == 3) {
this.sliderShow = !this.sliderShow
} else if (index as number == 4) {
this.sliderShow = false
if (this.controller) {
let selection = this.controller.getSelection();
let spans = selection.spans
spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
let span = item as RichEditorTextSpanResult
this.textStyle = span.textStyle
let start = span.offsetInSpan[0]
let end = span.offsetInSpan[1]
let offset = span.spanPosition.spanRange[0]
if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
this.textStyle.fontColor = Color.Black
} else {
this.textStyle.fontColor = Color.Orange
}
this.controller.updateSpanStyle({
start: offset + start,
end: offset + end,
textStyle: this.textStyle
})
}
})
}
}
})
.onTouch((event?: TouchEvent | undefined) => {
if(event != undefined){
if (event.type === TouchType.Down) {
this.iconBgColor[index as number] = $r('sys.color.ohos_id_color_click_effect')
}
if (event.type === TouchType.Up) {
this.iconBgColor[index as number] = this.colorTransparent
}
}
})
.onHover((isHover?: boolean, event?: HoverEvent) => {
this.iconBgColor.forEach((icon:ResourceColor, index1) => {
this.iconBgColor[index1] = this.colorTransparent
})
if(isHover != undefined) {
this.iconBgColor[index as number] = $r('sys.color.ohos_id_color_hover')
}
})
.backgroundColor(this.iconBgColor[index as number])
})
}
}
.clip(true)
.width(this.theme.defaultMenuWidth)
.padding(this.theme.expandedOptionPadding)
.borderRadius(this.theme.containerBorderRadius)
.margin({ bottom: this.theme.menuSpacing })
.backgroundColor(this.theme.backGroundColor)
.shadow(this.theme.iconPanelShadowStyle)
}
@Builder
SystemMenu() {
Column() {
Menu() {
if (this.controller) {
MenuItemGroup() {
MenuItem({ startIcon: this.theme.cutIcon, content: "Cut", labelInfo: "Ctrl+X" })
.onClick(() => {
if (!this.controller) {
return
}
let richEditorSelection = this.controller.getSelection()
this.PushDataToPasteboard(richEditorSelection);
this.controller.deleteSpans({
start: richEditorSelection.selection[0],
end: richEditorSelection.selection[1]
})
})
MenuItem({ startIcon: this.theme.copyIcon, content: "Copy", labelInfo: "Ctrl+C" })
.onClick(() => {
if (!this.controller) {
return
}
let richEditorSelection = this.controller.getSelection()
this.PushDataToPasteboard(richEditorSelection);
this.controller.closeSelectionMenu()
})
MenuItem({ startIcon: this.theme.pasteIcon, content: "Paste", labelInfo: "Ctrl+V" })
.enabled(this.pasteEnable)
.onClick(() => {
if (!this.controller) {
return
}
let richEditorSelection = this.controller.getSelection()
this.PopDataFromPasteboard(richEditorSelection)
})
MenuItem({ startIcon: this.theme.selectAllIcon, content: "Select all", labelInfo: "Ctrl+A" })
.visibility(this.visibilityValue)
.onClick(() => {
if (!this.controller) {
return
}
this.controller.setSelection(-1, -1)
this.visibilityValue = Visibility.None
})
MenuItem({ startIcon: this.theme.shareIcon, content: "Share", labelInfo: "" })
.enabled(false)
MenuItem({ startIcon: this.theme.translateIcon, content: "Translate", labelInfo: "" })
.enabled(false)
MenuItem({ startIcon: this.theme.searchIcon, content: "Search", labelInfo: "" })
.enabled(false)
}
}
}
.onVisibleAreaChange([0.0, 1.0], () => {
if (!this.controller) {
return
}
let richEditorSelection = this.controller.getSelection()
let start = richEditorSelection.selection[0]
let end = richEditorSelection.selection[1]
if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) {
this.visibilityValue = Visibility.None
} else {
this.visibilityValue = Visibility.Visible
}
})
.radius(this.theme.containerBorderRadius)
.clip(true)
.backgroundColor(Color.White)
.width(this.theme.defaultMenuWidth)
}
.width(this.theme.defaultMenuWidth)
}
@Builder sliderPanel() {
Column() {
Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
Text('A').fontSize(15)
Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
.width(210)
.onChange((value: number, mode: SliderChangeMode) => {
if (this.controller) {
let selection = this.controller.getSelection();
if (mode == SliderChangeMode.End) {
if (this.textSize == undefined) {
this.textSize = 0
}
let spans = selection.spans
spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize)
}
})
}
if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
this.start = selection.selection[0]
this.end = selection.selection[1]
this.textSize = value
this.controller.updateSpanStyle({
start: this.start,
end: this.end,
textStyle: { fontSize: this.textSize }
})
}
}
})
Text('A').fontSize(20).fontWeight(FontWeight.Medium)
}.borderRadius(this.theme.containerBorderRadius)
}
.shadow(ShadowStyle.OUTER_DEFAULT_MD)
.backgroundColor(Color.White)
.borderRadius(this.theme.containerBorderRadius)
.padding(15)
.height(48)
}
}
NOTE
Icons in bold and italics are not preset in the system. The sample code uses the default icons. You need to replace the icons in iconArr with the desired icons.
Example 4
// xxx.ets
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller };
private start: number = -1;
private end: number = -1;
@State message: string = "[-1, -1]"
@State content: string = ""
@State paddingVal: number = 5
@State borderRad: number = 4
build() {
Column() {
Column() {
Text("selection range:").width("100%")
Text() {
Span(this.message)
}.width("100%")
Text("selection content:").width("100%")
Text() {
Span(this.content)
}.width("100%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("20%")
Row() {
Button("updateSpanStyle1")
.fontSize(12)
.onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
textStyle:
{
fontWeight: FontWeight.Bolder
},
imageStyle: {
size: ["80px", "80px"],
layoutStyle: {
borderRadius: undefined,
margin: undefined
}
}
})
})
Button("updateSpanStyle2")
.fontSize(12)
.onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
textStyle:
{
fontWeight: FontWeight.Bolder
},
imageStyle: {
size: ["70px", "70px"],
layoutStyle: {
borderRadius: { topLeft: '100px', topRight: '20px', bottomLeft: '100px', bottomRight: '20px' },
margin: { left: '30px', top: '20px', right: '20px', bottom: '20px' }
}
}
})
})
Button("updateSpanStyle3")
.fontSize(12)
.onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
textStyle:
{
fontWeight: FontWeight.Bolder
},
imageStyle: {
size: ["60px", "60px"],
layoutStyle: {
borderRadius: '-10px',
margin: '-10px'
}
}
})
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("10%")
Row() {
Button('addImageSpan1')
.fontSize(12)
.onClick(() => {
this.controller.addImageSpan($r('app.media.app_icon'), {
imageStyle: {
size: ["80px", "80px"],
layoutStyle: {
borderRadius: '50px',
margin: '40px'
}
}
})
})
Button('addImageSpan2')
.fontSize(12)
.onClick(() => {
this.controller.addImageSpan($r('app.media.app_icon'), {
imageStyle: {
size: ["100px", "100px"],
verticalAlign: ImageSpanAlignment.BOTTOM,
layoutStyle: {
borderRadius: undefined,
margin: undefined
}
}
})
})
Button('addImageSpan3')
.fontSize(12)
.onClick(() => {
this.controller.addImageSpan($r('app.media.app_icon'), {
imageStyle: {
size: ["60px", "60px"],
verticalAlign: ImageSpanAlignment.BOTTOM,
layoutStyle: {
borderRadius: { topLeft: '10px', topRight: '20px', bottomLeft: '30px', bottomRight: '40px' },
margin: { left: '10px', top: '20px', right: '30px', bottom: '40px' }
}
}
})
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("10%")
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan("0123456789",
{
style:
{
fontColor: Color.Orange,
fontSize: 30
}
})
this.controller.addImageSpan($r("app.media.app_icon"),
{
imageStyle:
{
size: ["60px", "60px"],
verticalAlign: ImageSpanAlignment.BOTTOM,
layoutStyle: {
borderRadius: { topLeft: '10px', topRight: '20px', bottomLeft: '30px', bottomRight: '40px' },
margin: { left: '10px', top: '20px', right: '30px', bottom: '40px' }
}
}
})
this.controller.addTextSpan("0123456789",
{
style:
{
fontColor: Color.Black,
fontSize: 30
}
})
})
.onSelect((value: RichEditorSelection) => {
this.start = value.selection[0];
this.end = value.selection[1];
this.message = "[" + this.start + ", " + this.end + "]"
})
.aboutToIMEInput((value: RichEditorInsertValue) => {
console.log("---------------------- aboutToIMEInput ----------------------")
console.log("insertOffset:" + value.insertOffset)
console.log("insertValue:" + value.insertValue)
return true;
})
.onIMEInputComplete((value: RichEditorTextSpanResult) => {
console.log("---------------------- onIMEInputComplete ---------------------")
console.log("spanIndex:" + value.spanPosition.spanIndex)
console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
console.log("value:" + value.value)
})
.aboutToDelete((value: RichEditorDeleteValue) => {
console.log("---------------------- aboutToDelete --------------------------")
console.log("offset:" + value.offset)
console.log("direction:" + value.direction)
console.log("length:" + value.length)
value.richEditorDeleteSpans.forEach(item => {
console.log("---------------------- item --------------------------")
console.log("spanIndex:" + item.spanPosition.spanIndex)
console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
console.log("image:" + (item as RichEditorImageSpanResult).valueResourceStr)
} else {
console.log("text:" + (item as RichEditorTextSpanResult).value)
}
})
return true;
})
.onDeleteComplete(() => {
console.log("---------------------- onDeleteComplete ------------------------")
})
.borderWidth(1)
.borderColor(Color.Green)
.width("100%")
.height('80.00%')
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
Example 5
// xxx.ets
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController()
options: RichEditorOptions = { controller: this.controller };
@State textFlag: string = "TextFlag";
build() {
Column() {
Column() {
Text(this.textFlag)
.copyOption(CopyOptions.InApp)
.fontSize(50)
}
Divider()
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan('Area1\n', {
style:
{
fontColor: Color.Orange,
fontSize: 50
},
gesture:
{
onClick: () => {
this.textFlag = "Area1 is onClick."
},
onLongPress: () => {
this.textFlag = "Area1 is onLongPress."
}
}
})
this.controller.addTextSpan('Area2\n', {
style:
{
fontColor: Color.Blue,
fontSize: 50
},
gesture:
{
onClick: () => {
this.textFlag = "Area2 is onClick."
},
onLongPress: () => {
this.textFlag = "Area2 is onLongPress."
}
}
})
this.controller.addImageSpan($r("app.media.icon"),
{
imageStyle:
{
size: ["100px", "100px"],
layoutStyle: {
margin: 5,
borderRadius: 15
}
},
gesture:
{
onClick: () => {
this.textFlag = "ImageSpan is onClick."
},
onLongPress: () => {
this.textFlag = "ImageSpan is onLongPress."
}
}
})
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
Example 6
// xxx.ets
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
private spanParagraphs: RichEditorParagraphResult[] = [];
build() {
Column() {
RichEditor({ controller: this.controller })
.onReady(() => {
this.controller.addTextSpan("0123456789\n", {
style: {
fontColor: Color.Pink,
fontSize: "32",
},
paragraphStyle: {
textAlign: TextAlign.Start,
leadingMargin: 16
}
})
this.controller.addTextSpan("0123456789")
})
.width("80%")
.height("30%")
.border({ width: 1, radius: 5 })
.draggable(false)
Column({ space: 5 }) {
Button ("Align left").onClick () => {
this.controller.updateParagraphStyle({ start: -1, end: -1,
style: {
textAlign: TextAlign.Start,
}
})
})
Button ("Align right").onClick(() => {
this.controller.updateParagraphStyle({ start: -1, end: -1,
style: {
textAlign: TextAlign.End,
}
})
})
Button ("Center").onClick ((). => {
this.controller.updateParagraphStyle({ start: -1, end: -1,
style: {
textAlign: TextAlign.Center,
}
})
})
Divider()
Button("getParagraphs").onClick(() => {
this.spanParagraphs = this.controller.getParagraphs({ start: -1, end: -1 })
console.log("RichEditor getParagraphs:" + JSON.stringify(this.spanParagraphs))
})
Button("UpdateSpanStyle1").onClick(() => {
this.controller.updateSpanStyle({ start: -1, end: -1,
textStyle: {
fontColor: Color.Brown,
fontSize: 20
}
})
})
Button("UpdateSpanStyle2").onClick(() => {
this.controller.updateSpanStyle({ start: -1, end: -1,
textStyle: {
fontColor: Color.Green,
fontSize: 30
}
})
})
}
}
}
}
Example 7
// xxx.ets
import font from '@ohos.font'
const canvasWidth = 1000
const canvasHeight = 100
const Indentation = 10
class LeadingMarginCreator {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private offscreenCanvas: OffscreenCanvas = new OffscreenCanvas(canvasWidth, canvasHeight);
private offContext: OffscreenCanvasRenderingContext2D = this.offscreenCanvas.getContext("2d", this.settings);
public static instance: LeadingMarginCreator = new LeadingMarginCreator();
public genStrMark(fontSize: number, str: string): PixelMap {
this.offContext = this.offscreenCanvas.getContext("2d", this.settings);
this.clearCanvas();
this.offContext.font = fontSize + 'vp sans-serif';
this.offContext.fillText(str + '.', 0, fontSize * 0.9);
return this.offContext.getPixelMap(0, 0, fontSize * (str.length + 1) / 1.75, fontSize);
}
public genSquareMark(fontSize: number): PixelMap {
this.offContext = this.offscreenCanvas.getContext("2d", this.settings);
this.clearCanvas();
const coordinate = fontSize * (1 - 1 / 1.5) / 2;
const sideLength = fontSize / 1.5;
this.offContext.fillRect(coordinate, coordinate, sideLength, sideLength);
return this.offContext.getPixelMap(0, 0, fontSize, fontSize);
}
public genCircleMark(fontSize: number): PixelMap {
this.offContext = this.offscreenCanvas.getContext("2d", this.settings);
this.clearCanvas();
const centerCoordinate = fontSize / 2;
const radius = fontSize / 3;
this.offContext.ellipse(centerCoordinate, centerCoordinate, radius, radius, 0, 0, 2 * Math.PI);
this.offContext.fillStyle = Color.Black;
this.offContext.fill();
return this.offContext.getPixelMap(0, 0, fontSize, fontSize);
}
private clearCanvas() {
this.offContext.clearRect(0, 0, canvasWidth, canvasHeight);
}
}
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller };
private leadingMarkCreatorInstance = LeadingMarginCreator.instance;
private fontNameRawFile: string = 'MiSans-Bold'
@State fs: number = 30
@State cl: number = Color.Black
private leftMargin: Dimension = 0;
private richEditorTextStyle: RichEditorTextStyle = {};
aboutToAppear() {
font.registerFont({
familyName: 'MiSans-Bold',
familySrc: '/font/MiSans-Bold.ttf'
})
}
build() {
Scroll() {
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan("0123456789\n",
{
style:
{
fontWeight: 'medium',
fontFamily: this.fontNameRawFile,
fontColor: Color.Red,
fontSize: 50,
fontStyle: FontStyle.Italic,
decoration: { type: TextDecorationType.Underline, color: Color.Green }
}
})
this.controller.addTextSpan("abcdefg",
{
style:
{
fontWeight: FontWeight.Lighter,
fontFamily: 'HarmonyOS Sans',
fontColor: 'rgba(0,128,0,0.5)',
fontSize: 30,
fontStyle: FontStyle.Normal,
decoration: { type: TextDecorationType.Overline, color: 'rgba(169, 26, 246, 0.50)' }
}
})
})
.borderWidth(1)
.borderColor(Color.Green)
.width("100%")
.height("50%")
Row({ space: 5 }) {
Button('setTypingStyle1')
.fontSize(10)
.onClick(() => {
this.controller.setTypingStyle(
{
fontWeight: 'medium',
fontFamily: this.fontNameRawFile,
fontColor: Color.Blue,
fontSize: 50,
fontStyle: FontStyle.Italic,
decoration: { type: TextDecorationType.Underline, color: Color.Green }
})
})
Button('setTypingStyle2')
.fontSize(10)
.onClick(() => {
this.controller.setTypingStyle(
{
fontWeight: FontWeight.Lighter,
fontFamily: 'HarmonyOS Sans',
fontColor: Color.Green,
fontSize: '30',
fontStyle: FontStyle.Normal,
decoration: { type: TextDecorationType.Overline, color: 'rgba(169, 26, 246, 0.50)' }
})
})
}
Divider()
Button("getTypingStyle").onClick(() => {
this.richEditorTextStyle = this.controller.getTypingStyle()
console.log("RichEditor getTypingStyle:" + JSON.stringify(this.richEditorTextStyle))
})
Divider()
Row({ space: 5 }) {
Button ("Increase Bullet Indent").onClick(() => {
let margin = Number(this.leftMargin)
if (margin < 200) {
margin += Indentation;
this.leftMargin = margin;
}
this.controller.updateParagraphStyle({
start: -10,
end: -10,
style: {
leadingMargin : {
pixelMap : this.leadingMarkCreatorInstance.genCircleMark(16), size: [margin, 30]
}
}
})
})
Button("Decrease Bullet Indent").onClick(() => {
let margin = Number(this.leftMargin)
if (margin > 0) {
margin -= Indentation;
this.leftMargin = margin;
}
this.controller.updateParagraphStyle({
start: -10,
end: -10,
style: {
leadingMargin : {
pixelMap : this.leadingMarkCreatorInstance.genCircleMark(16), size: [margin, 30]
}
}
})
})
}
Divider()
Row({ space: 5 }) {
Button ("Increase Indent").onClick(() => {
let margin = Number(this.leftMargin)
if (margin < 200) {
margin += Indentation;
this.leftMargin = margin;
}
this.controller.updateParagraphStyle({
start: -10,
end: -10,
style: {
leadingMargin: margin
}
})
})
Button("Decrease Indent").onClick(() => {
let margin = Number(this.leftMargin)
if (margin > 0) {
margin -= Indentation;
this.leftMargin = margin;
}
this.controller.updateParagraphStyle({
start: -10,
end: -10,
style: {
leadingMargin: margin
}
})
})
}
}.borderWidth(1).borderColor(Color.Red)
}
}
}
Example 8
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller };
private start: number = -1;
private end: number = -1;
@State message: string = "[-1, -1]"
@State content: string = ""
@State visable :number = 0;
@State index:number = 0;
@State offsetx: number = 0;
@State textShadows : (ShadowOptions | Array<ShadowOptions> ) =
[{ radius: 10, color: Color.Red, offsetX: 10, offsetY: 0 },{ radius: 10, color: Color.Black, offsetX: 20, offsetY: 0 },
{ radius: 10, color: Color.Brown, offsetX: 30, offsetY: 0 },{ radius: 10, color: Color.Green, offsetX: 40, offsetY: 0 },
{ radius: 10, color: Color.Yellow, offsetX: 100, offsetY: 0 }]
@State textshadowOf : ShadowOptions[] = []
build() {
Column() {
Column() {
Text("selection range:").width("100%")
Text() {
Span(this.message)
}.width("100%")
Text("selection content:").width("100%")
Text() {
Span(this.content)
}.width("100%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("20%")
Row() {
Button("Update Style: Bold & Text Shadow").onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
end: this.end,
textStyle:
{
fontWeight: FontWeight.Bolder,
textShadow: this.textShadows
}
})
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("10%")
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan("0123456789",
{
style:
{
fontColor: Color.Orange,
fontSize: 30,
textShadow: { radius: 10, color: Color.Blue, offsetX: 10, offsetY: 0 }
}
})
})
.borderWidth(1)
.borderColor(Color.Green)
.width("100%")
.height("30%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
Example 9
@Builder
function placeholderBuilder2() {
Row({ space: 2 }) {
Image($r("app.media.icon")).width(24).height(24).margin({ left: -5 })
Text('okokokok').fontSize(10)
}.width('20%').height(50).padding(10).backgroundColor(Color.Red)
}
// xxx.ets
@Entry
@Component
struct Index {
controller: RichEditorController = new RichEditorController();
option: RichEditorOptions = { controller: this.controller };
private start: number = 2;
private end: number = 4;
@State message: string = "[-1, -1]"
@State content: string = ""
private my_offset: number | undefined = undefined
private my_builder: CustomBuilder = undefined
@Builder
placeholderBuilder() {
Row({ space: 2 }) {
Image($r("app.media.icon")).width(24).height(24).margin({ left: -5 })
Text('Custom Popup').fontSize(10)
}.width(100).height(50).padding(5)
}
@Builder
placeholderBuilder3() {
Text("hello").padding('20').borderWidth(1).width('100%')
}
@Builder
placeholderBuilder4() {
Column() {
Column({ space: 5 }) {
Text('direction:Row').fontSize(9).fontColor(0xCCCCCC).width('90%')
Flex({ direction: FlexDirection.Row }) { // The child components are arranged in the same direction as the main axis runs along the rows.
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
Text('direction:RowReverse').fontSize(9).fontColor(0xCCCCCC).width('90%')
Flex({ direction: FlexDirection.RowReverse }) { // The child components are arranged opposite to the Row direction.
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(50).backgroundColor(0xD2B48C)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
Text('direction:Column').fontSize(9).fontColor(0xCCCCCC).width('90%')
Flex({ direction: FlexDirection.Column }) { // The child components are arranged in the same direction as the main axis runs down the columns.
Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
}
.height(160)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
Text('direction:ColumnReverse').fontSize(9).fontColor(0xCCCCCC).width('90%')
Flex({ direction: FlexDirection.ColumnReverse }) { // The child components are arranged opposite to the Column direction.
Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
Text('1').width('20%').height(40).backgroundColor(0xF5DEB3)
Text('1').width('20%').height(40).backgroundColor(0xD2B48C)
}
.height(160)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
}.width('100%').margin({ top: 5 })
}.width('100%')
}
@Builder
MyMenu() {
Menu() {
MenuItem({ startIcon: $r("app.media.icon"), content: "Menu option 1" })
MenuItem({ startIcon: $r("app.media.icon"), content: "Menu option 2" })
.enabled(false)
}
}
build() {
Column() {
Column() {
Text("selection range:").width("100%")
Text() {
Span(this.message)
}.width("100%")
Text("selection content:").width("100%")
Text() {
Span(this.content)
}.width("100%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("20%")
Row() {
Button ("Get Span Info").onClick () => {
console.info('getSpans='+JSON.stringify(this.controller.getSpans({ start:1, end:5 })))
console.info('getParagraphs='+JSON.stringify(this.controller.getParagraphs({ start:1, end:5 })))
this.content = ""
this.controller.getSpans({
start: this.start,
end: this.end
}).forEach(item => {
if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
(item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
} else {
console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
(item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
(item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
}
} else {
this.content += (item as RichEditorTextSpanResult).value;
this.content += "\n"
console.info("text span: " + (item as RichEditorTextSpanResult).value)
}
})
})
Button ("Get Selection").onClick () => {
this.content = "";
let select = this.controller.getSelection()
console.info("selection start " + select.selection[0] + " end " + select.selection[1])
select.spans.forEach(item => {
if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
(item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
} else {
console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
(item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
(item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
}
} else {
this.content += (item as RichEditorTextSpanResult).value;
this.content += "\n"
console.info("text span: " + (item as RichEditorTextSpanResult).value)
}
})
})
Button("Delete Selection").onClick(() => {
this.controller.deleteSpans({
start: this.start,
end: this.end
})
})
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("10%")
Column() {
RichEditor(this.option)
.onReady(() => {
this.controller.addTextSpan("0123456789",
{
style:
{
fontColor: Color.Orange,
fontSize: 30
}
})
this.controller.addImageSpan($r("app.media.icon"),
{
imageStyle:
{
size: ["57px", "57px"]
}
})
})
.onSelect((value: RichEditorSelection) => {
this.start = value.selection[0];
this.end = value.selection[1];
this.message = "[" + this.start + ", " + this.end + "]"
console.info("onSelect="+JSON.stringify(value))
})
.aboutToIMEInput((value: RichEditorInsertValue) => {
console.log("---------------------- aboutToIMEInput --------------------")
console.info("aboutToIMEInput="+JSON.stringify(value))
console.log("insertOffset:" + value.insertOffset)
console.log("insertValue:" + value.insertValue)
return true;
})
.onIMEInputComplete((value: RichEditorTextSpanResult) => {
console.log("---------------------- onIMEInputComplete --------------------")
console.info("onIMEInputComplete="+JSON.stringify(value))
console.log("spanIndex:" + value.spanPosition.spanIndex)
console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]")
console.log("value:" + value.value)
})
.aboutToDelete((value: RichEditorDeleteValue) => {
value.richEditorDeleteSpans.forEach(item => {
console.log("---------------------- item --------------------")
console.info("spanIndex=" + item.spanPosition.spanIndex)
console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]")
console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]")
if (typeof (item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') {
if ((item as RichEditorImageSpanResult).valueResourceStr == "") {
console.info("builder span index " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range : " + (item as RichEditorImageSpanResult).offsetInSpan[0] + ", " +
(item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " + (item as RichEditorImageSpanResult).imageStyle[0] + ", " + (item as RichEditorImageSpanResult).imageStyle[1])
} else {
console.info("image span " + (item as RichEditorImageSpanResult).valueResourceStr + ", index : " + (item as RichEditorImageSpanResult).spanPosition.spanIndex + ", range: " +
(item as RichEditorImageSpanResult).offsetInSpan[0] + ", " + (item as RichEditorImageSpanResult).offsetInSpan[1] + ", size : " +
(item as RichEditorImageSpanResult).imageStyle.size[0] + ", " + (item as RichEditorImageSpanResult).imageStyle.size[1])
}
} else {
console.info("delete text: " + (item as RichEditorTextSpanResult).value)
}
})
return true;
})
.borderWidth(1)
.borderColor(Color.Green)
.width("100%")
.height("30%")
Button("add span")
.onClick(() => {
let num = this.controller.addBuilderSpan(this.my_builder, { offset: this.my_offset })
console.info('addBuilderSpan return ' + num)
})
Button("add image")
.onClick(() => {
let num = this.controller.addImageSpan($r("app.media.icon"), {
imageStyle: {
size: ["50px", "50px"],
verticalAlign: ImageSpanAlignment.BOTTOM,
layoutStyle: {
borderRadius: undefined,
margin: undefined
}
}
})
console.info('addImageSpan return' + num)
})
Row() {
Button('builder1').onClick(() => {
this.my_builder = () => {
this.placeholderBuilder()
}
})
Button('builder2').onClick(() => {
this.my_builder = placeholderBuilder2.bind(this)
})
Button('builder3').onClick(() => {
this.my_builder = () => {
this.placeholderBuilder3()
}
})
Button('builder4').onClick(() => {
this.my_builder = () => {
this.placeholderBuilder4()
}
})
}
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
Example 10
Example of using enableDataDetector and dataDetectorConfig
@Entry
@Component
struct TextExample7 {
controller: RichEditorController = new RichEditorController();
options: RichEditorOptions = { controller: this.controller };
@State phoneNumber: string = '(86) (755) ********';
@State url: string = 'www.********.com';
@State email: string = '***@example.com';
@State address: string = 'Street A, city B, state C';
@State enableDataDetector: boolean = true;
@State types: TextDataDetectorType[] = [];
build() {
Row() {
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan('Phone number:' + this.phoneNumber + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('URL:' + this.url + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('Email:' + this.email + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('Address:' + this.address,
{
style:
{
fontSize: 30
}
})
})
.copyOptions(CopyOptions.InApp)
.enableDataDetector(this.enableDataDetector)
.dataDetectorConfig({types : this.types, onDetectResultUpdate: (result: string)=>{}})
.borderWidth(1)
.padding(10)
.width('100%')
}
.width('100%')
}
}
}