富文本編輯器 - RichEditor
來自: http://blog.csdn.net/feelang/article/details/50597001
基本功能
RichEditor 是一個繼承自 WebView 的自定義 view,枚舉類型 Type 定了它所支持的排版格式:
public enum Type {
BOLD,
ITALIC,
SUBSCRIPT,
SUPERSCRIPT,
STRIKETHROUGH,
UNDERLINE,
H1,
H2,
H3,
H4,
H5,
H6
}
首先在構造函數中加載一個 html 文件。
private static final String SETUP_HTML = "file:///android_asset/editor.html"public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setVerticalScrollBarEnabled(false); setHorizontalScrollBarEnabled(false); getSettings().setJavaScriptEnabled(true); setWebChromeClient(new WebChromeClient()); setWebViewClient(createWebviewClient());
// 加載 html 文件 loadUrl(SETUP_HTML);
applyAttributes(context, attrs); }</pre>
其中 html 文件 -
editor.html加載了兩個 css 文件 -normalize.css&style.css,一個 js 文件 -rich_editor.js。
body中插入了一個contentEditable="true"的div。<!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中定義了很多排版功能的 API,RichEditor類似 proxy,對外提供了 Java API。public void setEditorFontColor(int color) { String hex = convertHexColorString(color); exec("javascript:RE.setBaseTextColor('" + hex + "');"); }其中
exec用于執行 js 代碼。protected void exec(final String trigger) { if (isReady) { load(trigger); } else { postDelayed(new Runnable() { @Override public void run() { exec(trigger); }, 100); } } }只有當
rich_editor.html加載完成后isReady才會被置為true。protected class EditorWebViewClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { isReady = url.equalsIgnoreCase(SETUP_HTML); if (mLoadListener != null) { mLoadListener.onAfterInitialLoad(isReady); } } // other methods }構造函數在加載
editor.html之前已經設置了一個EditorWebViewClient的實例。public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) { // other statements setWebViewClient(createWebviewClient()); loadUrl(SETUP_HTML) // other statements }protected EditorWebViewClient createWebviewClient() { return new EditorWebViewClient(); }</pre>
當
editor.html加載完成,isReady被設為true之后,exec方法就可以調用load(trigger)執行 js 了。private void load(String trigger) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { evaluateJavascript(trigger, null); } else { loadUrl(trigger); } }
evaluateJavascript以及loadUrl都是Webview提供的方法。
evaluateJavascript是從 API 19 開始引入的,可以異步執行 js 代碼。Asynchronously evaluates JavaScript in the context of the currently displayed page.SCHEME
RichEditor 提供了兩個 Scheme 負責回調 java 代碼。
private static final String CALLBACK_SCHEME = "re-callback://"; private static final String STATE_SCHEME = "re-state://";然后在
EditorWebViewClient中重寫了shouldOverrideUrlLoading方法:protected class EditorWebViewClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { isReady = url.equalsIgnoreCase(SETUP_HTML); if (mLoadListener != null) { mLoadListener.onAfterInitialLoad(isReady); } }@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { String decode; try { decode = URLDecoder.decode(url, "UTF-8"); } catch (UnsupportedEncodingException e) { // No handling return false; }
if (TextUtils.indexOf(url, CALLBACK_SCHEME) == 0) { callback(decode); return true; } else if (TextUtils.indexOf(url, STATE_SCHEME) == 0) { stateCheck(decode); return true; } return super.shouldOverrideUrlLoading(view, url);} }</pre>
如果 url 符合 CALLBACK 的 scheme,就會執行
callback方法:private void callback(String text) { mContents = text.replaceFirst(CALLBACK_SCHEME, ""); if (mTextChangeListener != null) { mTextChangeListener.onTextChange(mContents); } }如果 url 符合 STATE 的 scheme,就會執行
stateCheck方法:private void stateCheck(String text) { String state = text.replaceFirst(STATE_SCHEME, "").toUpperCase(Locale.ENGLISH); List<Type> types = new ArrayList<>(); for (Type type : Type.values()) { if (TextUtils.indexOf(state, type.name()) != -1) { types.add(type); } }if (mDecorationStateListener != null) { mDecorationStateListener.onStateChangeListener(state, types); } }</pre>
通過以下兩個 set 方法可以設置回調。
public void setOnDecorationChangeListener(OnDecorationStateListener listener) { mDecorationStateListener = listener; }public void setOnInitialLoadListener(AfterInitialLoadListener listener) { mLoadListener = listener; }</pre>
總結
編輯器的核心功能由 js 實現,RichEditor 封裝了 js 的功能,為上層提供了 java 接口。
參考