天天看點

Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:

#本文主要是講解部分原理,源碼及其使用請移步Github

https://github.com/RexSuper/RichEditor

APK:https://github.com/RexSuper/RichEditor/blob/master/RichHtmlEditorforAndroid/sample/release/sample-release.apk

Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:

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來寫這些呢,我想一定是你的上司在 鼓勵你走向 真·全棧之路 的 第一步吧。既然要我們做,我們就得硬着頭皮做,還要做好~拿人錢财,替人浪費時間不是?

簡單版圖例

Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:
Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:
Android富文本編輯器進階版(幹貨,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 

Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:

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" target="_blank" rel="external nofollow" >
    <link rel="stylesheet" type="text/css" href="style.css" target="_blank" rel="external nofollow" >
</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 >
<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

建立交流群

Android富文本編輯器進階版(幹貨,IOS可照搬邏輯)前言:結尾:

結尾:

這是一個不該由用戶端實作又可以實作的功能

繼續閱讀