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,则可以不用考虑后面的问题,难点在于如果在安卓端去调用一个个功能,然后获取其他状态,光标等问题
  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

新建交流群

 

结尾:

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章