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

新建交流羣

 

結尾:

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

 

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