Android: How can I know CSS injection is complete?

后端 未结 1 979
太阳男子
太阳男子 2021-01-24 10:22

I am trying to show my webview only after I inject the CSS file to the HTML. I have tried to put it on onPageCommitVisible function, but it works only 23 apis andd above. Someon

相关标签:
1条回答
  • 2021-01-24 11:08

    There are few places where you can handle this.

    • You could use evaluateJavaScript instead of loadUrl (API level 19) and pass callback in which you will set webview visible.
    • You could register your own javascript interface using addJavaScriptInterface and call it on the end of your script
    • You could set WebChromeClient and override onJsAlert then in your script raise alert with specific message.
    • UPDATE: Additionally this could be achieved by intercepting one of 'css' request, and append loaded file with needed content. This will allow you to inject your styles right before onPageFinished. Check this this thread.

    All approaches I have combined in following example:

        package com.j2ko.webviewapp;
    
        import android.support.v7.app.AppCompatActivity;
        import android.os.Bundle;
        import android.util.Base64;
        import android.util.Log;
        import android.view.View;
        import android.webkit.JavascriptInterface;
        import android.webkit.JsResult;
        import android.webkit.ValueCallback;
        import android.webkit.WebChromeClient;
        import android.webkit.WebView;
        import android.webkit.WebViewClient;
    
        import java.io.InputStream;
        import java.io.StringReader;
        import java.io.StringWriter;
    
        public class MainActivity extends AppCompatActivity {
            private static final String MAIN_FUNC_FMT = "(function() { %s })()";
            private static final String FUNC_BODY_FMT =
                    "var parent = document.loadedgetElementsByTagName('head').item(0);" +
                            "var css = document.createElement('style');" +
                            "css.type = 'text/css';" +
                            "css.innerHTML = %s;" +
                            "parent.appendChild(css);";
    
            private static final String BASE64_DECODE_FMT = "window.atob('%s')";
    
            WebView mWebView;
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
                mWebView = (WebView) findViewById(R.id.webview);
                mWebView.getSettings().setJavaScriptEnabled(true);
                mWebView.setWebViewClient(new WebViewClient() {
                    @Override
                    public void onPageFinished(WebView view, String url) {
                        super.onPageFinished(view, url);
                        //Change it to whatever
                        injectWithEvaluateAndInterface(view);
                    }
    
                    @Override
                    public void onLoadResource(WebView view, String url) {
                        super.onLoadResource(view, url);
                    }
                });
                mWebView.setVisibility(View.INVISIBLE);
                mWebView.loadUrl("http://wiki.org");
            }
    
    
            private static class CSSInjectBuilder {
                private final String mOrigin;
                private String mAtEnd = null;
                private boolean mUseBase64 = false;
    
                public CSSInjectBuilder(String css) {
                    mOrigin = css;
                }
    
                public CSSInjectBuilder withBase64() {
                    mUseBase64 = true;
                    return this;
                }
    
                public CSSInjectBuilder withExpressionAtEnd(String expression){
                    mAtEnd = expression;
                    return this;
                }
    
                String build() {
                    String func_body = FUNC_BODY_FMT;
    
                    if (mAtEnd != null) {
                        func_body += mAtEnd;
                    }
    
                    final String css;
                    if (mUseBase64) {
                        byte[] buffer = mOrigin.getBytes();
                        css = String.format(BASE64_DECODE_FMT, Base64.encodeToString(buffer, Base64.NO_WRAP));
                    } else {
                        css = "'" + mOrigin + "'";
                    }
    
                    func_body = String.format(func_body, css);
    
                    return String.format(MAIN_FUNC_FMT, func_body);
                }
            }
    
            byte[] loadAsset() {
                try {
                    InputStream inputStream = getAssets().open("style.css");
                    byte[] buffer = new byte[inputStream.available()];
                    inputStream.read(buffer);
                    inputStream.close();
                    return buffer;
                } catch (Exception e) {
                }
    
                return null;
            }
    
            String loadCSS() {
                return new String(loadAsset());
            }
    
            void injectWithEvaluate(final WebView view) {
                view.evaluateJavascript(new CSSInjectBuilder(loadCSS()).withBase64().build(), new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        view.setVisibility(View.VISIBLE);
                    }
                });
            }
    
            void injectWithEvaluateAndInterface(WebView view) {
                view.addJavascriptInterface(new WebViewInterface(), "WebViewBackEnd");
                final String injector = new CSSInjectBuilder(loadCSS())
                        .withBase64()
                        .withExpressionAtEnd("window.WebViewBackEnd.CSSInjectionComplete();")
                        .build();
    
                view.evaluateJavascript(injector, null);
            }
    
            void injectWithLoadUrlSimple(WebView view) {
                view.loadUrl("javascript:" + loadCSS());
                view.setVisibility(View.VISIBLE);
            }
    
            void injectWithLoadUrlAndCheckAlert(final WebView view) {
                view.setWebChromeClient(new WebChromeClient() {
                    @Override
                    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                        if (message.equals("CSSInjectionComplete")) {
                            view.setVisibility(View.VISIBLE);
                            return true;
                        }
                        return super.onJsAlert(view, url, message, result);
                    }
                });
    
                //alert could hang aplying scripts so put it on timeout
                final String injector = new CSSInjectBuilder(loadCSS())
                        .withBase64()
                        .withExpressionAtEnd("setTimeout(function(){alert('CSSInjectionComplete');}, 1);")
                        .build();
    
                view.loadUrl("javascript:"  + injector);
            }
    
            private class WebViewInterface {
    
                @JavascriptInterface
                public void CSSInjectionComplete(){
    
                    mWebView.post(new Runnable() {
                        @Override
                        public void run() {
                            mWebView.setVisibility(View.VISIBLE);
                        }
                    });
                }
            }
    
        }
    
    0 讨论(0)
提交回复
热议问题