专栏首页androidBlogWebView的使用及实战

WebView的使用及实战

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

这篇博客主要讲解一下问题 - webView 的 基本使用 - webView怎样配置缓存 - webView请求错误时候的处理 - webView cookie的同步与清除 - webView 下载文件的两种方法 - webView的 一些扩展使用

思路图如下

大概的效果图如下

webView的基本使用

大概可以分为以下步骤 - 配置权限 - 创建webView - 配置webView(是否支持js,是否由系统浏览器打开) - 加载数据

第一步,别忘了 在AndroidMainfest中 添加权限

<uses-permission android:name="android.permission.INTERNET"/>

第二步,创建webView

在xml文件中

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout android:layout_width="match_parent"
                    android:layout_height="match_parent"
    >

        <RelativeLayout
            android:id="@+id/rl_head"
            android:layout_width="match_parent"
            android:layout_height="?android:actionBarSize"
            android:background="@color/colorAccent">


            <ImageView
                android:id="@+id/iv_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:padding="10dp"
                android:src="@drawable/sel_back"/>

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:ellipsize="end"
                android:maxLength="200"
                android:maxLines="1"
                android:text="标题"/>
        </RelativeLayout>

        <ProgressBar

            android:id="@+id/progressBar"
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rl_head"/>

        <WebView android:id="@+id/webView"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_below="@id/progressBar"></WebView>

    </RelativeLayout>


</layout>
mWebView = bind.webView;

需要注意的是 这里我采用的是 databinding库,这个库可以很方便 地将我们减少findViewById 的代码,同时可以将数据与我们的View 绑定在一起,是Android支持MVVN的一个库,不了解这个库用法的用法的 请自行搜索,这里基本不涉及到这个库的 用法。

第三步,配置webView

如果想在当前客户端打开浏览器的话,可以给webView 设置 setWebViewClient,并重写 shouldOverrideUrlLoading这个方法

WebSettings settings = mWebView.getSettings();
// 设置是够支持js脚本
settings.setJavaScriptEnabled(true);


mWebView.setWebViewClient(new WebViewClient() {
    //重写这个方法 返回true,在当前 webView 打开,否则在浏览器中打开
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        view.loadUrl(mUrl);
        return true;
    }

});

如果想显示加载进度的话,可以给webView 设置setWebChromeClient,并重写onProgressChanged()方法,至于onReceivedTitle()这个方法是拿到网页加载中的 标题

    mWebView.setWebChromeClient(new WebChromeClient() {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            //                LUtils.i("newProgress=" + newProgress);
            if (newProgress != 100) {
                mProgressBar.setProgress(newProgress);
            } else {
                mProgressBar.setVisibility(View.GONE);
            }

        }

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

}

想大多数APP的做法一样,如微信,按下返回键,只是想后退,并不是想销毁Activity,我们可以这样做,重写 Activity的 onKeyDown()方法 ,并监听按下的键,采取 相应的 操作。

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
        mWebView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

既然有后退操作,当然也有前进操作

//是够能够前进
mWebView.canGoForward();
//前进
mWebView.goForward();

第四步

调用该方法开始加载网页

mWebView.loadUrl(mUrl);

到此,webView的基本用法讲解到此。


webView 配置缓存

配置 缓存模式

缓存模式主要有一下几种:

  • LOAD_CACHE_ELSE_NETWORK Use cached resources when they are available, even if they have expired.(如果本地有缓存,优先使用 本地 缓存,即使已经过期了 )
  • LOAD_CACHE_ONLY Don’t use the network, load from the cache.(只使用本地 缓存)
  • LOAD_DEFAULT Default cache usage mode.(默认的缓存 模式)
  • LOAD_NORMAL This constant was deprecated in API level 17. This value is obsolete, as from API level HONEYCOMB and onwards it has the same effect as LOAD_DEFAULT.
  • LOAD_NO_CACHE Don’t use the cache, load from the network.(不使用 本地缓存 )

我们可以通过以下方法设置缓存模式

WebSettings settings = mWebView.getSettings();
settings.setCacheMode(WebSettings.LOAD_DEFAULT);

webView请求错误时候的处理

因为系统自带的 错误页面太丑了,所以我们经常会对其 进行处理,目前本人了解到的主要有两种方法 - 加载本地的控件,显示 错误信息 - 加载自己 定义的 html页面

加载本地的控件

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    if (errorCode == 404) {
        //用javascript隐藏系统定义的404页面信息
        //String data = "Page NO FOUND!";
       // view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
        mWebView.setVisibility(View.INVISIBLE);
        mErrorView.setVisibility(View.VISIBLE);

    }
}

加载 定义的 html页面

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    if (errorCode == 404) {
        //用javascript隐藏系统定义的404页面信息
        String data = "Page NO FOUND!";
        view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
    }
}

当然实际开发中为了给用户比较还要的体验,会做非常多的处理,包括有网络情况和没有网络情况的处理,对于没有网络情况的处理,这里我们跳转到打开WiFi界面,详情可以参照我的 上一篇博客android 监听网络状态的变化及实战,而对于有网络情况的处理,这里我们只处理404错误,其他错误请根据项目的需求自行处理。

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
        failingUrl) {
    super.onReceivedError(view, errorCode, description, failingUrl);
    //  没有网络连接
    if (false == APP.getInstance().isConnected()) {
        APP.getInstance().showWifiDlg(NewsDetailActivity.this);
    } else {
        if (errorCode == 404) {
            //用javascript隐藏系统定义的404页面信息
            String data = "Page NO FOUND!";
            view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
            mWebView.setVisibility(View.INVISIBLE);
        } else {//其他状态码错误的处理,这里就不罗列出来了

        }
    }

}

webView cookie的同步与清除

关于这个问题,我们主要分为两步, - 怎样获取cookie - 怎样将cookie与webView进行 同步

对于怎样获取 cookie,主要有以下方法

下面只给出核心代码

第一,使用DefaultHttpClient

DefaultHttpClient client = new DefaultHttpClient();
CookieStore store = client.getCookieStore();
List<Cookie> list = store.getCookies();

第二,使用HttpURLConnection

URLConnection con= (HttpURLConnection) url.openConnection(); 
// 取得sessionid. 
String cookieval = con.getHeaderField("set-cookie"); 
String sessionid; 
if(cookieval != null) { 
    sessionid = cookieval.substring(0, cookieval.indexOf(";")); 
}

发送设置cookie:

URL url = new URL(requrl);
HttpURLConnectioncon= (HttpURLConnection) url.openConnection(); 
if(sessionid != null) { 
   con.setRequestProperty("cookie", sessionid); 
}

第三,使用Retrofit

Call<ResponseBody> call = tnGouAPi.getTest(test);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        ResponseBody body = response.body();
        Headers headers = response.headers();
        Set<String> names = headers.names();
        for(String  key:names){
            String value = headers.get(key);
        }


        try {
            Logger.i("onResponse:   body=" + body.string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Logger.i("onResponse:   t=" + t.getMessage());
    }
});
/**
 * cookie同步 
 */
@SuppressWarnings("deprecation")
private void syncCookieToWebView(String url,List<String> cookies)
{
    CookieSyncManager.createInstance(this);
    CookieManager cm = CookieManager.getInstance();
    cm.setAcceptCookie(true);
    if(cookies!=null)
    {
        for (String cookie : cookies)
        {
            cm.setCookie(url,cookie);//注意端口号和域名,这种方式可以同步所有cookie,包括sessionid 
        }
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
        CookieManager.getInstance().flush();
    } else {
        CookieSyncManager.getInstance().sync();
    }
}

清除cookie

@SuppressWarnings("deprecation")
public void clearCookies(Context context) {
    CookieSyncManager.createInstance(context);
    CookieManager cookieManager = CookieManager.getInstance();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        cookieManager.removeAllCookies(null);
    } else {
        cookieManager.removeAllCookie();
    }
}

注意事项

  1. 同步cookie要在WebView加载url之前,否则WebView无法获得相应的cookie,也就无法通过验证。
  2. 每次登录成功后都需要调用”syncCookie”方法将cookie同步到WebView中,同时也达到了更新WebView的cookie。如果登录后没有及时将cookie同步到WebView可能导致WebView拿的是旧的session id和服务器进行通信。

webView 下载文件的两种方法

总共 有两种 方法, - 第一种,自己实现实现逻辑 ,下载,保存到相应目录; - 第二种,调用系统的下载方法

核心代码如下

主要是给webView设置DownloadListener监听器

        mWebView.setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition,
                                        String mimetype, long contentLength) {

                //第一种下载方式是 自定义的http工具类
//                new HttpDownloadThread(url,contentDisposition,mimetype,contentLength).start();
                //第二种下载方式是调用系统的webView,具有默认的进度条
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse(url));
                startActivity(intent);
            }
        });

HttpDownloadThread

public class HttpDownloadThread extends Thread {


    private String mUrl;
    private String mContentDisposition;
    private String mMimetype;
    private long mContentLength;

    public HttpDownloadThread(String url, String contentDisposition, String mimetype, long contentLength) {
        this.mUrl = url;
        this.mContentDisposition=contentDisposition;
        this.mContentDisposition=mimetype;
        this.mContentDisposition=contentDisposition;

    }

    @Override
    public void run() {
        URL url;
        try {
            url = new URL(mUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            InputStream in = conn.getInputStream();

            File downloadFile;
            File sdFile;
            FileOutputStream out = null;
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)){
                downloadFile = Environment.getExternalStorageDirectory();
                sdFile = new File(downloadFile, "test.file");
                out = new FileOutputStream(sdFile);
            }

            //buffer 4k
            byte[] buffer = new byte[1024 * 4];
            int len = 0;
            while((len = in.read(buffer)) != -1){
                if(out != null)
                    out.write(buffer, 0, len);
            }

            //close resource
            if(out != null)
                out.close();

            if(in != null){
                in.close();
            }



        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

webView的 一些扩展使用

// 设置是否支持画面缩放
settings.setBuiltInZoomControls(true);

settings.setSupportZoom(true);
// 设置是否显示缩放器
settings.setDisplayZoomControls(false);
//  设置字体的大小
settings.setTextZoom(120);

关于webView 与js互相调用的,可以 参考这一篇文章webview与javascript交互回调与异步


题外话

  • 个人觉得webView还是 挺坑爹的,在不同的sdk版本中,方法修改的幅度有点多,看文档的时候经常看到许多方法过时了
  • webView的用法还有很多很多,这里只讲解了比较常用的,有兴趣的话了解更多的 话,请自行 阅读官方文档。不过本人建议是先掌握常用的 就OK了,以后其他需要用到的再去学就好了

关于如何监听网络简化及处理的 ,有兴趣的可以阅读我的这一篇博客android 监听网络状态的变化及实战

文章首发地址CSDN:http://blog.csdn.net/gdutxiaoxu/article/details/53084703

源码下载地址:https://github.com/gdutxiaoxu/FunAPP.git

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用CoordinatorLayout打造各种炫酷的效果

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

    用户2965908
  • 自定义 behavior - 完美仿 QQ 浏览器首页,美团商家详情页

    记得两年前的时候,曾写过自定义 behavior 的文章 自定义 Behavior -仿新浪微博发现页的实现,到现在差不多有一万多的阅读量吧。

    用户2965908
  • Android 自定义最大宽度,高度, 宽高比例 Layout

    这篇博客主要介绍的是怎样自定义一个可以指定最大宽度,高度,以及宽高比的 Layout。原理其实很简单,就是通过重写 onMeasure 方法,重新制定 Meas...

    用户2965908
  • Android 天气APP(十二)空气质量、UI优化调整

    这个空气质量包含的就是一些常规的空气指数,比如PM2.5、PM10等数据,相信任何天气APP都会有这些数据,所以我也加上去吧,并且修改一些UI。

    晨曦_LLW
  • 安卓自定义列表dialog

    这个形式也是最常用的,不过最近需要用到列表信息Dialog,原生的不光样式不能满足需求,而且是开发电视端的APP,需要对焦点进行特殊处理,所以就需要自定义...

    先知先觉
  • Android使用Spinner实现城市级联下拉框

    最近写一个使用Spinner实现城市级联下拉框的Dome,现在总结一下,第一次写博客,互相学习。

    砸漏
  • 9.indicate、xutils、json

    六月的雨
  • 12、json、GridView、缓存

    六月的雨
  • 在Android界面上显示和获取Logcat日志输出的方法

    当我们设置好之后,我们还需要一个process类,作用通俗来讲就是用Java代码来进行adb命令行操作代码是:

    砸漏
  • 笔记47 | Android性能优化之使用include标签重用layouts(二)

    项勇

扫码关注云+社区

领取腾讯云代金券