技术标签: 富文本编辑 iOS富文本编辑 android富文本编辑
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 增加基本图文混排等功能
目录
一个简单的带 contenteditable="true"的内容
1.网页的contenteditable如何实现hint功能
2.如何获取光标所在文字的所有style(需求场景 如选中了加粗 B变色,但你光标选中了)
6.自带只能实现固定几种的font size (1-7自带几种)怎么指定大小实现font-size
富文本显示还好,Android富文本编辑一直是一个大坑,还得考虑和其他端同步的问题,问题是你无论用什么方式实现,EditText支持的标签并不多,自定义span或者webview自定义标签也较为繁琐。
用html+webview+js肯定是最合适的可直接导出html兼容性高,Android端用js调用本地静态网页中的contenteditable(此原理做IOS富文本编辑可以无缝照搬)
本文致力于网络常见能搜到的功能实现原理外,补全其他可能遇到的坑,网上能搜到的内容只提关键字
本文也是基础RichEditor for Android思路上一个优化和讲解。它提供了如何和html edit之间交流。理论上来说html能实现的安卓就能实现了,我们只是需要找到这些我们需要的方法
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 |
editor.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="normalize.css">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="editor" contenteditable="true"></div>
<script type="text/javascript" src="rich_editor.js"></script>
</body>
</html>
rich_editor.js
RE.editor = document.getElementById('editor');
RE.setBold = function() {
document.execCommand('bold', false, null);
}
//简化最小模型代码
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:
editor.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.placeholder::after {
content: attr(placeholder);
position: absolute;
top: 10px;
color: #a9a9a9;
}
.placeholder-hide::after {
display:none;
}
</style>
</head>
<body>
<div class="editor placeholder" contenteditable="true" placeholder="你想说什么"></div>
<script>
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'
}
})
</script>
</body>
</html>
方法2
js
RE.setPlaceholder = function(placeholder) {
RE.editor.setAttribute("placeholder", placeholder);
}
css
#editor[placeholder]:empty:not(:focus):before {
content: attr(placeholder);
color: #9B9B9B;
font-size:15px;
}}
抽取成设置方法
<script>
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)
}
}
</script>
public void setPlaceholderColor(String color) {
exec("javascript:RE.setPlaceholderColor('" + color+ "');");
}
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'));
}
}
public void setNewLine() {
exec("javascript:RE.prepareInsert();");
exec("javascript:RE.insertHTML('<br></br>');");
}
5.1安卓实现webview图片点击放大方法回调
这种方式比网上搜出来PageFinish后要好
loadUrl("javascript:(function())有时候会因为加载问题,导致代码没添加进去,那种有bug
现提供更好的方式,同时实现的
public final static String IMG_CLICK_JS = "<script type='text/javascript'>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);" +
"};" +
"</script>";
*你也可以按照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);
}
}
}
//待更新
font size 类似于
addJavascriptInterface
removeJavascriptInterface
evaluateJavascript
一般是addJavascriptInterface或者
shouldOverrideUrlLoading
接收信息
其实最好用的是,也最安全
console.log() webview接收
editor(WebView)-->webChromeClient -->onConsoleMessage
1.不可二次编辑
loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl)
2.可二次编辑
editor.loadCSS("file:///android_asset/article_night.css")
无法继续编辑的问题
RE.editor.addEventListener("input", RE.callback);//其他html支持的 keyup ,selectionchange都支持 选中 文本变化 可以查下资料
https://github.com/RexSuper/RichHtmlEditorForAndroid
一定要理解,不然产品随便有个新需求你将寸步难行
// 让其可以继续进去编辑模式
RE.insertVideo = function(url,custom) {
var html = ' <video src="' + url + '" ' + custom +'></video> ';
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加入
}
前端知识修正:@ZX
这是一个不该由客户端实现又可以实现的功能
文章浏览阅读4.9k次。Sublime text 3搭建Python开发环境及常用插件安装_sublime python 环境搭建
文章浏览阅读643次。MySQL在首次安装后会执行一个安全脚本,用于设置root用户的密码以及其他安全选项。_centos7安装mysql8.0gpg密钥
文章浏览阅读864次。这种场景下,可以使用两个系列,一个系列是完整的图形,当做『背景』来表达总数值,另一个系列是使用 `symbolClip` 进行剪裁过的图形,表达当前数值。_echarts symbolboundingdata
文章浏览阅读1k次,点赞18次,收藏16次。这篇文章主要介绍了学python对电脑配置要求高吗,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。_python机器学习需要怎样配置的电脑
文章浏览阅读3.9k次。Datawhale开源开源方向:OCR开源项目01导读OCR方向的工程师,之前一定听说过PaddleOCR这个项目,其主要推荐的PP-OCR算法更是被国内外企业开发者广泛应用,短短半年..._github 2023年最新表格ocr
文章浏览阅读317次。【竞赛+作品集,点燃你的设计理想】设计课开题啦!百川柯纳陆续推出以国际设计竞赛项目为参考的设计题目让大家参与,借此丰富履历,充实作品集。本期的设计题目为:Parameterized Complexities参数化建筑设计。喜欢参数化的小伙伴,你们兴奋吗?Parameterized Complexities 选题背景 近期不断有小伙伴在后台给我们留言,或者咨询百川柯纳顾问老师表达希望能够参加以“参数..._python 建筑平面图
文章浏览阅读1k次。ATF点滴1、设置运行时栈SP2、寄存器的保存和恢复的实现3、寄存器的保存和恢复的使用场景1、设置运行时栈SPbl31_entrypoint—>el3_entrypoint_common---->plat_set_my_stack—>platform_set_stack—>platform_get_stack动态找到该cpufunc platform_set_stackmov x9, x30 // lrbl platform_get_stackmov sp, x0r_atf-tee
文章浏览阅读134次。300多个各种类型的PPT模板下载,为您提供各种类型PPT模板、PPT图片、PPT素材、海报模板、新媒体配图等内容下载。
文章浏览阅读546次。主要功能有:保安保洁管理、报修管理、房产信息管理、公告管理、管理员信息管理、业主信息管理、登录管理。_智能化哪些系统需要数据库
文章浏览阅读69次。本期年度书单,带大家盘点一下本年度图灵最受欢迎的套装图书,以前买套装书是为了凑单,如今套装书买回去不仅有一次性就能读完的酣畅感还极具收藏价值。一本好书往往要经过时间的验证,而阅读又是一种隐私,每个人的喜好大有不同,但能够集齐每个人的喜爱,这往往就是经典的诞生。今天这份书单里,有自成体系的套系书,还有一些因读者需求而产生的组成套系书。但不管哪种形式,它们都解决了读者在学习某些方面遇到的问题,也给大家...
文章浏览阅读809次。这个脚本的输入参数有三个:1.data/mfcc/train 2.exp/make_mfcc/train 3.mfcc/train1.data/mfcc/train中有数据预处理后的一些文件:phone.txt spk2utt text utt2spk wav.scp word.txt2.exp/make_mfcc/train中应该是要保存程序运行的日志文件的3.mfcc/train中是提取出的特征文件1是输入目录,2,3是输出目录#!/bin/bash# Copyright 2012-2_thch30
文章浏览阅读2.5k次。如图所示:在jsp中引入: Style.css 代码:.myOtherGridCell { font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; color:black; border-bottom:1px solid #a0a0a0;border-right:1px solid_listgrid添加样式