学习感受

WebView 网页视图在 Android APP 中也是用的非常多,比如微信、QQ、淘宝、bilibili 等等这些非常受欢迎的应用都有使用到这个组件。

同时,WebView 是可以调用里面的 Javascript 代码。JavaScript 代码也能够调用的 Android 中相关的代码,这就相当于是混合开发。混合开发的案例也非常多,比如我们的常用的淘宝,它就是使用了混合开发的方式了。

本日志仅仅是记录了 WebView 的一些基本的使用,深入学习这种混合的开发方式,这里不过多赘述。

实例

创建 WebView

创建 WebView 非常简单,只需创建组件即可,同时需要设置id

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

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

加载网页

加载网页分别有两种不同的加载方式,一个是加载本地的网页,另一个是加载远程的网页。

本地加载

在加载本地网页之前,需要创建一个 asset 的文件夹。因为只有 asset 文件夹下的文件不会被编译进去,比如我们之前在 res/drawable 文件夹中放入的这些图片都是会进行一个编译。调用图片的方式,也不是用路径来进行一个编译,而是使用这种 @drawablw/xxxx 这种方式来获取图片的。

正确的创建的 asset 文件夹的步骤如下。

  1. 新建

  2. 直接 Finish

创建完成之后,可以随便写一个 html 代码,放到这个 asset 文件夹下。这里用的是我之前写的一个网页进行测试

加载网页的方法用的是 onLoad 方法,固定的部分是 file:///android_asset/ ,后面的部分就可以指定的网页的。

package top.bestguo.androidlayout;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;

public class WebViewActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);

        progressBar = (ProgressBar) findViewById(R.id.progress);

        webView = (WebView) findViewById(R.id.web_view);
        // 允许 javascript
        webView.getSettings().setJavaScriptEnabled(true);
        // 加载本地的 html 文件
        webView.loadUrl("file:///android_asset/index.html");
    }

运行效果如下:

远程加载

远程加载只需要一个 http 或者 https 协议的地址就可以了。不过,在加载远程的一个网址时,需要允许 javascript 网页才能够正常的运行成功。不允许将加载不出来。

启用 javascript 的方法为 setJavaScriptEnabled ,设置为 true 允许,false 为禁止。

这里以我的个人网站为例子

package top.bestguo.androidlayout;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;

public class WebViewActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);

        progressBar = (ProgressBar) findViewById(R.id.progress);

        webView = (WebView) findViewById(R.id.web_view);
        // 允许 javascript
        webView.getSettings().setJavaScriptEnabled(true);
        // 加载本地的 html 文件
        // webView.loadUrl("file:///android_asset/index.html");
        webView.loadUrl("https://www.bestguo.top");

    }

运行效果如下

进入新页面

我们在浏览网页的时候,需要点击超链接来进入新的页面,但是我们在点击时。出现了以下的效果

就是需要我们选择一个外置的浏览器来访问新的页面,这样的体验其实并不好。

因此,解决这种方法需要用到 setWebViewClient 这个方法,它的参数是 WebViewClient 以及被它所继承的子类,重写其中的方法。这个方法就是 shouldOverrideUrlLoading

实现的代码如下

package top.bestguo.androidlayout;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class WebViewActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);

        progressBar = (ProgressBar) findViewById(R.id.progress);

        webView = (WebView) findViewById(R.id.web_view);
        // 允许 javascript
        webView.getSettings().setJavaScriptEnabled(true);
        // 加载本地的 html 文件
        // webView.loadUrl("file:///android_asset/index.html");
        webView.loadUrl("https://www.bestguo.top");
        webView.setWebViewClient(new MyWebViewClient());

    }

    private class MyWebViewClient extends WebViewClient {

        // 如果要设置成不用新浏览器打开
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            view.loadUrl(request.getUrl().toString());
            return true;
        }
    }
}

建立了一个内部类,类名为 MyWebViewClient,需要继承自 WebViewClient 类。重写其 shouldOverrideUrlLoading 方法,将当前的 webview 中的 url 进行一个重新的加载,这样一来就不会直接提示使用外置浏览器了。

调用 setWebViewClient 方法是,传入的是自己创建的内部类对象就行了 webView.setWebViewClient(new MyWebViewClient());

效果如下

返回

但是,我们进入到这个页面再返回,又出现了一个问题,请看下图。

很容易发现,它并没有后退到上一个页面,而是直接退出了。所以这个时候,我们需要对按键进行一个判断。判断方法也非常简单,就是按下的是否为返回键并且能够后退到上一个页面。条件成立则执行返回到上一个页面,条件不成立则直接退出。

同时网页的前进与后退,也有 6 种方法可以选择

  • webview.canGoBack() 能否后退到上一个页面
  • webview.goBack() 后退到上一个页面
  • webview.canGoForward() 前进到下一个页面
  • webview.goForward() 前进到下一个页面
  • webview.canGoBackOrForward(int steps) 能否后退或者前景到第几个页面,后退为负值,前进为正值
  • webview.GoBackOrForward(int steps) 后退或者前景到第几个页面,后退为负值,前进为正值

在 WebViewActivity 中重写 onKeyDown 方法,重新判端,按下返回键的操作。

/**
  * 设置返回值,防止直接返回
  * @param keyCode
  * @param event
  * @return
  */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // 如果按下了返回键,并且能够返回到上一级目录
    if(keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
        // 可以返回到上一级的目录
        webView.goBack();
        return true;
    }
    // 退出
    return super.onKeyDown(keyCode, event);
}

运行效果如下

其它高级设置

以上问题解决了,我们可以获取进度条来显示网页加载的情况,以及设置标题,将原来的 AndroidLayout 标题进行一个替换。

要做到这种效果,还是要写一个类,类名为 MyWebChromeClient ,该类需要继承自 WebChromeClient,重写它的两个方法就能够实现以上的功能了。

重写的两个方法,分别如下:

  • onProgressChanged 加载进度,创建一个 ProgressBar 这个组件就可以去实现
  • onReceivedTitle 得到的标题,直接使用父类的 setTitle 方法,对标题进行设置

在此之前,先添加一个进度条的组件

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

    <!-- 进度条 -->
    <ProgressBar
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:max="100"/>
    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

对应的Java代码

package top.bestguo.androidlayout;

import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

public class WebViewActivity extends AppCompatActivity {

    private WebView webView;
    // 声明了一个进度条组件
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);

        progressBar = (ProgressBar) findViewById(R.id.progress);

        webView = (WebView) findViewById(R.id.web_view);
        // 允许 javascript
        webView.getSettings().setJavaScriptEnabled(true);
        // 加载本地的 html 文件
        // webView.loadUrl("file:///android_asset/index.html");
        webView.loadUrl("https://www.bestguo.top");
        webView.setWebViewClient(new MyWebViewClient());
        webView.setWebChromeClient(new MyWebChromeClient());

    }

    ......

    // 新建了一个内部类,继承自 webchromeclient 类
    private class MyWebChromeClient extends WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            // 设置进度的状态
            progressBar.setProgress(newProgress);
            Log.d("loading", newProgress + "");
        }

        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
            setTitle(title);
        }
    }

    ......
}

运行效果如下

后记

项目的代码都在 AndroidStudy 的 github 仓库中。

KeyEvent 对应的事件,请参考 https://www.cnblogs.com/hujingnb/p/10282238.html