开天辟地 HarmonyOS(鸿蒙) - 组件(文本类): RichEditor(富文本编辑器)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 组件(文本类): RichEditor(富文本编辑器)

示例如下:

pages\component\text\RichEditorDemo.ets

/** RichEditor - 富文本编辑器(支持图文混排和文本交互式编辑)** 注:部分用法和 Text, TextInput 差不多,详见 TextDemo.ets, TextInputDemo.ets, StyledStringDemo.ets 中的说明*/import { TitleBar } from '../../TitleBar'@Entry
@Component
struct RichEditorDemo {build() {Column({ space: 10 }) {TitleBar()Tabs() {TabContent() { MySample1() }.tabBar('基础').align(Alignment.Top)TabContent() { MySample2() }.tabBar('添加自定义组件').align(Alignment.Top)TabContent() { MySample3() }.tabBar('自定义上下文菜单').align(Alignment.Top)TabContent() { MySample4() }.tabBar('属性字符串').align(Alignment.Top)}.scrollable(true).barMode(BarMode.Scrollable)}}
}@Component
struct MySample1 {// RichEditorController 是用于和 RichEditor 交互的,声明式编程通常都会用这种方式controller: RichEditorController = new RichEditorController();options: RichEditorOptions = { controller: this.controller };@State message: string = ""build() {Column({space: 10}) {Text(this.message).fontSize(16)/** RichEditor - 富文本编辑器(可以添加编辑多个不同种类的 span)*   placeholder() - 无输入时的占位文本*   onReady() - 富文本编辑器初始化完成后的回调*   onSelect() - 选中内容后的回调*   aboutToIMEInput(), onIMEInputComplete() - 输入字符前和输入字符后的一对回调*   aboutToDelete(), onDeleteComplete() - 删除字符前和删除字符后的一对回调* RichEditorController - 富文本编辑器的 controller*   addTextSpan() - 添加文本 span*   addSymbolSpan() - 添加符号图标 span*   addImageSpan() - 添加图片 span*   addBuilderSpan() - 插入自定义组件 span*   updateSpanStyle() - 更新指定范围的文本 span 的样式*   updateParagraphStyle() - 更新指定范围的段落的样式*   setTypingStyle() - 设置新输入的文本的样式*   getTypingStyle() - 获取新输入的文本的样式*   getSpans() - 获取指定范围内的所有 span*   deleteSpans() - 删除指定范围内的数据*   getParagraphs() - 获取指定范围内的所有段落*   setSelection() - 设置选中内容的范围*   getSelection() - 获取选中内容的范围和选中的内容*/Button("全部斜体").onClick(() => {// 全部文本都变为斜体this.controller.updateSpanStyle({start: -1,end: -1,textStyle:{fontStyle: FontStyle.Italic}})})Button("字符索引 1 - 4 加粗蓝色").onClick(() => {// 将索引位置 1 - 4 的所有文本的样式变为蓝色且加粗this.controller.updateSpanStyle({start: 1,end: 5,textStyle:{fontWeight: FontWeight.Bolder,fontColor: Color.Blue,}})})Button("获取字符索引 1 - 9 的所有父 span 集合").onClick(() => {// 获取索引位置 1 - 9 的内容的所属的所有父 span 集合let content = "";this.controller.getSpans({start: 1,end: 10}).forEach(item => {// 当前 span 是图片 spanif (typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined'){content += (item as RichEditorImageSpanResult).valueResourceStr;content += "\n"}// 当前 span 是符号图标 spanelse if(typeof(item as RichEditorTextSpanResult)['symbolSpanStyle'] != 'undefined') {content += (item as RichEditorTextSpanResult).symbolSpanStyle?.fontSize;content += "\n"}// 当前 span 是文本 spanelse {content += (item as RichEditorTextSpanResult).value;content += "\n"}})this.message = content})Button("字符索引 1 - 2 删除").onClick(() => {// 删除索引位置 1 - 2 的内容this.controller.deleteSpans({start: 1,end: 3})})Button("全部段落居右显示").onClick(() => {// 全部段落居右显示this.controller.updateParagraphStyle({start: -1,end: -1,style: {textAlign: TextAlign.End,}})})Column() {RichEditor(this.options).placeholder("请输入", {fontColor: Color.Gray,font: {size: 16,weight: FontWeight.Normal,style: FontStyle.Normal,family: "HarmonyOS Sans",}}).onReady(() => {// 添加文本 span,并指定其样式以及手势事件this.controller.addTextSpan("012345",{style:{fontColor: Color.Orange,fontSize: 32},gesture:{onClick: () => {this.message = "app.media.app_icon onClick"},onLongPress: () => {this.message = "app.media.app_icon onClick onLongPress"}}})// 添加符号图标 span,并指定其样式this.controller.addSymbolSpan($r("sys.symbol.wifi"),{style:{fontSize: 32}})// 添加图片 span,并指定其样式this.controller.addImageSpan($r("app.media.app_icon"),{imageStyle:{size: ["48vp", "48vp"]}})// 添加文本 span,并指定其样式this.controller.addTextSpan("56789",{style:{fontColor: Color.Black,fontSize: 32}})// 设置新输入的文本的样式this.controller.setTypingStyle({fontColor: Color.Blue,fontSize: 48,})})// 选中内容后的回调.onSelect((value: RichEditorSelection) => {this.message += `\nonSelect: ${value.selection[0]}, ${value.selection[1]}`})// 输入字符前的回调.aboutToIMEInput((value: RichEditorInsertValue) => {this.message = `aboutToIMEInputinsertValue:${value.insertValue},insertOffset:${value.insertOffset},previewText:${value.previewText}`// 返回 true 则代表允许此次输入,然后会调用 onIMEInputComplete()// 返回 false 则代表拒绝此次输入,之后也不会调用 onIMEInputComplete()return true;})// 输入字符后的回调.onIMEInputComplete((value: RichEditorTextSpanResult) => {// value - 输入的字符的所属的 span 的值// spanIndex - 输入的字符的所属的 span 在 RichEditor 中的所有 span 中的索引位置// offsetInSpan - 输入的字符在所属 span 中的索引位置this.message += `\nonIMEInputCompletevalue:${value.value}previewText:${value.previewText}spanIndex:${value.spanPosition.spanIndex}spanRange:${value.offsetInSpan[0]}, ${value.offsetInSpan[1]}`})// 删除字符前的回调.aboutToDelete((value: RichEditorDeleteValue) => {// richEditorDeleteSpans - 删除的所有内容的所属 span 的集合(如何判断当前 span 的类型已经在前面说明过了)this.message = `aboutToDelete,
offset:${value.offset}
direction:${value.direction}
length:${value.length}
richEditorDeleteSpans length:${value.richEditorDeleteSpans.length}`// 返回 true 则代表允许此次删除,然后会调用 onDeleteComplete()// 返回 false 则代表拒绝此次删除,之后也不会调用 onDeleteComplete()return true;})// 删除字符后的回调.onDeleteComplete(() => {this.message += `\nonDeleteComplete`}).borderWidth(1).borderColor(Color.Blue).width("95%").height("200")}}}
}@Component
struct MySample2 {@State message:string = ""controller: RichEditorController = new RichEditorController();// 自定义组件@Builder myBuilder() {Row({ space: 10 }) {Image($r("app.media.app_icon")).width(24).height(24)Text('hello').fontSize(16)}.backgroundColor(Color.Orange)}// 自定义组件 spanprivate myBuilderSpan: CustomBuilder = this.myBuilder.bind(this)build() {Column({space: 10}) {Text(this.message).fontSize(16)RichEditor({controller: this.controller}).onReady(() => {this.controller.addTextSpan("012",{style:{fontColor: Color.Red,fontSize: 24,}})this.controller.addTextSpan("34567",{style:{fontColor: Color.Red,fontSize: 24,}})}).borderWidth(1).borderColor(Color.Blue).width("95%").height("200")Button("插入自定义组件 span").onClick(() => {/** addBuilderSpan() - 插入自定义组件 span*   value - 需要插入的自定的组件 span*   options - 插入选项*     offset - 插入的位置*   返回值为:插入后,自定义组件 span 在所有 span 中的索引位置*/let index = this.controller.addBuilderSpan(this.myBuilderSpan, {offset: 5, // 在索引位置 5 之前插入自定义组件 span})// 以本例来说// 第一次在索引位置 5 之前插入自定义组件 span 后,原来的 2 个 span 会变为 4 个 span// 第 1 个 span 是 012// 第 2 个 span 是 34// 第 3 个 span 是 新插入的自定义组件 span// 第 4 个 span 是 567this.message = `spanIndex:${index}, spansCount:${this.controller.getSpans().length}`})}}
}@Component
struct MySample3 {@State message: string = ''controller: RichEditorController = new RichEditorController();// 自定义组件@Builder myBuilder() {Row({ space: 10 }) {Image($r("app.media.app_icon")).width(24).height(24)Text('hello').fontSize(16)}.backgroundColor(Color.Orange)}// 自定义组件 spanprivate myBuilderSpan: CustomBuilder = this.myBuilder.bind(this)@Builder MyContextMenu() {Column() {Menu() {MenuItemGroup() {MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 1" }).onClick((event) => {// 关闭指定的菜单this.controller.closeSelectionMenu();})MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 2" })MenuItem({ startIcon: $r('app.media.app_icon'), content: "menu 3" })}.backgroundColor(Color.Orange)}}}build() {Column({ space: 10 }) {/** bindSelectionMenu() - 使用自定义的上下文菜单*   spanType - 哪种类型的内容被选中后需要弹出自定义的上下文菜单(RichEditorSpanType 枚举)*     TEXT - 选中的内容只有文本时*     IMAGE - 选中的内容只有图片时*     BUILDER - 选中的内容只有自定义组件 span 时*     MIXED - 选中的内容有 2 种或以上的类型时*   content - 自定义的上下文菜单组件*   TextResponseType - 调出上下文菜单的方式(ResponseType 枚举)*     RightClick - 右键*     LongPress - 长按*   options - 选项*     onAppear - 自定义菜单弹出时的回调*     onDisappear - 自定义菜单关闭时的回调*/RichEditor({controller: this.controller}).onReady(() => {this.controller.addTextSpan("01234",{style:{fontColor: Color.Orange,fontSize: 32}})this.controller.addImageSpan($r("app.media.app_icon"),{imageStyle:{size: ["48vp", "48vp"]}})this.controller.addBuilderSpan(this.myBuilderSpan)}).bindSelectionMenu(RichEditorSpanType.MIXED, this.MyContextMenu, ResponseType.LongPress, {onAppear: () => {this.message = `自定义菜单弹出了`;},onDisappear: () => {this.message = `自定义菜单关闭了`;},}).borderWidth(1).borderColor(Color.Blue).width("95%").height("200")}}
}@Component
struct MySample4 {@State message:string = ""controller1: RichEditorController = new RichEditorController();controller2: TextController = new TextController();build() {Column({ space: 10 }) {RichEditor({controller: this.controller1}).onReady(() => {this.controller1.addTextSpan("012345",{style:{fontColor: Color.Orange,fontSize: 32}})this.controller1.addImageSpan($r("app.media.app_icon"),{imageStyle:{size: ["48vp", "48vp"]}})this.controller1.addTextSpan("56789",{style:{fontColor: Color.Black,fontSize: 32}})}).borderWidth(1).borderColor(Color.Blue).width("95%").height("100")Text(undefined, { controller: this.controller2 }).fontSize(16)Text(this.message).fontSize(16)/** 关于 RichEditor 的属性字符串,即 StyledString 的用法详见 StyledStringDemo.ets 中的说明* RichEditorController - 用于和 RichEditor 交互*   toStyledString() - 将指定范围的内容转为 StyledString*   fromStyledString() - 将指定的 StyledString 转为 span 集合*/Button("toStyledString() 和 fromStyledString()").onClick(() => {// 将全部内容转为 StyledString 对象let styledString = this.controller1.toStyledString({start: -1,end: -1})this.controller2.setStyledString(styledString)let spans = this.controller1.fromStyledString(styledString)this.message = JSON.stringify(spans)})}}
}

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/879096.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用ollama运行deepseek-r1模型

Ollama是一个功能强大的本地大语言模型的运行框架,用户可以非常简单地运行使用各种LLM。最最主要的是它完全免费,本地可以离线运行; 1. 最简单的安装:curl https://ollama.ai/install.sh | sh 2.也可以访问ollam.com直接点击下载安装3. 安装完成之后,ollama list一下可以看…

【python应用】Python也能使用动态链接库

前言 动态链接库(DLL)想必大家都不陌生了吧,C/C++编程经常会用到,那么,它跟我们的Python有什么关系?要说关系恐怕也就是Python是用C写的了,不过,还有一点更重要的关系,那就是Python可以调用C函数,这一点,在Pywin32中有所体现。下面我们就来详细了解下到底Python使用动…

ERP+PLM+MDM

ERP+PLM+MDM 新一代汽车数字工厂PLM+PLM+MDM集成解决方案专为汽车整车行业设计,通过整合企业业务流程和优化关键数据管理,实现了设计制造一体化。该方案以业务流程为导向,围绕整车项目研发、快速变型设计、产品配置和装配工艺设计四大业务场景,提供全面的数据管理和流程控制…

AI应用示例:用AI通过单张面部照片预测您的职业与受教育程度

AI 生成的图像 | Flux Labs AI 制作想象一下,你走进一场面试,还没开口,AI 面试官就已经根据你的脸对你做出评估。这是不是 “以貌取人 2.0”?这算是歧视吗?还是 AI 正在推动一种新的 “面相主义”(face-ism)? 最近,来自多所大学的研究人员进行了一项研究,他们声称 AI…

电脑本地部署deepseek教程

Ollama:本地大模型运行指南奇舞精选2024-04-2814,445阅读11分钟关注 智能总结 复制 重新生成 这篇文章主要介绍了本地大模型运行框架 Ollama。包括其简介、安装方法(下载安装及大模型下载)、终端对话(如显示帮助、模型信息等命令)、API 调用(generate 和 chat 两…

可能是对春节假期的一些总结

写在前面2603 字 | 总结 | 经历 | 思考 | 感触未经允许,禁止转载。 正文「我一直都应该知道,听到别人说出她的名字,我还是会心里一跳。」过年与我当初预计的完全不同。我当初的计划,春节七八天,写很多很多文章,极速推进今年年度计划的写作一项。为此还专门订阅了一个月的…

搜狗录音笔c1折腾

1.买的pdd厂家帮我整好了大部分(应该刷过固件) 2.遇到的问题就是c to c的线...它没有协商,得插电脑usb-a口上才能识别到 3.厂家提供的搜狗录音笔助手这个软件是有点小处理的不是原软件(指解锁登录,其他的没看出问题,至少软件没报毒)

task3

任务二:Smiling-Weeping-zhr/Travel-assistant 自建项目链接,有关大模型关于大模型解答旅游相关

1.4 条件分支和循环机制

程序的流程分为顺序执行、条件分支和循环三种 顺序执行是按照地址内容的顺序执行指令。 条件分支是根据条件执行任意地址的指令。 循环是指重复执行同一地址的指令。 顺序执行每执行一个指令程序计数器的值自动加1条件分支和循环中使用的跳转指令,会参照当前执行的运算结果来判…

vue3使用flv.js播放flv直播流

目前有个需求是:管理直播机有一个列表需要查看每个直播机的实时内容,所以需要在后台加这个功能。 效果:我用ffmpeg模拟推流: 如何用ffmpeg模拟推流请看我上一篇文章 网页: 如上是可以正确再网页端拉流,这个功能费了一天的时间,在这里免费分享出来。 首先安装flv.js(用n…

DeepSeep本地部署

1、浏览器搜索引擎更改 2、搜索:Ollama操作下载: 也可直接进入下载路径:https://ollama.com/download/windows ollama run deepseek-r1 本文来自博客园,作者:他还在坚持嘛,转载请注明原文链接:他还在坚持嘛 https://www.cnblogs.com/brf-test/p/18699050