Android富文本编辑器进阶版(干货,IOS可照搬逻辑)
#本文主要是讲解部分原理,源码及其使用请移步Github
https://github.com/RexSuper/RichEditor
APK:https://github.com/RexSuper/RichEditor/blob/master/RichHtmlEditorforAndroid/sample/release/sample-release.apk
Demo
https://github.com/RexSuper/RichEditor/tree/master/RichHtmlEditorforAndroid/sample
Add it in your root build.gradle at the end of repositories: allprojects { repositories { ... maven { url 'https://jitpack.io' } } }Step 2. Add the dependency dependencies { implementation 'com.github.RexSuper:RichEditor:1.0.5' }
github上的示例代码必须看,因为富文本有些功能后续不得不自己填充,比如用户的资源文件得先放到自己服务器生成链接等等
2019-09-04 优化用户体验,在编辑时候所有资源改为本地,加载转在线改为,最后统一发布的时候
2019-08-20 增加音频功能 增加链接下载功能 和视频下载功能
2019-08-16 增加各种文件功能
2019-08-15 增加添加视频功能
2019-06-21 增加基本图文混排等功能
目录
源码下载
前言:
简单版图例
基本原理:最小模型示例
asset里面放置一些静态网页和我们需要的初始化css
一个简单的带 contenteditable="true"的内容
安卓通过js和html互相调用 例如设置加粗
Webview中调用js中的加粗
1.网页的contenteditable如何实现hint功能
2.如何获取光标所在文字的所有style(需求场景 如选中了加粗 B变色,但你光标选中了)
4.如何自动换行
5.如何处理图片
6.自带只能实现固定几种的font size (1-7自带几种)怎么指定大小实现font-size
7.webview和js交互
8.如何在客户端load自带美化的css解决
9.事件监听
源码下载
10.添加视频
特别鸣谢
结尾:
前言:
富文本显示还好,Android富文本编辑一直是一个大坑,还得考虑和其他端同步的问题,问题是你无论用什么方式实现,EditText支持的标签并不多,自定义span或者webview自定义标签也较为繁琐。
用html+webview+js肯定是最合适的可直接导出html兼容性高,Android端用js调用本地静态网页中的contenteditable(此原理做IOS富文本编辑可以无缝照搬)
本文致力于网络常见能搜到的功能实现原理外,补全其他可能遇到的坑,网上能搜到的内容只提关键字
本文也是基础RichEditor for Android思路上一个优化和讲解。它提供了如何和html edit之间交流。理论上来说html能实现的安卓就能实现了,我们只是需要找到这些我们需要的方法
- 也可以用另一种理解方式,html本身就自带这些所有功能,兼容性又好,写好html作为静态放到客户端本地。然后通过webview 用js去调这些功能。那为什么不让web前端做成在线的 再通过js调用呢?要让安卓和ios来写这些呢,我想一定是你的领导在 鼓励你走向 真·全栈之路 的 第一步吧。既然要我们做,我们就得硬着头皮做,还要做好~拿人钱财,替人浪费时间不是?
简单版图例
Video | Image | Audio | File And Download |
Italic | Subscript | Superscript | Strikethrough |
Underline | JustifyLeft | JustifyCenter | JustifyRight |
Blockquote | Heading | Undo | Redo |
Indent | Outdent | InsertLink | Checkbox |
TextColor | TextBackgroundColor | FontSize | UnorderedList |
OrderedList | Hint | NewLine | Blod |
android简单富文本显示基本原理:最小模型示例
- spannablestring和html.fromhtml +style 可以简单使用为图文混排,多种颜色但这种只支持到h4部分标签,很多内容如font-size都不支持,只支持small big这样的,此处顺带一提
- android复杂富文本编辑和再现
如果你采用自定义原始view 1.编辑,2.整理成数据传输到后台再回来3.再现,三处都很复杂。如果使用html前端的自带的edit,则可以不用考虑后面的问题,难点在于如果在安卓端去调用一个个功能,然后获取其他状态,光标等问题
- 编辑器的框架搭建(以加粗为例,其他居中 撤销什么的 均是一个逻辑 事件监听 html自带)最小实现模型
asset里面放置一些静态网页和我们需要的初始化css
editor.html
一个简单的带 contenteditable="true"的内容
安卓通过js和html互相调用 例如设置加粗
rich_editor.js
RE.editor = document.getElementById('editor');RE.setBold = function() { document.execCommand('bold', false, null);}
Webview中调用js中的加粗
//简化最小模型代码public void setBold() { exec("javascript:RE.setBold();");}public void exec(String trigger) { load(trigger)}private void load(String trigger) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { evaluateJavascript(trigger, null); } else { loadUrl(trigger); }}
1.网页的contenteditable如何实现hint功能
方法1:
editor.html
Document .placeholder::after { content: attr(placeholder); position: absolute; top: 10px; color: #a9a9a9;}.placeholder-hide::after {display:none;} document.querySelector('.editor').addEventListener("input", function(event) {var inputValue = event.target.innerHTML;if (inputValue) {document.querySelector('.editor').className = 'editor placeholder placeholder-hide'} else {document.querySelector('.editor').className = 'editor placeholder'} })
方法2
jsRE.setPlaceholder = function(placeholder) { RE.editor.setAttribute("placeholder", placeholder);}css#editor[placeholder]:empty:not(:focus):before { content: attr(placeholder); color: #9B9B9B; font-size:15px;}}
抽取成设置方法
function setPlaceholderColor (color) { var placeHolderStyle = document.querySelector("#placeholder-style"); var styleContent = "#editor:empty:not(:focus):before {color:" + color +"}"; if (placeHolderStyle) { placeHolderStyle.innerText = styleContent } else { placeHolderStyle = document.createElement('style') placeHolderStyle.setAttribute('id', 'placeholder-style') placeHolderStyle.innerText = styleContent document.documentElement.appendChild(placeHolderStyle) }}
public void setPlaceholderColor(String color) { exec("javascript:RE.setPlaceholderColor('" + color+ "');"); }
2.如何获取光标所在文字的所有style(需求场景 如选中了加粗 B变色,但你光标选中了)
rich_editor.js
配合7:addEventListener
RE.getSelectedNode = function() { var node,selection; if (window.getSelection) { selection = getSelection(); node = selection.anchorNode; } if (!node && document.selection) { selection = document.selection var range = selection.getRangeAt ? selection.getRangeAt(0) : selection.createRange(); node = range.commonAncestorContainer ? range.commonAncestorContainer : range.parentElement ? range.parentElement() : range.item(0); } if (node) { var item = (node.nodeName == "#text" ? node.parentNode : node); console.log("innerHTML:"+item.innerHTML); console.log("font-size:"+item.style["font-size"]); console.log("color:"+item.getAttribute("color")); console.log("queryCommandState1 bold:"+document.queryCommandState('bold')); }}
4.如何自动换行
public void setNewLine() { exec("javascript:RE.prepareInsert();"); exec("javascript:RE.insertHTML('
');"); }
5.如何处理图片
5.1安卓实现webview图片点击放大方法回调
这种方式比网上搜出来PageFinish后要好
loadUrl("javascript:(function())有时候会因为加载问题,导致代码没添加进去,那种有bug
现提供更好的方式,同时实现的
public final static String IMG_CLICK_JS = "window.onload = function(){" + "var $img = document.getElementsByTagName('img');" + "for(var p in $img){" + " if (typeof $img[p] === 'object') {" + " $img[p].style.width = '100%';" + " $img[p].style.height ='auto';" + " $img[p].onclick = function(e){" + " ImgClick(e.srcElement.src);" + " };" + " }" + "}" + "};" + "function ImgClick(src) {" + " var message = {" + " 'imgUrl' : src," + " };" + " window.imageOnclick.openImage(src);" + "};" + "";
*你也可以按照7.webview和js交互 console.log()传值方式 替换window.imageOnclick.openImage(src);"
将上面代码 直接加入到你的html中
实现对应回调
webview.getSettings().setJavaScriptEnabled(true); webview.addJavascriptInterface(new JavascriptInterfaceImageOnclick(), "imageOnclick");
private class JavascriptInterfaceImageOnclick { @android.webkit.JavascriptInterface public void openImage(String imgUrl) { if (mOnClickImageTagListener != null && !TextUtils.isEmpty(imgUrl)) { mOnClickImageTagListener.onClick(imgUrl); } } }
6.自带只能实现固定几种的font size (1-7自带几种)怎么指定大小实现font-size
//待更新
font size 类似于
- xx-small
- x-small
- small
- medium
- large
- x-large
- xx-large
而我们需要 int px
7.webview和js交互
addJavascriptInterfaceremoveJavascriptInterfaceevaluateJavascript
一般是addJavascriptInterface或者
shouldOverrideUrlLoading
接收信息
其实最好用的是,也最安全
console.log() webview接收editor(WebView)-->webChromeClient -->onConsoleMessage
8.如何在客户端load自带美化的css解决
1.不可二次编辑loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)2.可二次编辑 editor.loadCSS("file:///android_asset/article_night.css")
无法继续编辑的问题
9.事件监听
RE.editor.addEventListener("input", RE.callback);//其他html支持的 keyup ,selectionchange都支持 选中 文本变化 可以查下资料
源码下载
https://github.com/RexSuper/RichHtmlEditorForAndroid
一定要理解,不然产品随便有个新需求你将寸步难行
10.添加视频
// 让其可以继续进去编辑模式RE.insertVideo = function(url,custom) { var html = ' '; RE.insertHTML(html);}
private void addVideo() { //需要编辑框有光标才行 richEditor.focusEditor();// pb.setVisibility(View.VISIBLE); //将视频上传到自己服务器得到链接 //============> richEditor.setNeedSetNewLineAfter(true); richEditor.insertVideo("https://www.w3school.com.cn/example/html5/mov_bbb.mp4", //增加进度控制 "controls=\"controls\"" + //视频显示第一帧 " initial-time=\"0.01\" " + //宽高 "height=\"300\" " + //样式 " style=\"margin-top:10px;max-width:100%;\"" ); }
//临时记录
function insertHtml(html) { var sel,range,div,node sel = window.getSelection()//返回一个Selection对象,用来表示用户选择的文本范围或插入符当前位置。 range = sel.getRangeAt(0) //获取Range,参数为0或其他能够==0,如false,'',null div=document.createElement('div') div.innerHTML=html node=div.firstChild range.deleteContents()//删除目前range的内容 range.insertNode(node)//新增的节点内容 range.setStartAfter(node)//重新定位range(光标位置) sel.removeAllRanges() //清除所有选中 sel.addRange(range) //将新定位的range加入}
特别鸣谢
思路参考 RichEditor for Android
前端知识修正:@ZX
新建交流群
结尾:
这是一个不该由客户端实现又可以实现的功能