欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android编程权威指南(第二版)学习笔记(二十八)—— 第28章 网页浏览

程序员文章站 2022-03-09 15:20:55
...

本章主要讲的是使用 WebView 在应用内浏览网页

GitHub 地址:
完成第28章,未完成挑战
完成第28章挑战

1. WebView

如果不使用 WebView,我们可以使用隐式 intent,也就是用独立的浏览器打开一个网页。但是我们通常只想在 activity 中显示网页内容而不是打开浏览器:或许是想显示自己生成的 HTML,或许是想以某种方式限制用户使用浏览器。对于大多数需要帮助文档的应用,普遍做法是以网页的形式提供帮助文档,这样会方便后期的更新与维护。打开浏览器查看帮助文档,既不专业,又妨碍应用行为的定制,同时也无法将网页整合进自己的用户界面。

1.1 WebView 的使用

和其他的 View 一样,在 fragment 的布局文件中声明之后,在 fragment 中获取实例,然后进行一番设置:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    <WebView
        android:id="@+id/fragment_photo_page_web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>
public class PhotoPageFragment extends VisibleFragment {
    private static final String ARG_URI = "photo_page_url";

    private Uri mUri;
    private WebView mWebView;

    ………………
    
    // 在这里防止 Lint 警告 JavaScript 被启用
    @SuppressLint("SetJavaScriptEnabled")
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_photo_page, container, false);

        // 首先获取实例
        mWebView = (WebView) v.findViewById(R.id.fragment_photo_page_web_view);
        // 然后将 JavaScript 设置为启用
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient() {
            // 这个方法决定 url 在哪里处理,返回 false 代表让 WebView 去加载
            // 其实该方法默认返回的就是 false
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                return false;
            }
        });
        mWebView.loadUrl(mUri.toString());

        return v;
    }
}

可以看到,WebClient 是一个关键,如果不重写该方法,WebView 可能不会加载这个 url

1.2 使用 WebChromeClient 优化 WebView 显示

WebViewClient 主要帮助 WebView 处理各种通知、请求事件,而 WebChromeClient 主要辅助 WebView 处理 Javascript 的对话框、网站图标、网站 title、加载进度等。

我们在视图中加入 ProgressBar 放在 WebView 上方并默认隐藏,由于 WebChromeClient 返回进度值在0~100之间,所以我们要设置进度条的最大值为100。

mProgressBar.setMax(100); // WebChromeClient 进度范围就是0~100

mWebView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (newProgress == 100) {
            mProgressBar.setVisibility(View.GONE);
        } else {
            mProgressBar.setVisibility(View.VISIBLE);
            mProgressBar.setProgress(newProgress);
        }
    }

    @Override
    public void onReceivedTitle(WebView view, String title) {
        AppCompatActivity activity = (AppCompatActivity) getActivity();
        设置网站标题
        activity.getSupportActionBar().setSubtitle(title);
    }
});

1.3 处理 WebView 的设备旋转问题

尝试旋转设备屏幕,可以发现 WebView 必须重新加载网页。这是因为 WebView 包含太多的数据,无法在 onSaveInstanceState(...)方法内全部保存。所以每次设备旋转,它都必须从头开始加载网页数据。

因为 WebView 是视图层级结构的一部分,所以旋转后它肯定会销毁并重建,不能用 retainFragment 来保留。对于一些类似的类(如 VideoView),Android 文档推荐让 activity 自己处理设备配置变更。也就是说,无需销毁重建 activity,就能直接调整自己的视图以适应新的屏幕尺寸。这样,WebView 也就不必重新加载全部数据了。

<activity android:name=".PhotoPageActivity"
    android:configChanges="keyboardHidden|orientation|screenSize"/>

android:configChanges 属性表明,如果因键盘开或关、屏幕方向改变、屏幕大小改变(也包括 Android 3.2之后的屏幕方向变化)而发生设备配置更改,那么 activity 应自己处理配置更改。

2. 挑战练习

2.1 使用后退键浏览历史页面

我们覆盖 Activity.onBackPressed 方法,就可以对返回键的行为进行控制,但是要获取 WebView 的实例,所以在 fragment 中加入了一个 getter。

// PhotoPageActivity.java
@Override
public void onBackPressed() {
    PhotoPageFragment fragment = (PhotoPageFragment) getSupportFragmentManager()
            .findFragmentById(R.id.fragment_container);
    if (fragment.getWebView().canGoBack()) {
        fragment.getWebView().goBack();
    } else {
        super.onBackPressed();
    }
}

2.2 非 HTTP 链接支持

只需要修改重写的 shouldOverrideUrlLoading 方法即可。

mWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("https://") || url.startsWith("http://")) {
            return false;
        } else {
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(intent);
            return true;
        }
    }
});

GitHub Page: kniost.github.io
简书:http://www.jianshu.com/u/723da691aa42