1.自定義WebView說明
1.1.這個WebView可以加載緩存的數據。(需要后端配合,將html轉換成一個字符串,主要是圖片要用特殊格式)
1.2.注入了圖片鏈接,為了方便點擊webView中的圖片而跳轉。
1.3.這是一個FrameLayout動態加載的WebView,布局中沒有任何聲明這個WebView。
2.源代碼及應用
2.1.源代碼如下,可以直接Copy。

public class MarkdownView extends WebView { private static final String TAG = MarkdownView.class.getSimpleName(); // 帶有點擊的圖片 => 為了防止被轉換,提前轉化為 html // [text  text](link) => <a href="link" ><img src="image_url" /></a> private static final String IMAGE_LINK_PATTERN = "\\[(.*)!\\[(.*)\\]\\((.*)\\)(.*)\\]\\((.*)\\)"; private static final String IMAGE_LINK_REPLACE = "<a href=\"$5\" >$1<img src=\"$3\" />$4</a>"; // 純圖片 => 添加點擊跳轉,方便后期攔截 //  => <img class="gcs-img-sign" src="image_url" /> private static final String IMAGE_PATTERN = "!\\[(.*)\\]\\((.*)\\)"; private static final String IMAGE_REPLACE = "<img class=\"gcs-img-sign\" src=\"$2\" />"; private String mPreviewText; public MarkdownView(Context context) { this(context, null); } public MarkdownView(Context context, AttributeSet attrs) { this(context, attrs, 0); } @SuppressLint({"AddJavascriptInterface", "SetJavaScriptEnabled"}) public MarkdownView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { return; } WebSettings settings = getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setDatabaseEnabled(true); initialize(); } private void initialize() { loadUrl("file:///android_asset/html/preview.html"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getSettings().setAllowUniversalAccessFromFileURLs(true); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } setWebChromeClient(new WebChromeClient() { @SuppressLint("JavascriptInterface") @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); if (newProgress == 100) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { loadUrl(mPreviewText); } else { evaluateJavascript(mPreviewText, null); } } } }); } public void loadMarkdownFromFile(File markdownFile) { String mdText = ""; try { FileInputStream fileInputStream = new FileInputStream(markdownFile); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String readText; StringBuilder stringBuilder = new StringBuilder(); while ((readText = bufferedReader.readLine()) != null) { stringBuilder.append(readText); stringBuilder.append("\n"); } fileInputStream.close(); mdText = stringBuilder.toString(); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException:" + e); } catch (IOException e) { Log.e(TAG, "IOException:" + e); } setMarkDownText(mdText); } public void loadMarkdownFromAssets(String assetsFilePath) { try { StringBuilder buf = new StringBuilder(); InputStream json = getContext().getAssets().open(assetsFilePath); BufferedReader in = new BufferedReader(new InputStreamReader(json, "UTF-8")); String str; while ((str = in.readLine()) != null) { buf.append(str).append("\n"); } in.close(); setMarkDownText(buf.toString()); } catch (IOException e) { e.printStackTrace(); } } public void setMarkDownText(String markdownText) { String injectMdText = injectImageLink(markdownText); String escMdText = escapeForText(injectMdText); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { mPreviewText = String.format("javascript:preview('%s')", escMdText); } else { mPreviewText = String.format("preview('%s')", escMdText); } initialize(); } /** * 注入圖片鏈接 */ private String injectImageLink(String mdText) { // TODO 修復代碼區md格式圖片被替換問題 mdText = mdText.replaceAll(IMAGE_LINK_PATTERN, IMAGE_LINK_REPLACE); mdText = mdText.replaceAll(IMAGE_PATTERN, IMAGE_REPLACE); return mdText; } private String escapeForText(String mdText) { String escText = mdText.replace("\n", "\\\\n"); escText = escText.replace("'", "\\\'"); escText = escText.replace("\r", ""); return escText; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInEditMode()) { canvas.drawColor(Color.WHITE); canvas.translate(canvas.getWidth() / 2, 30); Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(30); paint.setColor(Color.GRAY); canvas.drawText("MarkdownView", -30, 0, paint); } } }
2.2.然后如何動態加載WebView。
2.3.從緩存中獲取字符串
這個body就是一些緩存的數據了。還要進行轉換才能得到html。
3.局部分析
3.1.成員變量的定義
因為這里用到了將網頁內容緩存。
所以緩存后的數據,特別是緩存后的圖片就變成
這樣的東西了
[texttext](link)這樣的東西了
然后需要替換成原始的html。所以就用到了2個模板來替換。
private String mPreviewText; 就是緩存的html內容。
3.2.有三個構造函數
一個參數的構造函數
兩個參數的構造函數
三個參數的構造函數
這是最重要的構造函數。
setJavaScriptEnabled(true)==>支持js
setDomStorageEnabled(true)==>開啟DOM storage API功能
setDatabaseEnabled(true)==>開啟database storage API功能
3.3.初始化函數initialize()
首先加載本地文件(file://android_asset/html/preview.html)
為了防止webView加載一些鏈接出現白板現象
這里需要判斷一下如果SDK>=16,需要設置:
getSettings().setAllowUniversalAccessFromFileURLs(true);
為了防止加載https的URL時在5.0以上加載不了,5.0以下可以加載,SDK>=21,需要設置:
getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
然后設置一個新的WebChromeClient
當newProgress到百分之百了,需要判斷SDK<19
小於19的話,執行loadUrl(緩存text);
大於19的話,執行evaluateJavascript(緩存text,null);==>專門用於異步調用javascript方法,有一個回調。
3.4.加載一個緩存文件
作用就是:先從一個文件中讀取字符,存放到一個字符串中,然后再調用setMarkDownText(字符串);
3.5.加載一個緩存資源
作用:從應用的資源文件中獲取字符流,然后轉換成字符串。
3.6.將緩存數據替換成html標簽
首先是將圖片替換成正常的html
然后是將一些制表符、換行符替換成正常的html
最后再執行初始化函數。
3.7.注入圖片鏈接,將圖片翻譯成正常的html
3.8.將換行符,特殊字符,翻譯成正常的html
3.9.重寫WebView的onDraw函數
重畫WebView的界面。
這個函數估計沒什么用,我注釋掉以及修改代碼都沒有反應。
但是我將isInEditMode()刪除之后就有影響了。可能這個只有在編輯模式下才需要這樣設置的。