Android富文本编辑器进阶版(干货,IOS可照搬逻辑)_android原生富文本编辑器-程序员宅基地

技术标签: 富文本编辑  iOS富文本编辑  android富文本编辑  

#本文主要是讲解部分原理,源码及其使用请移步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,则可以不用考虑后面的问题,难点在于如果在安卓端去调用一个个功能,然后获取其他状态,光标等问题
  1. 编辑器的框架搭建(以加粗为例,其他居中 撤销什么的 均是一个逻辑  事件监听 html自带)最小实现模型

asset里面放置一些静态网页和我们需要的初始化css 

editor.html

一个简单的带 contenteditable="true"的内容

<!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>

安卓通过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


<!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+ "');");
  }

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('<br></br>');");
    }

5.如何处理图片

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);
            }
        }
    }

 

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交互

addJavascriptInterface
removeJavascriptInterface
evaluateJavascript

一般是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.添加视频

//&nbsp; 让其可以继续进去编辑模式
RE.insertVideo = function(url,custom) {
    var html = '&nbsp;<video src="' + url + '" ' + custom +'></video>&nbsp;';
    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

新建交流群

 

结尾:

这是一个不该由客户端实现又可以实现的功能

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_28844947/article/details/91870015

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读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&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读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个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签