WebView使用解析(一)之基本用法
来源:互联网 发布:淘宝优惠券怎么用不了 编辑:程序博客网 时间:2024/05/23 14:34
WebView基本用法
加载在线URL
void loadUrl(String url)
这个函数主要加载url所对应的网页地址,或者用于调用网页中的指定的JS方法(调用js方法的用法,后面会讲),但有一点必须注意的是:loadUrl()必须在主线程中执行!!!否则就会报错!!!。
注意:加载在线网页地址是会用到联网permission权限。
url = "http://www.baidu.com";...mLoad.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.loadUrl(url); } });
代码很简单,就是在点击按钮的时候加载网址,但需要注意的是:网址必须完整即以http://或者ftp://等协议开头,不能省略!不然将加载不出来,这是因为webview是没有自动补全协议功能的,所以如果我们不加,它将识别不出来网址类型,也就加载不出来了。
但如果我们运行上面的代码,效果却是利用浏览器来打开网址,却不是使用webview打开网址:
如果我们想实现在webview中打开网址需要怎么做呢? 我们需要设置WebViewClient:
mWebView.setWebViewClient(new WebViewClient());
再来运行一下看看:
要在WebView中打开链接,就必须要设置WebViewClient。
加载本地URL
本地html文件可以放在assets文件夹下,也可以放在手机目录下。
public static final String OFFLINE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/offline/";url = "file:///android_asset/web.html"; //Assets目录下url = "file://" + OFFLINE_PATH_DOC + "web.html"; //手机目录下或者 url = "file:///storage/emulated/0/offline/web.html"
HTML内容:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <h1>WebView加载本地HTML</h1> </head> <body> </body> </html>
对于加载URL的总结就是:
1、如果是在线网址记得添加网络访问权限
2、在线网址中,如果要使用webview打开,记得设置WebViewClient
3、打开本地html文件时,是不需要设置WebViewClient,对应的asstes目录的url为:file:///android_asset/xxxxx
加载HTML片段
上面讲了通过loadUrl()来加载本地页面和在线地址的方式,这里给大家再补充两个方法LoadData()与loadDataWithBaseURL(),它们不是用来加载整个页面文件的,而是用来加载一段代码片的。
1. LoadData()
public void loadData(String data, String mimeType, String encoding)
- String data:代码片段内容
- String mimeType:代码片段所对应的MIME类型,如果传null,则默认为text/html
- String encoding:代码片段的编码方式
public class MyActivity extends Activity { private WebView mWebView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mWebView = (WebView) findViewById(R.id.webview); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setDefaultTextEncodingName("utf-8"); String summary = "<html><body>A <b>android</b> developer.</body></html>"; mWebView.loadData(summary, "text/html", "utf-8"); }}
在使用loadData时,在数据里面不能出现英文字符:’#’, ‘%’, ‘\’ , ‘?’ 这四个字符,如果有的话可以用 %23, %25, %27, %3f,这些字符来替换。直接使用这四个字符会造成的问题如下:
%:会报找不到页面错误,页面全是乱码。
#:会让你的goBack失效,但canGoBAck是可以使用的。于是就会产生返回按钮生效,但不能返回的情况。
\ 和? :在转换时,会报错,因为它会把\当作转义符来使用,如果用两级转义,也不生效。
其实,Android给我们提供了一个专门用来转码的函数:URLEncoder.encode(String s, String charsetName) ,它能将冲突的字符进行转义,然后再传给webview,这样webview在加载时就不会有冲突了,encode函数的声明如下:
/** String s:代码段String charsetName:编码类型*/public static String encode(String s, String charsetName);
使用data中,如果出现中文乱码问题,解决办法:参数传”utf-8”,页面的编码格式也必须是utf-8,这样编码统一就不会乱了。
注意:
1、loadData()应该是不能加载图片的,加载图片的内容我们后面会使用loadDataWithBaseURL来实现。
2、为了防止字符冲突,在传递loadData的数据时,必须使用URLEncoder.encode()函数来转义
3、页面的编码格式必须与代码中传参的编码格式一致,不然会导致乱码
1. loadDataWithBaseURL()
相比loadData,这个函数更常用,因为loadData能实现的功能,它都能实现,而且也不会出现字符冲突。其函数声明如下:
public void loadDataWithBaseURL(String baseUrl, String data,String mimeType, String encoding, String historyUrl)
- String baseUrl:基准URL,不需要可以传null,它的意思是,如果data中的url是相对地址,则就会加上基准url来拼接出完整的地址,比如baseUrl是http://img.my.csdn.net,data中有个Img标签,它的内容是:<\img src=’/uploads/201309/01/1378037151_7904.jpg’>,很明显src的地址不是本地地址也不是在线地址,那它就是一个相对地址,所以加上baseUrl以后才是它的完整地址:http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg。如果data中的url是绝对地址,则baseUrl不起作用。
- String mimeType:MIME类型
- String encoding:编码方式
- String historyUrl:当前的历史记录所要存储的值。如果不需要可以传Null,loadDataWithBaseURL它本身并不会向历史记录中存储数据,要想实现历史记录,需要我们自己来实现;有关历史记录的实现方式是比较复杂的,历史记录是以Key/value的方式存储在一个historyList里的,当前进后退时,会用Key来取出对应的value值来加载进webview中。而Key就是这里的baseUrl,Value就是这里的historyUrl;history所指向的必须是一个页面,并且页面存在于SD卡中或程序中(assets);
public class MyActivity extends Activity { private WebView mWebView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mWebView = (WebView) findViewById(R.id.webview); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setDefaultTextEncodingName("utf-8"); String baseURL = "http://img.my.csdn.net"; String data = "风景优美 <img src='/uploads/201309/01/1378037151_7904.jpg'>"; mWebView.loadDataWithBaseURL(baseURL, data, "text/html", "utf-8", null); }}
总结:
loadData和loadDataWithBaseURL这两种方法,我建议使用后者,虽然loadData的历史记录不需要我们自己来实现,但在使用时,这就两个加载上后者比前者快一到两倍,且不会出现字符冲突。另外loadData不能加载图片,而loadDataWithBaseURL是可以加载图片的。
WebView基本设置
如果我们需要设置WebView的属性,是通过WebView.getSettings()获取设置WebView的WebSettings对象, 然后调用WebSettings中的方法来实现的。
WebSettings的方法及说明如下:
/** * 是否支持缩放,配合方法setBuiltInZoomControls使用,默认true */setSupportZoom(boolean support)/** * 是否需要用户手势来播放Media,默认true */setMediaPlaybackRequiresUserGesture(boolean require)/** * 是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false */setBuiltInZoomControls(boolean enabled)/** * 是否显示窗口悬浮的缩放控制,默认true */setDisplayZoomControls(boolean enabled)/** * 是否允许访问WebView内部文件,默认true */setAllowFileAccess(boolean allow)/** * 是否允许获取WebView的内容URL ,可以让WebView访问ContentPrivider存储的内容。 默认true */setAllowContentAccess(boolean allow)/** * 是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false */setLoadWithOverviewMode(boolean overview)/** * 是否保存表单数据,默认false */setSaveFormData(boolean save)/** * 设置页面文字缩放百分比,默认100% */setTextZoom(int textZoom)/** * 是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口 */setUseWideViewPort(boolean use)/** * 是否支持多窗口,如果设置为true ,WebChromeClient#onCreateWindow方法必须被主程序实现,默认false */setSupportMultipleWindows(boolean support)/** * 指定WebView的页面布局显示形式,调用该方法会引起页面重绘。默认LayoutAlgorithm#NARROW_COLUMNS */setLayoutAlgorithm(LayoutAlgorithm l)/** * 设置标准的字体族,默认”sans-serif”。font-family 规定元素的字体系列。 * font-family 可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体, * 则会尝试下一个。也就是说,font-family 属性的值是用于某个元素的字体族名称或/及类族名称的一个 * 优先表。浏览器会使用它可识别的第一个值。 */setStandardFontFamily(String font)/** * 设置混合字体族。默认”monospace” */setFixedFontFamily(String font)/** * 设置SansSerif字体族。默认”sans-serif” */setSansSerifFontFamily(String font)/** * 设置SerifFont字体族,默认”sans-serif” */setSerifFontFamily(String font)/** * 设置CursiveFont字体族,默认”cursive” */setCursiveFontFamily(String font)/** * 设置FantasyFont字体族,默认”fantasy” */setFantasyFontFamily(String font)/** * 设置最小字体,默认8. 取值区间[1-72],超过范围,使用其上限值。 */setMinimumFontSize(int size)/** * 设置最小逻辑字体,默认8. 取值区间[1-72],超过范围,使用其上限值。 */setMinimumLogicalFontSize(int size)/** * 设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。 */setDefaultFontSize(int size)/** * 设置默认填充字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。 */setDefaultFixedFontSize(int size)/** * 设置是否加载图片资源,注意:方法控制所有的资源图片显示,包括嵌入的本地图片资源。 * 使用方法setBlockNetworkImage则只限制网络资源图片的显示。值设置为true后, * webview会自动加载网络图片。默认true */setLoadsImagesAutomatically(boolean flag)/** * 是否加载网络图片资源。注意如果getLoadsImagesAutomatically返回false,则该方法没有效果。 * 如果使用setBlockNetworkLoads设置为false,该方法设置为false,也不会显示网络图片。 * 当值从true改为false时。WebView会自动加载网络图片。 */setBlockNetworkImage(boolean flag)/** * 设置是否加载网络资源。注意如果值从true切换为false后,WebView不会自动加载, * 除非调用WebView#reload().如果没有android.Manifest.permission#INTERNET权限, * 值设为false,则会抛出java.lang.SecurityException异常。 * 默认值:有android.Manifest.permission#INTERNET权限时为false,其他为true。 */setBlockNetworkLoads(boolean flag)/** * 设置是否允许执行JS。 */setJavaScriptEnabled(boolean flag)/** * 是否允许Js访问任何来源的内容。包括访问file scheme的URLs。考虑到安全性, * 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的, * 不会受到影响。ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本 * 以上默认为false */setAllowUniversalAccessFromFileURLs(boolean flag)/** * 是否允许Js访问其他file scheme的URLs。包括访问file scheme的资源。考虑到安全性, * 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的, * 不会受到影响。如果getAllowUniversalAccessFromFileURLs为true,则该方法被忽略。 * ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本以上默认为false */setAllowFileAccessFromFileURLs(boolean flag)/** * 设置存储定位数据库的位置,考虑到位置权限和持久化Cache缓存,Application需要拥有指定路径的 * write权限 */setGeolocationDatabasePath(String databasePath)/** * 是否允许Cache,默认false。考虑需要存储缓存,应该为缓存指定存储路径setAppCachePath */setAppCacheEnabled(boolean flag)/** * 设置Cache API缓存路径。为了保证可以访问Cache,Application需要拥有指定路径的write权限。 * 该方法应该只调用一次,多次调用自动忽略。 */setAppCachePath(String appCachePath)/** * 是否允许数据库存储。默认false。查看setDatabasePath API 如何正确设置数据库存储。 * 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView * 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置 */setDatabaseEnabled(boolean flag)/** * 是否存储页面DOM结构,默认false。 */setDomStorageEnabled(boolean flag)/** * 是否允许定位,默认true。注意:为了保证定位可以使用,要保证以下几点: * Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的权限 * Application 需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的回调, * 接收Js定位请求访问地理位置的通知 */setGeolocationEnabled(boolean flag)/** * 是否允许JS自动打开窗口。默认false */setJavaScriptCanOpenWindowsAutomatically(boolean flag)/** * 设置页面的编码格式,默认UTF-8 */setDefaultTextEncodingName(String encoding)/** * 设置WebView代理,默认使用默认值 */setUserAgentString(String ua)/** * 通知WebView是否需要设置一个节点获取焦点当 * WebView#requestFocus(int,android.graphics.Rect)被调用的时候,默认true */setNeedInitialFocus(boolean flag)/** * 基于WebView导航的类型使用缓存:正常页面加载会加载缓存并按需判断内容是否需要重新验证。 * 如果是页面返回,页面内容不会重新加载,直接从缓存中恢复。setCacheMode允许客户端根据指定的模式来 * 使用缓存。 * LOAD_DEFAULT 默认加载方式 * LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存 * LOAD_NO_CACHE 不使用缓存 * LOAD_CACHE_ONLY 只使用缓存 */setCacheMode(int mode)/** * 设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW方 * 式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW */setMixedContentMode(int mode)
例如:
示例1:打开页面时, 自适应屏幕:
webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放 webSettings.setLoadWithOverviewMode(true);
效果图如下:(所使用的网址为:http://www.w3school.com.cn/)
注意:自己写的网页代码,也可以在HTML中做宽度100%自适应屏幕
示例2:使页面支持缩放:
//开启javascript支持 webSettings.setJavaScriptEnabled(true); // 设置可以支持缩放 webSettings.setSupportZoom(true); // 设置出现缩放工具 webSettings.setBuiltInZoomControls(true);
示例3:如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点
webview.requestFocusFromTouch();
其他请自行摸索。
WebView缓存模式
WebView是Android中直接加载html页面的控件。当我们加载Html时候,会在我们data/应用package下生成database与cache两个文件夹:
我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webviewCache文件夹下。
WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)、H5缓存(即AppCache)。
网页缓存
网页缓存的结构:
/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db缓存模式(5种)
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
如:www.taobao.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。
www.360.com.cn的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
总结:根据以上两种模式,建议缓存策略为:判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。
3、清除缓存
webview.clearCache(boolean);
CacheManager.clear高版本中需要调用隐藏API。
4、控制大小
无系统API支持。
可选方式:定时统计缓存大小、按时间顺序删除缓存。
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private TextView mTitle; private WebView mWebView; private TextView mClear; private String url; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); url = "https://wap.baidu.com/"; findView(); } private void findView() { mTitle = (TextView) findViewById(R.id.tv_topbar_title); mWebView = (WebView) findViewById(R.id.mWebView); mClear = (TextView) findViewById(R.id.clear); mClear.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.clearCache(true); //清除缓存 } }); initWebView(); mWebView.setWebViewClient(new WebViewClient() { @Override public void onLoadResource(WebView view, String url) { Log.i(TAG, "onLoadResource url="+url); // 开始加载 super.onLoadResource(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView webview, String url) { Log.i(TAG, "intercept url="+url); // 重写此方法表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边 webview.loadUrl(url); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.e(TAG, "onPageStarted"); } @Override public void onPageFinished(WebView view, String url) { String title = view.getTitle(); //得到网页标题 Log.e(TAG, "onPageFinished WebView title=" + title); mTitle.setText(title); mTitle.setVisibility(View.VISIBLE); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { Toast.makeText(getApplicationContext(), description, Toast.LENGTH_LONG).show(); } }); mWebView.loadUrl(url); } private void initWebView() { mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); // 设置缓存模式 if (NetUtils.isNetworkAvailable(MainActivity.this)) { mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); } else { mWebView.getSettings().setCacheMode( WebSettings.LOAD_CACHE_ELSE_NETWORK); } // webView.getSettings().setBlockNetworkImage(true);//把图片加载放在最后来加载渲染 // 支持多窗口 webView.getSettings().setSupportMultipleWindows(true); // 开启 DOM storage API 功能 mWebView.getSettings().setDomStorageEnabled(true); //开启 database storage API 功能 mWebView.getSettings().setDatabaseEnabled(true); // 开启 Application Caches 功能// webView.getSettings().setAppCacheEnabled(true); } @Override // 设置回退 // 覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法 public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { mWebView.goBack(); // goBack()表示返回WebView的上一页面 return true; } else { finish(); } return super.onKeyDown(keyCode, event); } /*** * 防止WebView加载内存泄漏 */ @Override protected void onDestroy() { super.onDestroy(); mWebView.removeAllViews(); mWebView.destroy(); }}
AndroidManifest.xml 中加权限
<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
H5缓存
Application Cache(简称 AppCache)似乎是为支持 Web App 离线使用而开发的缓存机制。它的缓存机制类似于浏览器的缓存(Cache-Control 和 Last-Modified)机制,都是以文件为单位进行缓存,且文件有一定更新机制。但 AppCache 是对浏览器缓存机制的补充,不是替代。
如果你还不了解什么叫做H5缓存,推荐这篇文章:H5 缓存机制浅析 - 移动端 Web 加载性能优化
1、缓存构成
根据setAppCachePath(String appCachePath)提供的路径,在H5使用缓存过程中生成的缓存文件。
2、缓存模式
无模式选择,通过setAppCacheEnabled(boolean flag)设置是否打开。默认关闭,即,H5的缓存无法使用。
3、清除缓存
找到调用setAppCachePath(String appCachePath)设置缓存的路径,把它下面的文件全部删除就OK了。
4、控制大小
通过setAppCacheMaxSize(long appCacheMaxSize)设置缓存最大容量,默认为Max Integer。
同时,可能通过覆盖WebChromeClient.onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)来设置缓存超过先前设置的最大容量时的策略。
...String cacheDirPath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();Log.i(TAG, "cacheDirPath="+cacheDirPath);/**设置Application Caches 缓存目录*/mWebView.getSettings().setAppCachePath(cacheDirPath);//开启 Application Caches 功能mWebView.getSettings().setAppCacheEnabled(true);mWebView.getSettings().setAppCacheMaxSize(5*1024*1024); //5MmWebView.getSettings().setAllowFileAccess(true); //使manifest生效
webview可以设置一个WebChromeClient对象,在其onReachedMaxAppCacheSize函数对扩充缓冲做出响应。代码如下:
mWebView.setWebChromeClient(new WebChromeClient(){ //扩充缓存的容量 @Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { quotaUpdater.updateQuota(spaceNeeded * 2); }
清除缓存:
/** * 清除WebView缓存 */ public void clearWebViewCache(){ //清理Webview缓存数据库 try { deleteDatabase("webview.db"); deleteDatabase("webviewCache.db"); } catch (Exception e) { e.printStackTrace(); } //WebView 缓存文件 File appCacheDir = new File(getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath()); Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath()); //删除webview 缓存 缓存目录 if(appCacheDir.exists()){ deleteFile(appCacheDir); } }
权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
其次要修改http服务器中的配置,使其支持text/cache-manifest,我使用的是apach服务器,是windows版本的,在apache的conf文件夹中找到mime.types文件,打开后在文件的最后加上
“text/cache-manifest mf manifest”,
重启服务器即可。
WebView离线阅读
WebView离线阅读就是使用WebView加载本地的html文档,上面说的H5缓存实质上已经实现了WebView离线阅读的功能,但是需要服务器的支持,本节我们自己来实现WebView的离线阅读而不依赖服务器。
首先来看看webview加载网络资源的情况:
url = "http://dfz.eastday.com/nanchang/u1ai17496_t11.html";...mOnline.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.loadUrl(url); } });
我们的思路是把html文档下载到本地,然后用webview加载本地文档即可。
private void DownloadArticle(String url){ String htmlCode = ""; try { Document document = Jsoup.parse(new URL(url), 10000); if (document != null){ String filePath = OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html"; //url进行MD5编码后作为文件名 htmlCode = document.html().toString(); HttpUtil.SaveTextToFile(filePath, htmlCode); } } catch (Exception e) { e.printStackTrace(); } }
mOffline.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String filePath = "file://" + OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html"; mWebView.loadUrl(filePath); } });
可以看到,确实加载到了本地的html文档,但是图片没有了,版式也有些不对。其实看html源码
我们可以看到图片,版式等都是在加载html时实时从网络获取的,我们现在在离线状态下,当然获取不到这些内容了。所以我们还需要把需要的js、css、图片等文件也下载下来,在我们执行html文档的时候可以选择从本地加载这些资源。有了想法,实施。
/** * 下载稿件html以及包含的图片,cs,js * @param url */ private void DownloadArticle(String url){ String htmlCode = ""; try { Document document = Jsoup.parse(new URL(url), 10000); if (document != null){ String filePath = OFFLINE_PATH_DOC + MD5Util.getMD5Str(url) + ".html"; htmlCode = document.html().toString(); htmlCode = replaceImage(document,"img","src",htmlCode); //替换成从本地获取image htmlCode = replaceStyle(document,"link","href",htmlCode); //替换成从本地获取css htmlCode = htmlCode.replace("Common.js", ""); //这篇稿件特殊情况,Common.js会弹出一个alert,屏蔽它 HttpUtil.SaveTextToFile(filePath, htmlCode); } } catch (Exception e) { e.printStackTrace(); } }... /** * 替换中间的图片 * @param document * @param tag * @param attr * @param htmlCode */ private String replaceImage(Document document, String tag, String attr, String htmlCode){ Elements imageDocuments = document.getElementsByTag(tag); String imgURL = ""; Element element = null; for(int i = 0; i < imageDocuments.size(); i++){ element = imageDocuments.get(i); imgURL = element.attr(attr); if (imgURL.isEmpty()) continue; DownloadImage(getWholeURL(imgURL)); htmlCode = htmlCode.replace(imgURL, "file://" + OFFLINE_PATH_IMG + MD5Util.getMD5Str(getWholeURL(imgURL))); } return htmlCode; }.../** *替换中间的样式css * @param document * @param tag * @param attr * @param htmlCode * @return */ private String replaceStyle(Document document, String tag, String attr, String htmlCode){ String initialUrl = ""; String replaceUrl = ""; String getBack = ""; String fileJS = ""; Element element = null; Elements elements = document.getElementsByTag(tag); for(int i = 0; i < elements.size(); i++){ element = elements.get(i); initialUrl = element.attr(attr); if (initialUrl.isEmpty()) continue; replaceUrl = getWholeURL(initialUrl); fileJS = OFFLINE_PATH_JS + MD5Util.getMD5Str(getWholeURL(replaceUrl)); if (!new File(fileJS).exists()){ getBack = HttpUtil.requestContentWithGet11(replaceUrl); HttpUtil.SaveTextToFile(fileJS, getBack); } htmlCode = htmlCode.replace(initialUrl, "file://" + fileJS); } return htmlCode; }
再运行一下
非常完美,成功实现了webview的离线阅读。
WebView与JS交互
JS调用Java代码
网页中需要通过JS代码来调用本地的Android代码,比如H5页面需要判断当前用户是否登录等。
利用JS代码调用JAVA代码,主要是用到WebView下面的一个函数:
public void addJavascriptInterface(Object obj, String interfaceName)
这个函数有两个参数:
- Object obj:interfaceName所绑定的对象
- String interfaceName:所绑定的对象所对应的名称
它的意思就是向WebView注入一个obj对象,对象的别名为interfaceName,在JS中,我们就可以通过interfaceName这个别名来调用obj对象中的任何public方法。
我们实现这样一个效果,在上面的html中添加了一个按钮,当点击按钮时调用Android的Toast函数弹出一个toast消息。
先看android代码:
public class MyActivity extends Activity { private WebView mWebView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new JSBridge(), "android"); mWebView.loadUrl("file:///android_asset/web.html"); } public class JSBridge { //在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效 @JavascriptInterface public void toastMessage(String message) { Toast.makeText(getApplicationContext(), "JS--->Natvie:" + message, Toast.LENGTH_LONG).show(); } }
下面我们看看html代码:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <h1>WebView加载本地HTML</h1> <input type="button" value="js调native" onclick="ok()"></head><body><script type="text/javascript"> function ok() { android.toastMessage("我是来自JS的消息!"); }</script></body></html>
JAVA调用JS代码
前面给大家演示了如何通过JS调用Java代码,这里就反过来看看,如何在Native中调用JS的代码 。
本例的效果图如下:
在点击“求和”按钮时,调用webview中的JavaScript求和函数,将结果通过alert显示出来。
先看html代码:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <h1>WebView加载本地HTML</h1> <input type="button" value="js调native" onclick="ok()"></head><body><script type="text/javascript"> function ok() { android.toastMessage("我是来自JS的消息!"); } function sum(i,m) { alert("Native--->JS sum=" + (i + m)); }</script></body></html>
在这里,我们写了一个求和函数sum(i,m) ,alert出求和结果
再来看看JAVA的调用代码:
public class MyActivity extends Activity { private WebView mWebView; private Button mBtn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mWebView = (WebView) findViewById(R.id.webview); mBtn = (Button) findViewById(R.id.btn); WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); mWebView.loadUrl("file:///android_asset/web.html"); mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.loadUrl("javascript:sum(3,8)"); } }); } }
看看在JAVA中调用JS函数的方法:
String url = "javascript:methodName(params……);" webView.loadUrl(url);
javascript:伪协议让我们可以通过一个链接来调用JavaScript函数 ,中间methodName是JavaScript中实现的函数 ,jsonParams是传入的参数列表 。
JAVA中如何得到JS中的返回值呢?
相信看完了上面Native和JS的互相调用,你一定知道了。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <h1>WebView加载本地HTML</h1> <input type="button" value="js调native" onclick="ok()"></head><body><script type="text/javascript"> function ok() { android.toastMessage("我是来自JS的消息!"); } function sum(i,m) { var result = i+m; alert("Native--->JS sum=" + result); android.onSumResult(result); }</script></body></html>
public class JSBridge { //在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效 @JavascriptInterface public void toastMessage(String message) { Toast.makeText(getApplicationContext(), "JS--->Natvie:" + message, Toast.LENGTH_LONG).show(); } @JavascriptInterface public void onSumResult(int result) { Toast.makeText(this,"received result:"+result,Toast.LENGTH_SHORT).show(); } }
Android4.4之后,我们有新的方法在JAVA中获取JS的返回值:
首先给html的求和函数一个返回值:
function sum(i,m) { var result = i+m; return result; }
其次java代码时用evaluateJavascript方法调用:
mWebView.evaluateJavascript("sum(3,8)", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Toast.makeText(getApplicationContext(),"Android 4.4 received result:"+value,Toast.LENGTH_SHORT).show(); } });
注意:
1. evaluateJavascript须在html加载完毕后执行,否则返回的value为null。
2. 上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。
3. evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。
Demo下载地址
- WebView使用解析(一)之基本用法
- (一)WebView实践之基本用法
- android WebView(一)基本使用
- Android WebView使用解析一
- Android之网络编程(一)WebView 的用法
- EventBus完全解析(一)基本使用
- WebView使用解析(二)之WebViewClient/WebChromeClient
- Android Volley完全解析(一),Volley的基本用法
- Android Volley详细解析(一):Volley基本用法
- 3、Volley解析(一),了解Volley的基本用法
- xml解析之SAX解析基本用法
- Android---WebView基本用法
- WebView基本用法
- Android WebView 基本用法
- WebView基本用法
- Android 属性动画解析(一): 基本用法
- webview使用总结(一)
- WebView的使用(一)
- leetcode 110. Balanced Binary Tree DFS
- 如何使用jquery 动态修改元素的 background-image?
- Ubuntu 8.04安装VMware tools
- View系列(1)--自定义一个ListView的下拉刷新
- 冒泡排序
- WebView使用解析(一)之基本用法
- 签名文件错误Failed to finalize session : INSTALL_FAILED_INVALID_APK:
- web开发内嵌tomcat的使用,告别复杂的配置
- Android事件分发机制一
- iOS集成FFmpeg
- iOS开发 代码截图相关
- 32 linux内核里的中断处理
- html拼接时onclick事件传递json对象
- java中调用oracle procedure或者function返回类型的问题