
鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:RichEditor)
ArkTS声明式开发(基础手势:RichEditor)
支持图文混排和文本交互式编辑的组件。
说明:
该组件从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 RichEditor仅支持通过onDragStart事件实现浮起等拖拽效果。
子组件
不包含子组件。
接口
RichEditor(value: RichEditorOptions)
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | RichEditorOptions | 是 | 富文本组件初始化选项。 |
属性
支持通用属性。
说明:
其中clip属性默认值为true。 align属性只支持上方丶中间和下方位置的对齐方式。
名称 | 参数类型 | 描述 |
---|---|---|
customKeyboard | CustomBuilder | 设置自定义键盘。 说明: 当设置自定义键盘时,输入框激活后不会打开系统输入法,而是加载指定的自定义组件。 自定义键盘的高度可以通过自定义组件根节点的height属性设置,宽度不可设置,使用系统默认值。 自定义键盘采用覆盖原始界面的方式呈现,不会对应用原始界面产生压缩或者上提。 自定义键盘无法获取焦点,但是会拦截手势事件。 默认在输入控件失去焦点时,关闭自定义键盘。 如果设备支持拍摄输入,设置自定义键盘后,该输入框会不支持拍摄输入。 |
bindSelectionMenu | { spantype: RichEditorSpanType, content: CustomBuilder, responseType: ResponseType | RichEditorResponseType11+, options?: SelectionMenuOptions } |
设置自定义选择菜单。 默认值:{ spanType: RichEditorSpanType.TEXT responseType: ResponseType.LongPress 其他:空 } |
copyOptions | CopyOptions | 组件支持设置文本内容是否可复制粘贴。 默认值:CopyOptions.LocalDevice 说明: copyOptions不为CopyOptions.None时,长按组件内容,会弹出文本选择弹框。如果通过bindSelectionMenu等方式自定义文本选择菜单,则会弹出自定义的菜单。 设置copyOptions为CopyOptions.None,复制、剪切功能不生效。 |
enableDataDetector11+ | boolean | 使能文本识别。 默认值: false 说明: 所识别实体的 fontColor 和decoration 会被更改为如下样式:fontColor:Color.Blue decoration: { type: TextDecorationType.Underline, color: Color.Blue } 该接口依赖设备底层应具有文本识别能力,否则设置不会生效。 当 enableDataDetector 设置为true,同时不设置dataDetectorConfig 属性时,默认识别所有类型的实体。当 copyOptions 设置为CopyOptions.None时,该功能不会生效。对 addBuilderSpan 的节点文本,该功能不会生效。 |
dataDetectorConfig11+ | TextDataDetectorConfig | 文本识别配置。 默认值:{ types: [ ], onDetectResultUpdate: null } 说明: 需配合 enableDataDetector 一起使用,设置enableDataDetector 为true时,dataDetectorConfig 的配置才能生效。 |
事件
除支持通用事件外,还支持以下事件:
名称 | 功能描述 |
---|---|
onReady(callback: () => void) | 富文本组件初始化完成后,触发回调。 |
onSelect(callback: (value: RichEditorSelection) => void) | 鼠标左键按下选择,松开左键后触发回调。 用手指选择时,松开手指触发回调。 - value:选中的所有span信息。 |
aboutToIMEInput(callback: (value: RichEditorInsertValue) => boolean) | 输入法输入内容前,触发回调。 - value:输入法将要输入内容信息。 |
onIMEInputComplete(callback: (value: RichEditorTextSpanResult) => void) | 输入法完成输入后,触发回调。 - value:输入法完成输入后的文本Span信息。 |
aboutToDelete(callback: (value: RichEditorDeleteValue) => boolean) | 输入法删除内容前,触发回调。 - value:准备删除的内容所在的文本Span信息。 |
onDeleteComplete(callback: () => void) | 输入法完成删除后,触发回调。 |
onPaste11+(callback: (event?: PasteEvent) => void) | 完成粘贴前,触发回调。 说明: 系统的默认粘贴和拖拽行为,只支持纯文本的粘贴。 开发者可以通过该方法,覆盖系统默认行为,实现图文的粘贴。 |
RichEditorInsertValue
插入文本信息。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
insertOffset | number | 是 | 插入的文本偏移位置。 |
insertValue | string | 是 | 插入的文本内容。 |
RichEditorDeleteValue
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
offset | number | 是 | 删除内容的偏移位置。 |
direction | RichEditorDeleteDirection | 是 | 删除操作的方向。 |
length | number | 是 | 删除内容长度。 |
richEditorDeleteSpans | Array<RichEditorTextSpanResult | RichEditorImageSpanResult> | 是 | 删除的文本或者图片Span的具体信息。 |
RichEditorDeleteDirection
删除操作的方向。
名称 | 描述 |
---|---|
BACKWARD | 向后删除。 |
FORWARD | 向前删除。 |
RichEditorTextSpanResult
文本Span信息。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
spanPosition | RichEditorSpanPosition | 是 | Span位置。 |
value | string | 是 | 文本Span内容。 |
textStyle | RichEditorTextStyleResult | 是 | 文本Span样式信息。 |
offsetInSpan | [number, number] | 是 | 文本Span内容里有效内容的起始和结束位置。 |
RichEditorSpanPosition
Span位置信息。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
spanIndex | number | 是 | Span索引值。 |
spanRange | [number, number] | 是 | Span内容在RichEditor内的起始和结束位置。 |
RichEditorSpanType
Span类型信息。
名称 | 值 | 描述 |
---|---|---|
TEXT | 0 | Span为文字类型。 |
IMAGE | 1 | Span为图像类型。 |
MIXED | 2 | Span为图文混合类型。 |
RichEditorTextStyleResult
后端返回的文本样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
fontColor | ResourceColor | 是 | 文本颜色。 |
fontSize | number | 是 | 字体大小。 |
fontStyle | FontStyle | 是 | 字体样式。 |
fontWeight | number | 是 | 字体粗细。 |
fontFamily | string | 是 | 字体列表。 |
decoration | { type: TextDecorationType, color: ResourceColor } |
是 | 文本装饰线样式及其颜色。 |
RichEditorImageSpanResult
后端返回的图片信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
spanPosition | RichEditorSpanPosition | 是 | Span位置。 |
valuePixelMap | PixelMap | 否 | 图片内容。 |
valueResourceStr | ResourceStr | 否 | 图片资源id。 |
imageStyle | RichEditorImageSpanStyleResult | 是 | 图片样式。 |
offsetInSpan | [number, number] | 是 | Span里图片的起始和结束位置。 |
RichEditorImageSpanStyleResult
后端返回的图片样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
size | [number, number] | 是 | 图片的宽度和高度。 |
verticalAlign | ImageSpanAlignment | 是 | 图片垂直对齐方式。 |
objectFit | ImageFit | 是 | 图片缩放类型。 |
RichEditorOptions
RichEditor初始化参数。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
controller | RichEditorController | 是 | 富文本控制器。 |
TextDataDetectorConfig11+
文本识别配置参数。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
types | TextDataDetectorType[] | 是 | 文本识别的实体类型。设置types 为null 或者[] 时,识别所有类型的实体,否则只识别指定类型的实体。 |
onDetectResultUpdate | (result: string) => void | 否 | 文本识别成功后,触发onDetectResultUpdate 回调。result :文本识别的结果,Json格式。 |
RichEditorController
RichEditor组件的控制器。
导入对象
controller: RichEditorController = new RichEditorController()
getCaretOffset
getCaretOffset(): number
返回当前光标所在位置。
返回值:
类型 | 说明 |
---|---|
number | 当前光标所在位置。 |
setCaretOffset
setCaretOffset(offset: number): boolean
设置光标位置。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
offset | number | 是 | 光标偏移位置。超出文本范围时,设置失败。 |
返回值:
类型 | 说明 |
---|---|
boolean | 光标是否设置成功。 |
addTextSpan
addTextSpan(value: string, options?: RichEditorTextSpanOptions): number
添加文本内容。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | string | 是 | 文本内容。 |
options | RichEditorTextSpanOptions | 否 | 文本选项。 |
返回值:
类型 | 说明 |
---|---|
number | 添加完成的Text Span所在的位置。 |
addImageSpan
addImageSpan(value: PixelMap | ResourceStr, options?: RichEditorImageSpanOptions): number
添加图片内容。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | PixelMap|ResourceStr | 是 | 图片内容。 |
options | RichEditorImageSpanOptions | 否 | 图片选项。 |
返回值:
类型 | 说明 |
---|---|
number | 添加完成的imageSpan所在的位置。 |
addBuilderSpan11+
addBuilderSpan(value: CustomBuilder, options?: RichEditorBuilderSpanOptions): number
说明:
- RichEditor组件添加占位Span,占位Span调用系统的measure方法计算真实的长宽和位置。
- 可通过RichEditorBuilderSpanOptions设置此builder在RichEditor中的index(一个文字为一个单位)。
- 此占位Span不可获焦,不支持拖拽(与可拖拽Span如文本或图片Span一起拖拽时,此Span对应的位置内容不会显示但会占相同大小的区域),支持部分通用属性,占位、删除等能力等同于ImageSpan,长度视为一个文字。
- 不支持通过bindSelectionMenu设置自定义菜单。
- 不支持通过getSpans,getSelection,onSelect,aboutToDelete获取builderSpan信息。
- 不支持通过updateSpanStyle,updateParagraphStyle等方式更新builder。
- 对此builder节点进行复制或粘贴不生效。
- builder的布局约束由RichEditor传入,如果builder里最外层组件不设置大小,则会用RichEditor的大小作为maxSize。
- builder的手势相关事件机制与通用手势事件相同,如果builder中未设置透传,则仅有builder中的子组件响应。
通用属性仅支持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。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | CustomBuilder | 是 | 自定义组件。 |
options | RichEditorBuilderSpanOptions | 否 | builder选项。 |
返回值:
类型 | 说明 |
---|---|
number | 添加完成的builderSpan所在的位置。 |
getTypingStyle11+
getTypingStyle(): RichEditorTextStyle
获得用户预设的样式。
返回值:
类型 | 说明 |
---|---|
RichEditorTextStyle | 用户预设样式。 |
setTypingStyle11+
setTypingStyle(value: RichEditorTextStyle): void
设置用户预设的样式。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | RichEditorTextStyle | 是 | 预设样式。 |
updateSpanStyle
updateSpanStyle(value: RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions): void
更新文本或者图片样式。
若只更新了一个Span的部分内容,则会根据更新部分、未更新部分将该Span拆分为多个Span。
使用该接口更新文本或图片样式时默认不会关闭自定义文本选择菜单。
参数:
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
value | RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions | 是 | 文本或者图片的样式选项信息。 |
updateParagraphStyle11+
updateParagraphStyle(value: RichEditorParagraphStyleOptions): void
更新段落的样式。
参数:
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
value | RichEditorParagraphStyleOptions | 是 | 段落的样式选项信息。 |
getSpans
getSpans(value?: RichEditorRange): Array<RichEditorTextSpanResult| RichEditorImageSpanResult>
获取span信息。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | RichEditorRange | 否 | 需要获取span范围。 |
返回值:
类型 | 说明 |
---|---|
Array<RichEditorTextSpanResult | RichEditorImageSpanResult> | 文本和图片Span信息。 |
deleteSpans
deleteSpans(value?: RichEditorRange): void
删除指定范围内的文本和图片。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | RichEditorRange | 否 | 删除范围。省略时,删除所有文本和图片。 |
getParagraphs11+
getParagraphs(value?: RichEditorRange): Array<RichEditorParagraphResult>
获得指定返回的段落。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
value | RichEditorRange | 否 | 需要获取段落的范围。 |
返回值:
类型 | 说明 |
---|---|
Array<RichEditorParagraphResult> | 选中段落的信息。 |
closeSelectionMenu
closeSelectionMenu(): void
关闭自定义选择菜单或系统默认选择菜单。
setSelection11+
setSelection(selectionStart: number, selectionEnd: number)
支持设置文本选中,选中部分背板高亮。
selectionStart和selectionEnd均为-1时表示全选。
接口调用前有带手柄菜单弹出时则调用后不主动关闭菜单,且调整菜单位置。
接口调用前有不带手柄菜单弹出时则调用后不主动关闭菜单,且保持菜单原来位置。
接口调用前无菜单弹出,则调用后也无菜单弹出。
未获焦时调用该接口不产生选中效果。
使用示例。
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
selectionStart | number | 是 | 选中开始位置。 |
selectionEnd | number | 是 | 选中结束位置。 |
getSelection11+
getSelection(): RichEditorSelection
获取选中文本内容。
使用示例。
返回值:
类型 | 说明 |
---|---|
RichEditorSelection | 选中内容信息。 |
RichEditorSelection
选中内容信息。
名称 | 类型 | 必填 | 说明 |
---|---|---|---|
selection | [number, number] | 是 | 选中范围。 |
spans | Array<RichEditorTextSpanResult| RichEditorImageSpanResult> | 是 | span信息。 |
RichEditorUpdateTextSpanStyleOptions
文本样式选项。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
start | number | 否 | 需要更新样式的文本起始位置,省略或者设置负值时表示从0开始。 |
end | number | 否 | 需要更新样式的文本结束位置,省略或者超出文本范围时表示无穷大。 |
textStyle | RichEditorTextStyle | 是 | 文本样式。 |
RichEditorUpdateImageSpanStyleOptions
图片样式选项。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
start | number | 否 | 需要更新样式的图片起始位置,省略或者设置负值时表示从0开始。 |
end | number | 否 | 需要更新样式的图片结束位置,省略或者超出文本范围时表示无穷大。 |
imageStyle | RichEditorImageSpanStyle | 是 | 图片样式。 |
RichEditorParagraphStyleOptions11+
段落样式选项
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
start | number | 否 | 需要更新样式的段落起始位置,省略或者设置负值时表示从0开始。 |
end | number | 否 | 需要更新样式的段落结束位置,省略、负数或者超出文本范围时表示无穷大。 |
style | RichEditorParagraphStyle | 是 | 段落样式。 |
RichEditorParagraphStyle11+
段落样式。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
textAlign | TextAlign | 否 | 设置文本段落在水平方向的对齐方式。 |
leadingMargin | Dimension | LeadingMarginPlaceholder | 否 | 设置文本段落缩进,不支持设置百分比。 |
LeadingMarginPlaceholder11+
前导边距跨度。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
pixelMap | PixelMap | 是 | 图片内容。 |
size | [Dimension, Dimension] | 是 | 图片大小,不支持设置百分比。 |
RichEditorParagraphResult11+
后端返回的段落信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
style | RichEditorParagraphStyle | 是 | 段落样式。 |
range | [number, number] | 是 | 段落起始位置。 |
RichEditorTextSpanOptions
添加文本的偏移位置和文本样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
offset | number | 否 | 添加文本的位置。省略时,添加到所有文本字符串的最后。 当值小于0时,放在字符串最前面;当值大于字符串长度时,放在字符串最后面。 |
style | RichEditorTextStyle | 否 | 文本样式信息。省略时,使用系统默认文本信息。 |
paragraphStyle11+ | RichEditorParagraphStyle | 否 | 段落样式。 |
gesture11+ | RichEditorGesture | 否 | 行为触发回调。省略时,仅使用系统默认行为。 |
RichEditorTextStyle
文本样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
fontColor | ResourceColor | 否 | 文本颜色。 默认值:Color.Black。 |
fontSize | Length | number | 否 | 设置字体大小,Length为number类型时,使用fp单位。字体默认大小16。不支持设置百分比字符串。 从API version 9开始,该接口支持在ArkTS卡片中使用。 |
fontStyle | FontStyle | 否 | 字体样式。 默认值:FontStyle.Normal。 |
fontWeight | FontWeight | number | string | 否 | 字体粗细。 number类型取值[100,900],取值间隔为100,默认为400,取值越大,字体越粗。 string类型仅支持number类型取值的字符串形式,例如“400”,以及“bold”、“bolder”、“lighter”、“regular” 、“medium”分别对应FontWeight中相应的枚举值。 默认值:FontWeight.Normal。 |
fontFamily | ResourceStr | 否 | 设置字体列表。默认字体'HarmonyOS Sans',当前支持'HarmonyOS Sans'字体和注册自定义字体。 默认字体:'HarmonyOS Sans'。 |
decoration | { type: TextDecorationType, color?: ResourceColor } |
否 | 设置文本装饰线样式及其颜色。 默认值:{ type: TextDecorationType.None, color:Color.Black }。 |
textShadow11+ | ShadowOptions | Array<ShadowOptions> | 是 | 设置文字阴影效果。该接口支持以数组形式入参,实现多重文字阴影。 说明: 不支持fill字段, 不支持智能取色模式。 |
RichEditorImageSpanOptions
添加图片的偏移位置和图片样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
offset | number | 否 | 添加图片的位置。省略时,添加到所有文本字符串的最后。 当值小于0时,放在字符串最前面;当值大于字符串长度时,放在字符串最后面。 |
imageStyle | RichEditorImageSpanStyle | 否 | 图片样式信息。省略时,使用系统默认图片信息。 |
gesture11+ | RichEditorGesture | 否 | 行为触发回调。省略时,仅使用系统默认行为。 |
RichEditorImageSpanStyle
图片样式。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
size | [Dimension, Dimension] | 否 | 图片宽度和高度。 |
verticalAlign | ImageSpanAlignment | 否 | 图片垂直对齐方式。 默认值:ImageSpanAlignment.BASELINE |
objectFit | ImageFit | 否 | 图片缩放类型。 默认值:ImageFit.Cover。 |
layoutStyle11+ | { margin ?: Dimension | Margin, borderRadius ?: Dimension | BorderRadiuses } |
否 | 图片布局风格。 |
RichEditorBuilderSpanOptions11+
添加图片的偏移位置和图片样式信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
offset | number | 否 | 添加builder的位置。省略或者为异常值时,添加到所有文本字符串的最后。 |
RichEditorRange
范围信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
start | number | 否 | 起始位置,省略或者设置负值时表示从0开始。 |
end | number | 否 | 结束位置,省略或者超出文本范围时表示无穷大。 |
SelectionMenuOptions11+
范围信息。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
onAppear | () => void | 否 | 自定义选择菜单弹出时回调。 |
onDisappear | () => void | 否 | 自定义选择菜单关闭时回调。 |
PasteEvent11+
定义用户粘贴事件。
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
preventDefault | () => void | 否 | 用户自定义粘贴事件。 存在时会覆盖系统粘贴事件。 |
RichEditorGesture11+
用户行为回调。
onClick11+
onClick(callback: (event?: ClickEvent) => void)
点击完成时回调事件。
双击时,第一次点击触发回调事件。
参数:
参数名 | 参数类型 | 必填 | 描述 |
---|---|---|---|
event | ClickEvent | 否 | 用户点击事件。 |
onLongPress11+
onLongPress(callback: (event?: GestureEvent) => void )
长按完成时回调事件。
参数:
参数名 | 参数类型 | 必填 | 描述 |
---|---|---|---|
event | GestureEvent | 否 | 用户长按事件。 |
示例
示例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("更新样式:加粗").onClick(() => {
this.controller.updateSpanStyle({
start: this.start,
end: this.end,
textStyle:
{
fontWeight: FontWeight.Bolder
}
})
})
Button("获取选择内容").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 {
this.content += (item as RichEditorTextSpanResult).value;
this.content += "\n"
}
})
})
Button("删除选择内容").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("0123456789",
{
style:
{
fontColor: Color.Orange,
fontSize: 30
}
})
this.controller.addImageSpan($r("app.media.icon"),
{
imageStyle:
{
size: ["57px", "57px"]
}
})
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("30%")
}
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
.height("70%")
}
}
}
示例2
// xxx.ets
@Entry
@Component
struct RichEditorExample {
controller: RichEditorController = new RichEditorController()
// 自定义键盘组件
@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 })
// 绑定自定义键盘
.customKeyboard(this.CustomKeyboardBuilder()).margin(10).border({ width: 1 })
.height(200)
.borderWidth(1)
.borderColor(Color.Red)
.width("100%")
}
}
}
示例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: "剪切", 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: "复制", 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: "粘贴", 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: "全选", 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: "分享", labelInfo: "" })
.enabled(false)
MenuItem({ startIcon: this.theme.translateIcon, content: "翻译", labelInfo: "" })
.enabled(false)
MenuItem({ startIcon: this.theme.searchIcon, content: "搜索", 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)
}
}
说明:
系统暂未预置加粗、斜体等图标,示例代码使用系统默认图标,开发者使用时需自行替换iconArr中的资源。
示例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%")
}
}
}
示例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%")
}
}
}
示例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("段落左对齐").onClick(() => {
this.controller.updateParagraphStyle({ start: -1, end: -1,
style: {
textAlign: TextAlign.Start,
}
})
})
Button("段落右对齐").onClick(() => {
this.controller.updateParagraphStyle({ start: -1, end: -1,
style: {
textAlign: TextAlign.End,
}
})
})
Button("段落居中").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
}
})
})
}
}
}
}
示例7
// xxx.ets
import font from '@ohos.font'
const canvasWidth = 1000
const canvasHeight = 100
const Indentation = 40
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()
// 获得字体字号级别,分别是从0到4级
public getFontSizeLevel(fontSize: number) {
const fontScaled: number = Number(fontSize) / 16
enum FontSizeScaleThreshold {
SMALL = 0.9,
NORMAL = 1.1,
LEVEL_1_LARGE = 1.2,
LEVEL_2_LARGE = 1.4,
LEVEL_3_LARGE = 1.5
}
let fontSizeLevel: number = 1
if (fontScaled < FontSizeScaleThreshold.SMALL) {
fontSizeLevel = 0
} else if (fontScaled < FontSizeScaleThreshold.NORMAL) {
fontSizeLevel = 1
} else if (fontScaled < FontSizeScaleThreshold.LEVEL_1_LARGE) {
fontSizeLevel = 2
} else if (fontScaled < FontSizeScaleThreshold.LEVEL_2_LARGE) {
fontSizeLevel = 3
} else if (fontScaled < FontSizeScaleThreshold.LEVEL_3_LARGE) {
fontSizeLevel = 4
} else {
fontSizeLevel = 1
}
return fontSizeLevel
}
// 获得字体字号级别,分别是从0到4级
public getmarginLevel(Width: number) {
let marginlevel: number = 1
if (Width == 40) {
marginlevel = 2.0
} else if (Width == 80) {
marginlevel = 1.0
} else if (Width == 120) {
marginlevel = 2/3
} else if (Width == 160) {
marginlevel = 0.5
} else if (Width == 200) {
marginlevel = 0.4
}
return marginlevel
}
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, width: number, level?: number ): PixelMap {
const indentLevel = level ?? 1
const offsetLevel = [22, 28, 32, 34, 38]
const fontSizeLevel = this.getFontSizeLevel(fontSize)
const marginlevel = this.getmarginLevel(width)
const newOffContext: OffscreenCanvasRenderingContext2D = this.offscreenCanvas.getContext("2d", this.settings)
const centerCoordinate = 50
const radius = 10
this.clearCanvas()
newOffContext.ellipse(100 * (indentLevel + 1) - centerCoordinate * marginlevel, offsetLevel[fontSizeLevel], radius * marginlevel, radius, 0, 0, 2 * Math.PI)
newOffContext.fillStyle = '66FF0000'
newOffContext.fill()
return newOffContext.getPixelMap(0, 0, 100 + 100 * indentLevel, 100)
}
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("向右列表缩进").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(100, margin, 1),
size: [margin, 40]
}
}
})
})
Button("向左列表缩进").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(100, margin, 1),
size: [margin, 40]
}
}
})
})
}
Divider()
Row({ space: 5 }) {
Button("向右空白缩进").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("向左空白缩进").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)
}
}
}
示例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("更新样式: 加粗 & 文本阴影").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%")
}
}
}
示例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 }) { // 子组件在容器主抽上行布局
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 }) { // 子组件在容器主抽上反向行布局
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 }) { // 子组件在容器主抽上列布局
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 }) { // 子组件在容器主抽上反向列布局
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: "菜单选项1" })
MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项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("获取选择内容 getSpans").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("获取选择内容 getSelection").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("删除选择内容").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%")
}
}
}
示例10
enableDataDetector和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 = 'XX省XX市XX区XXXX';
@State enableDataDetector: boolean = true;
@State types: TextDataDetectorType[] = [];
build() {
Row() {
Column() {
RichEditor(this.options)
.onReady(() => {
this.controller.addTextSpan('电话号码:' + this.phoneNumber + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('链接:' + this.url + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('邮箱:' + this.email + '\n',
{
style:
{
fontSize: 30
}
})
this.controller.addTextSpan('地址:' + 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%')
}
}
}
最后,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(Harmony NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(Harmony NEXT)资料包含了:鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(Harmony NEXT)最新学习路线
-
HarmonOS基础技能
- HarmonOS就业必备技能
- HarmonOS多媒体技术
- 鸿蒙NaPi组件进阶
- HarmonOS高级技能
- 初识HarmonOS内核
- 实战就业级设备开发
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。
更多推荐
所有评论(0)