Android 自定義WebView 實現可以加載緩存數據


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](image_url) 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>";
    // 純圖片 => 添加點擊跳轉,方便后期攔截
    // ![text](image_url) => <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);
        }
    }
}
View Code

 

2.2.然后如何動態加載WebView。

  

 

2.3.從緩存中獲取字符串

  

  這個body就是一些緩存的數據了。還要進行轉換才能得到html。


3.局部分析

3.1.成員變量的定義

  

  因為這里用到了將網頁內容緩存。

  所以緩存后的數據,特別是緩存后的圖片就變成

  ![text](image_url)這樣的東西了

  [text![text](image_url)text](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()刪除之后就有影響了。可能這個只有在編輯模式下才需要這樣設置的。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM