富文本編輯器 - RichEditor

hkc5599 9年前發布 | 12K 次閱讀 Android開發 移動開發

來自: 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 接口。

參考

 本文由用戶 hkc5599 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!