技术标签: 富文本编辑 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
这是一个不该由客户端实现又可以实现的功能
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland