- 移动开发
- UI组件
【免费下载链接】richeditor-android
RichEditor for Android is a beautiful Rich Text WYSIWYG Editor for Android.
本文以richeditor-android仓库根目录下的 CHANGELOG.md 为主线,逐条拆解从 1.0.0 到 2.0.0 的版本变更记录,并结合仓库内 RichEditor.java、rich_editor.js 等核心源码,验证每一项 Feature、Update 与 Bugfix 的实际落点。读完本文,你将能够理解 2.0.0 版本为图片、视频、音频等富媒体能力带来的 API 变化,掌握 URL 编解码、只读模式、占位符等关键机制的底层实现,并能在自己的 Android 项目中正确升级与调用这些接口。
版本演进全景:从 1.0.0 到 2.0.0
CHANGELOG.md完整记录了该开源项目的六个正式版本,时间跨度从 2015 年 11 月的产品发布到 2020 年 9 月的 2.0.0 大版本。整理如下:
| 版本 | 发布日期 | 核心内容 |
|---|---|---|
| 2.0.0 | 2020-09-16 | 图片尺寸参数、Youtube/Video/Audio 标签、SDK 25→30、Normalize.css 8.0.1、5 项 Bugfix |
| 1.2.2 | 2017-03-21 | 新增只读模式、SDK 23→25、Support Library 升级、removeFormat 文档修复 |
| 1.2.1 | 2016-08-04 | 修复类型缺失、更新样例工程 |
| 1.2.0 | 2016-01-07 | 新增字号(fontSize)功能 |
| 1.1.0 | 2016-01-07 | 合并 PR #37(CHANGELOG 未详述具体内容) |
| 1.0.0 | 2015-11-10 | 正式产品发布 |
可以看到,2.0.0 是该项目功能与工程化改动最为集中的一次发布:它同时带来了富媒体能力扩充、SDK 目标升级和一批与 WebView 通信机制相关的关键修复。下面以 2.0.0 为重点,逐项深入。
2.0.0 Feature 一:图片插入支持尺寸参数
CHANGELOG.md记录的第一项新特性是 "Supported Image can set size params",对应 PR #176。它解决的核心问题是:在 2.0.0 之前,插入图片只能使用图片原始尺寸,在手机屏幕上经常出现图片过大或过小的问题。
Java 侧 API 的变化
在 RichEditor.java 中,2.0.0 为图片插入提供了三个重载方法:
// 原始尺寸插入 public void insertImage(String url, String alt) { exec("javascript:RE.prepareInsert();"); exec("javascript:RE.insertImage('" + url + "', '" + alt + "');"); } // 仅指定宽度(高度按比例自适应) public void insertImage(String url, String alt, int width) { exec("javascript:RE.prepareInsert();"); exec("javascript:RE.insertImageW('" + url + "', '" + alt + "','" + width + "');"); } // 同时指定宽度与高度 public void insertImage(String url, String alt, int width, int height) { exec("javascript:RE.prepareInsert();"); exec("javascript:RE.insertImageWH('" + url + "', '" + alt + "','" + width + "', '" + height + "');"); }源码注释明确说明了这一设计意图:insertImage(String, String)会按图片原始尺寸展示,而带宽高参数的重载方法可以让开发者手动控制尺寸,使图片适配不同的手机屏幕。width与height均以像素(px)为单位。
JS 侧的对应实现
这三个 Java 方法分别调用 rich_editor.js 中的insertImage、insertImageW、insertImageWH:
RE.insertImage = function(url, alt) { var html = '<img src="' + url + '" alt="' + alt + '" />'; RE.insertHTML(html); } RE.insertImageW = function(url, alt, width) { var html = '<img src="' + url + '" alt="' + alt + '" width="' + width + '"/>'; RE.insertHTML(html); } RE.insertImageWH = function(url, alt, width, height) { var html = '<img src="' + url + '" alt="' + alt + '" width="' + width + '" height="' + height +'"/>'; RE.insertHTML(html); }三者的差异直观可见:不传尺寸时不写width/height属性;传宽度时写入width属性;传宽高时同时写入两者。最终通过document.execCommand('insertHTML', ...)插入编辑器选区。
实践建议
在真实项目中,推荐至少传入width,避免大图撑破编辑区:
mEditor.insertImage("https://example.com/path/to/image.jpg", "图片描述", 320);alt属性会被写入<img>标签,建议填写有意义的描述文本,便于无障碍访问与 SEO 语义化。
2.0.0 Feature 二:Youtube / Video / Audio 富媒体标签
CHANGELOG.md记录的第二项新特性是 "Supported Youtube, Video, Audio tag",对应 PR #189。这是 2.0.0 最显著的功能扩充,让编辑器从纯图文升级为可插入音视频的富媒体编辑器。
视频(Video)
Java API 同样提供三个重载:
mEditor.insertVideo("https://example.com/video.mp4"); mEditor.insertVideo("https://example.com/video.mp4", 360); mEditor.insertVideo("https://example.com/video.mp4", 360, 240);对应 rich_editor.js 生成原生 HTML5<video>标签,并默认带上controls属性以便用户控制播放:
RE.insertVideo = function(url, alt) { var html = '<video src="' + url + '" controls></video><br>'; RE.insertHTML(html); } RE.insertVideoW = function(url, width) { var html = '<video src="' + url + '" width="' + width + '" controls></video><br>'; RE.insertHTML(html); } RE.insertVideoWH = function(url, width, height) { var html = '<video src="' + url + '" width="' + width + '" height="' + height + '" controls></video><br>'; RE.insertHTML(html); }音频(Audio)
Java API 目前只提供单一入参版本:
mEditor.insertAudio("https://example.com/audio.mp3");对应 rich_editor.js 生成带controls的<audio>标签,同样在末尾追加<br>换行:
RE.insertAudio = function(url, alt) { var html = '<audio src="' + url + '" controls></audio><br>'; RE.insertHTML(html); }Youtube 嵌入
Java API 提供三个重载,内部生成<iframe>嵌入播放器:
mEditor.insertYoutubeVideo("https://www.youtube.com/embed/pS5peqApgUA"); mEditor.insertYoutubeVideo("https://www.youtube.com/embed/pS5peqApgUA", 640); mEditor.insertYoutubeVideo("https://www.youtube.com/embed/pS5peqApgUA", 640, 360);注意这里传入的 URL 应当是 YouTube 的embed形式的嵌入链接,而不是普通分享页。对应 rich_editor.js 生成的 iframe 携带了较为完整的播放器参数:
RE.insertYoutubeVideo = function(url) { var html = '<iframe width="100%" height="100%" src="' + url + '" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br>' RE.insertHTML(html); }allow属性覆盖了自动播放、剪贴板写入、加密媒体、陀螺仪、画中画等常见能力,allowfullscreen保证可全屏播放。默认(无参)时宽高为 100%,可通过带宽高参数的重载固定播放器尺寸。
样例工程中的调用示范
MainActivity.java 中可以看到这些新 API 的完整接线方式,例如:
mEditor.insertImage("https://raw.githubusercontent.com/wasabeef/art/master/chip.jpg", "dachshund", 320); mEditor.insertYoutubeVideo("https://www.youtube.com/embed/pS5peqApgUA"); mEditor.insertAudio("https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_5MG.mp3"); mEditor.insertVideo("https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4", 360);工具栏按钮与这些方法一一对应,完整布局可参考 activity_main.xml。
2.0.0 Update:compile / target SDK 25 → 30 与 Normalize.css 8.0.1
CHANGELOG.md记录了 2.0.0 的两项工程化升级:
SDK 版本升级
"Compile / Target SDK Version 25 -> 30"。在 gradle.properties 中可以看到当前版本的实际取值:
VERSION_NAME=2.0.0 VERSION_CODE=200 COMPILE_SDK_VERSION=30 TARGET_SDK_VERSION=30 MIN_SDK_VERSION=14即 2.0.0 的编译与目标 SDK 均为 30(Android 11),而最低支持版本仍是 API 14(Android 4.0),与 README 中 "Android 4+" 的要求一致。这一升级使得WebView的evaluateJavascript路径(API 19+)成为默认执行方式,详见 RichEditor.java:
private void load(String trigger) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { evaluateJavascript(trigger, null); } else { loadUrl(trigger); } }SDK 30 下绝大多数设备走evaluateJavascript,loadUrl("javascript:...")仅作为 API 19 以下设备的降级方案保留。
Normalize.css 升级
"Normalize.css to 8.0.1"。仓库内的 normalize.css 文件头注释明确标注为 "normalize.css v8.0.1",与 CHANGELOG 记录一致。Normalize.css 的作用是抹平不同浏览器/WebView 内核间的默认样式差异,保证contenteditable编辑区域在各 Android WebView 上呈现一致的排版。该文件与 style.css、editor.html 一起打包进 APK 的 assets 目录,由RichEditor构造时通过file:///android_asset/editor.html加载(见 RichEditor.java)。
2.0.0 Bugfix 源码级解读
CHANGELOG.md的 Bugfix 列表共五项,全部与编辑器的 JavaScript 桥接机制相关,值得逐一深入。
修复一:placeholder(PR #240)
占位符功能通过setPlaceholder(String)设置,Java 侧调用 rich_editor.js 的setPlaceholder,把占位文本写到编辑区 DOM 的placeholder属性上:
RE.setPlaceholder = function(placeholder) { RE.editor.setAttribute("placeholder", placeholder); }而占位符的显示逻辑在 style.css 中:
#editor[placeholder]:empty:not(:focus):before { content: attr(placeholder); opacity: .5; }该 CSS 选择器的三个条件是:编辑区带有placeholder属性、内容为空(:empty)、且未处于聚焦状态(:not(:focus))。PR #240 修复的正是这条显示链路上的问题——例如空内容但存在空白字符、聚焦状态判断等导致占位符不显示或显示时机错误的场景。修复后占位符以半透明(opacity: .5)方式显示,一旦用户开始输入或获得焦点便自动隐藏。
修复二:typo(PR #194)
CHANGELOG 记录 "Fixed typp",即修复了一处拼写错误。这类修复通常涉及方法名、变量名或注释中的拼写问题,虽然不改变功能行为,但会直接影响调用方能否正确编译。从当前源码看,编辑器所有对外方法命名规范一致(如setStrikeThrough、setUnderline等),说明命名已在修复后趋于稳定。
修复三与五:URL 解码与 encodeURIComponent(PR #123、PR #145)
这两项修复共同保证了编辑器内容从 JS 到 Java 的传输不被破坏,是整个桥接机制中最核心的部分。
JS 侧的回调在 rich_editor.js:
RE.callback = function() { window.location.href = "re-callback://" + encodeURIComponent(RE.getHtml()); }编辑器内容(HTML 字符串)先经encodeURIComponent()编码,再拼接到re-callback://自定义 scheme 上,通过window.location.href触发 WebView 的 URL 拦截。Java 侧在 RichEditor.java 的shouldOverrideUrlLoading中接收并解码:
String decode = Uri.decode(url); if (TextUtils.indexOf(url, CALLBACK_SCHEME) == 0) { callback(decode); return true; } else if (TextUtils.indexOf(url, STATE_SCHEME) == 0) { stateCheck(decode); return true; }RE.getHtml()返回的是innerHTML,其中可能包含引号、空格、中文、HTML 实体乃至多语言字符,如果不编码直接拼进 URL,会破坏 URL 结构或导致解码错乱。encodeURIComponent()(PR #145)保证编码完整性,Uri.decode()(PR #123)则负责在 Java 侧还原原始 HTML。
仓库内 UrlDecoderTest.java 用 Robolectric 对这一链路做了针对性测试:测试字符串同时包含空格、<div>/<font>标签、引号、HTML 实体 、连续加号、中文"下"、日文"ぁ"以及阿拉伯文"ص",编码后经Uri.decode能完整还原,验证了多语言与特殊字符场景下的解码正确性。
修复四:shouldOverrideUrlLoading(PR #133)
shouldOverrideUrlLoading在 Android 版本间存在签名差异,RichEditor.java 中实现了两个重载:
// API 21 之前的旧签名 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { ... } // API 21+ 的新签名(带 WebResourceRequest) @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { final String url = request.getUrl().toString(); ... }PR #133 修复的正是这个兼容性问题:在 Android 5.0(API 21)引入新签名后,旧签名在新设备上不再被调用,导致re-callback://与re-state://拦截失效、文本变更回调丢失。两个重载采用相同的拦截逻辑,内部都先做Uri.decode,再判断 scheme 前缀,命中后消费该 URL 并返回true,避免 WebView 真正去加载自定义 scheme。
历史版本亮点回顾
2.0.0 之外的早期版本同样留下了延续至今的核心能力,结合源码可以一一印证。
1.2.2:只读模式(PR #101)
"Add ability to disable input on editor making it readonly" 是 1.2.2 引入的能力,对应今天的setInputEnabled(Boolean):
public void setInputEnabled(Boolean inputEnabled) { exec("javascript:RE.setInputEnabled(" + inputEnabled + ")"); }JS 侧实现 rich_editor.js 直接控制contentEditable属性:
RE.setInputEnabled = function(inputEnabled) { RE.editor.contentEditable = String(inputEnabled); }传入false时编辑器变为只读,内容不可编辑但仍可展示、滚动与复制;传入true恢复可编辑。这在"预览模式""表单确认页"等场景非常实用。
同期 1.2.2 还记录了 compile/target SDK 23→25、Build Tools 24.0.2→25.0.2、Support Library 23.4.0→25.3.0 的工程升级,以及两项 Bugfix:修复removeFormat缺失的文档(该 API 存在于 RichEditor.java 与 JS 的RE.removeFormat中),以及外部 RSBlur 项目的资源清理。
1.2.0:字号功能(PR #42)
字号功能对应 Java APIsetFontSize(int),RichEditor.java 对参数范围有明确约束:
public void setFontSize(int fontSize) { if (fontSize > 7 || fontSize < 1) { Log.e("RichEditor", "Font size should have a value between 1-7"); } exec("javascript:RE.setFontSize('" + fontSize + "');"); }取值限制在 1~7 之间,超出范围会输出Log.e错误日志(注意:仍会照常执行命令)。JS 侧通过document.execCommand('fontSize', false, fontSize)应用字号。
1.2.1 / 1.1.0 / 1.0.0
- 1.2.1(2016-08-04):修复部分类型缺失(对应 PR #63),并更新了样例工程。从源码结构看,格式化类型统一收拢在 RichEditor.java 的
Type枚举中(BOLD、ITALIC、H1~H6、ORDEREDLIST、UNORDEREDLIST、JUSTIFYCENTER/JUSTIFYFULL/JUSTIFYLEFT/JUSTIFYRIGHT 等)。 - 1.1.0(2016-01-07):合并 PR #37,具体变更内容未在 CHANGELOG 中详述。
- 1.0.0(2015-11-10):产品正式发布,宣告该项目进入可用状态。
从 CHANGELOG 到源码:三个值得留意的实现细节
在对照 CHANGELOG 与源码的过程中,有三个细节能帮助你更准确地使用 2.0.0:
第一,命令执行有"就绪队列"机制。RichEditor.java 中的exec()会先检查isReady(由onPageFinished在editor.html加载完成后置为 true)。如果 WebView 尚未就绪,命令会以 100ms 间隔延迟重试,直到页面加载完成。因此onCreate中立即调用setBold()等命令也是安全的,不必担心 JS 尚未初始化。
第二,内容回传走自定义 scheme。除了re-callback://(内容变更回调),还有re-state://(光标处格式状态回调)。RichEditor.java 的stateCheck会把 JS 上报的格式状态与Type枚举做匹配,供OnDecorationStateListener使用——这正是工具栏按钮高亮"当前光标处是加粗还是斜体"这一交互的底层来源。
第三,setHtml 需要保持编码一致。RichEditor.java 的setHtml使用URLEncoder.encode(contents, "UTF-8")编码后再传给 JS,JS 端则用decodeURIComponent(contents.replace(/\+/g, '%20'))还原(rich_editor.js)。其中+会被替换回空格,与回调方向的encodeURIComponent+Uri.decode形成完整的双向编解码闭环。
如何在项目中落地 2.0.0
依赖引入
2.0.0 已发布到 Maven Central,按 README.md 说明配置:
repositories { mavenCentral() } dependencies { implementation 'jp.wasabeef:richeditor-android:2.0.0' }若需自行编译验证,可在仓库根目录执行./gradlew :sample:assembleDebug(当前仓库 build.gradle 使用 AGP 4.2.0-beta04,样例工程通过 sample/build.gradle 的implementation project(':richeditor')依赖本地模块)。
新增 API 使用清单
升级到 2.0.0 后,与旧版最大的差异集中在富媒体插入:
| 能力 | 2.0.0 API | 生成的 HTML 标签 |
|---|---|---|
| 图片(原尺寸) | insertImage(url, alt) | <img> |
| 图片(指定宽度) | insertImage(url, alt, width) | <img width="..."> |
| 图片(指定宽高) | insertImage(url, alt, width, height) | <img width height> |
| 视频 | insertVideo(url[, width[, height]]) | <video controls> |
| 音频 | insertAudio(url) | <audio controls> |
| Youtube | insertYoutubeVideo(url[, width[, height]]) | <iframe> |
测试验证
仓库在 richeditor/src/test 下提供了基于 Robolectric 的单元测试,其中 UrlDecoderTest.java 覆盖了多语言、特殊字符的 URL 解码链路,是验证"内容回传不丢字"这一 2.0.0 修复成果的直接手段,可在本机运行验证。
总结
CHANGELOG.md虽然只有短短几十行,却完整勾勒了 richeditor-android 从 1.0.0 到 2.0.0 的能力演进主线:2015 年完成产品化,2016 年补齐字号与类型修复,2017 年引入只读模式,最终在 2020 年的 2.0.0 上集中兑现了图片尺寸可控与 Youtube/Video/Audio 三类富媒体标签,同时将工程基线推进到 SDK 30,并借助encodeURIComponent+Uri.decode的编解码闭环与双版本shouldOverrideUrlLoading兼容修复,从根本上加固了 WebView 与 Java 层之间的通信可靠性。对照 RichEditor.java 与 rich_editor.js 阅读 CHANGELOG,每一项记录都能找到确切的代码落点,这既是一份版本历史,更是一份浓缩的架构设计说明书。
- 移动开发
- UI组件
【免费下载链接】richeditor-android
RichEditor for Android is a beautiful Rich Text WYSIWYG Editor for Android.
相关推荐
buildkit 依赖中的 ULID 演进史:oklog/ulid v2 CHANGELOG 逐版本解析与源码印证
buildkit 依赖中的 ULID 演进史:oklog/ulid v2 CHANGELOG 逐版本解析与源码印证 本篇技术文章以 buildkit 仓库 ve
构建工具云原生后端@vuelidate/validators 版本演进全解析:从 2.0.0-alpha 到 2.0.4 的关键变更与源码验证
@vuelidate/validators 版本演进全解析:从 2.0.0 alpha 到 2.0.4 的关键变更与源码验证 本指南以 Vuelidate 仓库
前端Pyxel 2.5 → 2.9 演进全解析:从 CHANGELOG 到源码级验证的版本指南
Pyxel 2.5 → 2.9 演进全解析:从 CHANGELOG 到源码级验证的版本指南 本篇技术指南以 Pyxel 开源仓库根目录的 CHANGELOG.m
游戏开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考