腾讯X5WebView+JsBridge交互及WebView加载进度条效果实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangwo1991/article/details/80049942

最近在项目开发中有不少页面需要采用html的方式实现,自然而然就涉及到原生和js的交互问题,WebView也提供了addJavascriptInterface方法可以进行js的交互,实现也比较简单,由于需要交互的地方比较多,还是没有采用这种方式,使用了JsBridge第三方来实现,JsBridge用起来比较方便,可以主动给js发送消息,同时回调发送结果,也可以有js主动调用,同时回调调用结果等;

JsBridge地址:https://github.com/lzyzsd/JsBridge

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private final String CODING = "UTF-8"; // 编码格式
    private String loadUrl = "file:///android_asset/demo.html";
    private BridgeWebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (BridgeWebView) findViewById(R.id.web_view);
        //垂直滚动条不显示
//        webView.setVerticalScrollBarEnabled(false);
        IX5WebViewExtension x5WebViewExtension = mWebView.getX5WebViewExtension();
        if (x5WebViewExtension != null) {
            x5WebViewExtension.setScrollBarFadingEnabled(false);
        }
        //如果访问的页面中有Javascript,则webview必须设置支持Javascript
        WebSettings settings = mWebView.getSettings();
        //		settings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
//		settings.setUseWideViewPort(true);
        settings.setUseWideViewPort(true);
        //设置运行加载js
//        settings.setJavaScriptEnabled(true);
        // 允许js弹出窗口
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        //设置编码
        settings.setDefaultTextEncodingName(CODING);
        //设置支持DomStorage
        settings.setDomStorageEnabled(true);
        // 实现8倍缓存
        settings.setAppCacheMaxSize(Long.MAX_VALUE);
        settings.setAllowFileAccess(true);
        // 开启Application Cache功能
        settings.setAppCacheEnabled(true);
        //取得缓存路径
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
//        String chejusPath = getFilesDir().getAbsolutePath()+ APP_CACHE_DIRNAME;
        //设置路径
        //API 19 deprecated
        settings.setDatabasePath(appCachePath);
        // 设置Application caches缓存目录
        settings.setAppCachePath(appCachePath);
        //是否启用数据库
        settings.setDatabaseEnabled(true);
        //设置存储模式 建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //设置不支持字体缩放
        settings.setSupportZoom(false);
        //设置对应的cookie具体设置有子类重写该方法来实现
        setCookie(loadUrl);
        //还有一种是加载https的URL时在5.0以上加载不了,5.0以下可以加载,这种情况可能是网页中存在非https得资源,在5.0以上是默认关闭,需要设置,
//		loadWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            settings.setAllowUniversalAccessFromFileURLs(true);
        }
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//            WebView.setWebContentsDebuggingEnabled(true);
//        }
// 设置具体WebViewClient
        mWebView.setWebViewClient(new MyWebViewClient(mWebView));
        // set HadlerCallBack
        mWebView.setDefaultHandler(new myHadlerCallBack());
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);

            }
        });
        try {
            mWebView.loadUrl(loadUrl);
            mWebView.setBackgroundColor(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //js调用Android方法  如果页面上面有多个的话,可以注册多个方法
        //submitFromWeb 要和js那边定义的一样就可以了
        mWebView.registerHandler("submitFromWeb", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
                //如果js那边调用后又 进行回调的话可以在这里进行回调的
                function.onCallBack("submitFromWeb-----------------");
            }
        });

        UserInfo user = new UserInfo();
        user.name = "SDU";
        user.pwd = "123456";
        //Android发送消息给js,也可以注册多个
        //functionInJs和js定义的要一致
        mWebView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
                //这里也是可以进行js回传的
            }
        });

        mWebView.send("hello");
    }

    /**
     * 自定义的WebViewClient
     */
    class MyWebViewClient extends BridgeWebViewClient {

        public MyWebViewClient(BridgeWebView webView) {
            super(webView);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);

        }

        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);

        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);

        }
    }


    /**
     * 自定义回调
     */
    class myHadlerCallBack extends DefaultHandler {

        @Override
        public void handler(String data, CallBackFunction function) {

        }
    }

    /**
     * 设置对应的cookie具体设置有子类重写该方法来实现
     * "age=20;sex=1;time=today"
     */
    protected void setCookie(String loadUrl) {
        UserInfo info = new UserInfo();
        info.name = "1111";
        info.pwd = "123456789";
        Gson gs = new Gson();
        String toJson = gs.toJson(info);
        synCookies(loadUrl, "user=" + toJson);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ViewParent parent = mWebView.getParent();
        if (parent != null) {
            ((ViewGroup) parent).removeView(mWebView);
        }
        mWebView.stopLoading();
        mWebView.getSettings().setJavaScriptEnabled(false);
        mWebView.clearHistory();
        mWebView.clearView();
        mWebView.removeAllViews();
        mWebView.destroy();
        mWebView = null;

    }

    /**
     * 设置Cookie
     *
     * @param url
     * @param cookie 格式:uid=21233 如需设置多个,需要多次调用
     *               synCookies(this, url, "age=20;sex=1;time=today");
     */
    protected void synCookies(String url, String cookie) {
//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//            CookieSyncManager.createInstance(this);
//        }
        CookieSyncManager.createInstance(this);
        CookieSyncManager.getInstance().sync();
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        cookieManager.setCookie(url, cookie);//cookies是在HttpClient中获得的cookie
//        CookieSyncManager.getInstance().sync();
    }
}

通过调用registerHandler注册处理程序,以便javascript调用它,在BridgeHandler回调中,可以通过function.onCallBack()进行回调的处理,就是说js主动调用原生,原生又可以立马回调js ;

/**
	 * register handler,so that javascript can call it
	 * 注册处理程序,以便javascript调用它
	 * @param handlerName handlerName
	 * @param handler BridgeHandler
	 */
	public void registerHandler(String handlerName, BridgeHandler handler) {
		if (handler != null) {
            // 添加至 Map<String, BridgeHandler>
			messageHandlers.put(handlerName, handler);
		}
	}

需要注意handlerName要和js那边保持一致,其实是保存在一个map集合中;

Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();

所有建议当页面销毁的是时候调用unregisterHandler将注册处理程序注销掉,其实就是从messageHandlers集合中移除;

/**
	 * unregister handler
	 * 
	 * @param handlerName
	 */
	public void unregisterHandler(String handlerName) {
		if (handlerName != null) {
			messageHandlers.remove(handlerName);
		}
	}

调用callHandler方法可以主动给js发送消息,在CallBackFunction回调中可以处理js的回调;

/**
	 * call javascript registered handler
	 * 调用javascript处理程序注册
     * @param handlerName handlerName
	 * @param data data
	 * @param callBack CallBackFunction
	 */
	public void callHandler(String handlerName, String data, CallBackFunction callBack) {
        doSend(handlerName, data, callBack);
	}

可以多次调用registerHandler和callHandler方法,不过在使用的时候要注意不要去重写shouldOverrideUrlLoading方法,如果重写shouldOverrideUrlLoading不能进行js的交互;原生和js的交互是搞定了,但是发现使用系统的WebView加载效果不理想,加载慢,卡顿等;出现问题就需要去解决,就建议使用腾讯的X5WebView,但是X5WebView并没有对js交互做很好的处理,还是使用的addJavascriptInterface来实现,如果不能处理好,将X5WebView集成进来,之前实现的js交互就白写了,最后考虑,不采用远程依赖的方式引入JsBridge,将JsBridge源码拷贝到项目中,让JsBridge中的BridgeWebView extends X5WebView中的WebView,这样子问题就解决了;

腾讯X5WebView地址:http://x5.tencent.com/tbs/guide/sdkInit.html

替换成腾讯X5WebView加载速度和效果好了很多,接下来是实现进度加载效果;这个效果的实现网上有些是采用这种方式实现的;

public class ProgressWebView extends WebView {
    private ProgressBar mProgressBar;

    public ProgressWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mProgressBar = new ProgressBar(context, null,
                android.R.attr.progressBarStyleHorizontal);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, 8);
        mProgressBar.setLayoutParams(layoutParams);

        Drawable drawable = context.getResources().getDrawable(
                R.drawable.web_progress_bar_states);
        mProgressBar.setProgressDrawable(drawable);
        addView(mProgressBar);
        setWebChromeClient(new WebChromeClient());
    }

    public class WebChromeClient extends android.webkit.WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                mProgressBar.setVisibility(GONE);
            } else {
                if (mProgressBar.getVisibility() == GONE)
                    mProgressBar.setVisibility(VISIBLE);
                mProgressBar.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        LayoutParams lp = (LayoutParams) mProgressBar.getLayoutParams();
        lp.x = l;
        lp.y = t;
        mProgressBar.setLayoutParams(lp);
        super.onScrollChanged(l, t, oldl, oldt);
    }
}

自定义WebView,然后实例化一个ProgressBar,设置ProgressBar的样式并将其添加到自定义的WebView中,这样导致WebView和ProgressBar成为一个整体,会发现当WebView滑动的时候ProgressBar也会跟着滑动,这样的效果肯定是不行的,需要的效果是不管WebView怎么滑动,ProgressBar的位置是不变的,WebView和ProgressBar是彼此独立的,ProgressBar位于WebView上面;可以采用下面这种在xml布局中处理;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:background="#0ca5f5">

    </LinearLayout>
    <ProgressBar
        android:id="@+id/load_progress"
        android:layout_width="match_parent"
        android:layout_height="3dp"
        style="?android:attr/progressBarStyleHorizontal"
        android:progressDrawable="@drawable/progress_bar"
        android:visibility="gone"/>
    <com.isales.webtest.jsbridge.BridgeWebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

然后在setWebChromeClient()中onProgressChanged回调中进行处理,设置setProgress已达到进度的改变;

mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                //加载进度改变的时候回调
                if (newProgress == 100) {
                    //隐藏掉进度条
                    mProgress.setVisibility(View.GONE);
                } else {
                    //显示进度条并加载进度
                    mProgress.setVisibility(View.VISIBLE);
                    mProgress.setProgress(newProgress);
                }
            }
        });

这样子不管WebView怎么滑动,ProgressBar的位置是不变的,效果是实现了,如果只有一个地方使用还好些,使用的地方多了,该实现方式还是比较麻烦的,可以使用下面的实现方式;

/**
 * 
 * webview加载进度效果
 */

public class LoadWebView extends LinearLayout {
    private ProgressBar mProgress;
    private BridgeWebView mWebView;
    private LoadInterface loadInfterface;

    public LoadWebView(Context context) {
        this(context, null);
    }

    public LoadWebView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    /**
     * 初始化
     *
     * @param context
     */
    private void init(Context context) {
        setOrientation(VERTICAL);

        initProgressBar(context);
        initWebView(context);
    }

    /**
     * 初始化进度条
     *
     * @param context
     */
    private void initProgressBar(Context context) {
        mProgress = (ProgressBar) LayoutInflater.from(context).inflate(R.layout.progress_horizontal, null);
        //设置最大进度
        mProgress.setMax(100);
        //设置当前进度
        mProgress.setProgress(0);
        //将progressbar添加到布局中
        addView(mProgress, LayoutParams.MATCH_PARENT, dip2px(3));
    }

    /**
     * 初始化WebView
     *
     * @param context
     */
    private void initWebView(Context context) {
        mWebView = new BridgeWebView(context);
        //设置滚动条不可用
        IX5WebViewExtension x5WebViewExtension = mWebView.getX5WebViewExtension();
        if (x5WebViewExtension != null) {
            x5WebViewExtension.setScrollBarFadingEnabled(false);
        }
        WebSettings settings = mWebView.getSettings();
        settings.setUseWideViewPort(true);
        // 允许js弹出窗口
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        //设置编码
        settings.setDefaultTextEncodingName("UTF-8");
        //设置支持DomStorage
        settings.setDomStorageEnabled(true);
        // 实现8倍缓存
        settings.setAppCacheMaxSize(Long.MAX_VALUE);
        settings.setAllowFileAccess(true);
        // 开启Application Cache功能
        settings.setAppCacheEnabled(true);
        //取得缓存路径
        String appCachePath = context.getCacheDir().getAbsolutePath();
//        String chejusPath = getFilesDir().getAbsolutePath()+ APP_CACHE_DIRNAME;
        //设置路径
        //API 19 deprecated
        settings.setDatabasePath(appCachePath);
        // 设置Application caches缓存目录
        settings.setAppCachePath(appCachePath);
        //是否启用数据库
        settings.setDatabaseEnabled(true);
        //设置存储模式 建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //设置不支持字体缩放
        settings.setSupportZoom(false);
        //设置对应的cookie具体设置有子类重写该方法来实现
        //还有一种是加载https的URL时在5.0以上加载不了,5.0以下可以加载,这种情况可能是网页中存在非https得资源,在5.0以上是默认关闭,需要设置,
//		loadWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            settings.setAllowUniversalAccessFromFileURLs(true);
        }
        //将webview添加到布局中
        LayoutParams lps = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
        addView(mWebView, lps);

        mWebView.setWebViewClient(new MyWebViewClient(mWebView));
        // set HadlerCallBack
        mWebView.setDefaultHandler(new myHadlerCallBack());
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                //加载进度改变的时候回调
                if (newProgress == 100) {
                    //隐藏掉进度条
                    mProgress.setVisibility(View.GONE);
                } else {
                    //显示进度条并加载进度
                    mProgress.setVisibility(View.VISIBLE);
                    mProgress.setProgress(newProgress);
                }
                //进行加载回调
                if (loadInfterface != null) {
                    loadInfterface.onProgressChanged(view, newProgress);
                }
            }
        });

    }

    public void goForward() {
        if (canGoForward()) {
            mWebView.goForward();
        }
    }

    public boolean canGoForward() {
        return mWebView != null && mWebView.canGoForward();
    }

    /**
     * 判断是否可以返回
     *
     * @return
     */
    public boolean canGoBack() {
        return mWebView != null && mWebView.canGoBack();
    }

    /**
     * 执行返回的动作
     */
    public void goBack() {
        if (canGoBack()) {
            mWebView.goBack();
        }
    }

    /**
     * 加载url链接
     *
     * @param url
     */
    public void loadUrl(String url) {
        if (mWebView != null) {
            //加载url链接
            try {
                mWebView.loadUrl(url);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取当前的ProgressBar
     *
     * @return
     */
    public ProgressBar getProgressBar() {
        return mProgress;
    }

    /**
     * 获取当前的WebView
     *
     * @return
     */
    public BridgeWebView getWebView() {
        return mWebView;
    }

    /**
     * 设置监听回调
     *
     * @param listener
     */
    public void addOnWebViewLoadingListener(LoadInterface listener) {
        this.loadInfterface = listener;
    }

    /**
     * 自定义的WebViewClient
     */
    class MyWebViewClient extends BridgeWebViewClient {

        public MyWebViewClient(BridgeWebView webView) {
            super(webView);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            //加载完成回调
            if (loadInfterface != null) {
                loadInfterface.onPageFinished(view, url);
            }
        }

        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);

        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);

        }
    }

    /**
     * 自定义回调
     */
    class myHadlerCallBack extends DefaultHandler {

        @Override
        public void handler(String data, CallBackFunction function) {

        }
    }

    private int dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
    }
}

自定义一个布局容器,首先实例化一个ProgressBar,设置ProgressBar样式、大小等,将其添加到布局容器中,再去实例化一个WebView,同样添加到布局容器中,提供相应的方法供外部调用,在使用的时候直接引用该自定义容器即可;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:background="#0ca5f5">

    </LinearLayout>
    <com.isales.webtest.widget.LoadWebView
        android:id="@+id/load_webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
/**
 * webview加载进度效果
 */

public class EffectSecondActivity extends AppCompatActivity {
    private String loadUrl = "file:///android_asset/demo.html";
    private BridgeWebView webView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_effect_second);
        LoadWebView loadWebview = (LoadWebView) findViewById(R.id.load_webview);
        webView = loadWebview.getWebView();
        loadWebview.loadUrl(loadUrl);
        //设置加载监听
        loadWebview.addOnWebViewLoadingListener(new LoadInterface() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress == 100) {
                    //加载完成后进行js交互
                    //js调用Android方法  如果页面上面有多个的话,可以注册多个方法
                    //submitFromWeb 要和js那边定义的一样就可以了
                    webView.registerHandler("submitFromWeb", new BridgeHandler() {
                        @Override
                        public void handler(String data, CallBackFunction function) {
                            Toast.makeText(EffectSecondActivity.this, data, Toast.LENGTH_LONG).show();
                            //如果js那边调用后又 进行回调的话可以在这里进行回调的
                            function.onCallBack("submitFromWeb-----------------");
                        }
                    });

                    UserInfo user = new UserInfo();
                    user.name = "SDU";
                    user.pwd = "123456";
                    //Android发送消息给js,也可以注册多个
                    //functionInJs和js定义的要一致
                    webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
                        @Override
                        public void onCallBack(String data) {
                            //这里也是可以进行js回传的
                        }
                    });

                    webView.send("hello");
                }
            }

            @Override
            public void onPageFinished(WebView view, String url) {

            }
        });
    }
}

这样子使用起来就方便多了;效果如下:


源码地址:https://pan.baidu.com/s/1XIVHFxpTT7tnjGgtKjKC-g

猜你喜欢

转载自blog.csdn.net/wangwo1991/article/details/80049942