本篇为大家带来的是WebView的多种使用场景讲解,说起WebView往往给大家带来的印象应该是Android与H5的混合开发,下面洒家为大家进行详细讲解
我那些关于WebView的回忆 ~ 包含入门使用、优化加载样式、监听加载状态、各场景后退键处理、俩端交互流程、header、user-agent传值、交互常见问题、较全API整合
2018.4.23 因项目需求,扩展了后退键的监听使用,特此修改了文章结构,同时把新的内容补充到了文章末尾
基础使用
- 权限
<uses-permission android:name="android.permission.INTERNET" />
- MainActivity
package com.example.webviewdemo;
import android.os.Bundle;
import android.app.Activity;
import android.view.KeyEvent;
import android.view.Menu;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends Activity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.main_web);
// 首先设置支持JS脚本
mWebView.getSettings().setJavaScriptEnabled(true);
// 支持Js在当前App打开应用,当页面跳转的时候依旧在当前的WebView之中
mWebView.setWebViewClient(new WebViewClient());
// 传入当前我们需要打开的网址(基于http的,也有的是https,不过后台给的一般都没有问题),记得添加网络权限
mWebView.loadUrl("http://www.baidu.com");
}
}
- MainActivity的Xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.administrator.webdemo.MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_web" />
</RelativeLayout>
常见问题
仅记录个人在使用WebView时所遇的一些问题场景和解决方式
1:常规后退键处理,解决常规返回关闭activity问题
Tips:
- 清单文件中配置
Inter(网络)权限
- 上文代码缺一不可
- Web的Xml大小自行处理
MainActivity(具体代码且附带详解)
package com.example.webviewdemo;
import android.os.Bundle;
import android.app.Activity;
import android.view.KeyEvent;
import android.view.Menu;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends Activity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.main_web);
// 首先设置支持JS脚本
mWebView.getSettings().setJavaScriptEnabled(true);
// 支持Js在当前App打开应用,当页面跳转的时候依旧在当前的WebView之中
mWebView.setWebViewClient(new WebViewClient());
// 传入当前我们需要打开的网址(基于http的,也有的是https,不过后台给的一般都没有问题),记得添加网络权限
mWebView.loadUrl("http://www.baidu.com");
}
//使用Webview的时候,返回键没有重写的时候会直接关闭程序,这时候其实我们要其执行的知识回退到上一步的操作
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//这是一个监听用的按键的方法,keyCode 监听用户的动作,如果是按了返回键,同时Webview要返回的话,WebView执行回退操作,因为mWebView.canGoBack()返回的是一个Boolean类型,所以我们把它返回为true
if(keyCode==KeyEvent.KEYCODE_BACK&&mWebView.canGoBack()){
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.administrator.webdemo.MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_web" />
</RelativeLayout>
补充内容
2:解决当WebView内部页面多次跳转之后,最终退到起始页的时候,无法退出当前Activity的问题
- 需求:当我们当前打开的WebView内部进行多次跳转,最后退回到首次打开的WebView页面,这时候我们需要关闭当前依赖的Activity
- 逻辑想法:判断
WebView是否还可goBack
? 如果可以就是goBack
,反之onBackPressed()
关了当前页面就oK了!
解决方案
后退键的点击事件:
if(mWebView.canGoBack()){
mWebView.goBack();
}else{
onBackPressed();
}
重写onKeyDown
:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_BACK&&mWebView.canGoBack()){
mWebView.goBack();
return true;
}else{
onBackPressed();
}
return super.onKeyDown(keyCode, event);
}
注意(补于2018/12/25 圣诞节 - - !) :部分测试会要求我们移动端的右侧返回箭头同样具备层层推出的逻辑,所以我们需要重写 onBackPressed()方法 ! (如测试无此要求可不做处理)
// 千万注意这里可没有重写父类的 super.onBackPressed(); ,如果有这个你的设置是无效的,所以记得删除!(至于是否可以将这行代码移动到末尾处,君请亲测)
@Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
finish();
}
}
最终效果图
3:当我们Activity使用的Title是原生的时候,动态捕获WebView的Title进行更改我们Title展示内容(这里只实现了首次获取title方法,却无法动态捕获WebView的Title进行替换)
获取WebView的title方法
mWebView.getTitle();
初级使用:
-
需求:获取加载页(H5)的title,用于展示在 移动端的Title处
-
解决方案
onCreate
中 获取title
//获取我们Activity原生的Title(TextView)
TextView tvTitle = findViewById(R.id.tv_title);
在 onPageFinished
中获取title (这个只是讲获取前端title的方式,具体要看下面的终极使用)
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//通过mWebView.getTitle() 获取html页的title
String title = view.getTitle();
if (!TextUtils.isEmpty(title)) {
tvTitle.setText(title);
}
}
终极使用:
建立在初级使用的基础之上,当我们Web加载完页面之后进行多重跳转,同时需求我们及时更换title,如首页 - 抽奖 - 活动积分这样的流程
- 加入
WebChromeClient
//监听浏览器内部跳转
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String title) {
//及时获取title进行更换
super.onReceivedTitle(view, title);
if (title != null) {
tvTitle.setText(title);
}
}
});
WebViewClient
的onPageFinished
进行设置
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String title = view.getTitle();
if (!TextUtils.isEmpty(title)) {
tvTitle.setText(title);
}
}
});
4:解决WebView内存泄漏
@Override
protected void onDestroy() {
if( mWebView!=null) {
// 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
// destory()
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();
}
super.on Destroy();
}
5:WebView页面栈遇上重定向,导致用户多次返回,针对此问题进行的交互优化
借鉴地址:WebView页面栈遇上重定向,导致用户多次返回,针对此问题进行的交互优化
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
if (showLoadingWeb.canGoBack()){
//关键在于这里,判断用户如果返回的回次在俩次以内直接关闭activity ,可自行设置
if (showLoadingWeb.copyBackForwardList().getSize()<=2){
finish();
overridePendingTransition(R.anim.left_fadein,R.anim.left_fadeout);
}else {
showLoadingWeb.goBack();
}
}else {
finish();
overridePendingTransition(R.anim.left_fadein,R.anim.left_fadeout);
}
return true;
}else {
return super.onKeyDown(keyCode , event);
}
}
//WebBackForwardList list = webViewInstance.copyBackForwardList();
//以上注释是对核心代码的一个解释说明 这样会看的更清晰一点,其主要获取回退栈内的页面数量
项目使用
项目中成熟且完美的使用Webview
完整代码 (因为是从项目里copy出来修改之后的,所以一些基类就不贴了,不影响大家观看)
package com.bakheet.garage.home.activity;
import android.Manifest;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.bakheet.garage.R;
import com.bakheet.garage.base.BaseActivity;
import com.bakheet.garage.http.HttpManager;
import com.bakheet.garage.http.HttpUrl;
import com.bakheet.garage.http.ObjectResult;
import com.bakheet.garage.utils.SpUtil;
import com.bakheet.garage.utils.TlogUtils;
import com.bakheet.garage.utils.ToastUtils;
import com.umeng.socialize.ShareAction;
import com.umeng.socialize.UMShareAPI;
import com.umeng.socialize.UMShareListener;
import com.umeng.socialize.bean.SHARE_MEDIA;
import com.umeng.socialize.media.UMImage;
import com.umeng.socialize.media.UMWeb;
import java.io.IOException;
import cn.qqtheme.framework.util.CompatUtils;
import retrofit2.Call;
import retrofit2.Response;
/**
* @author yongliu
* date 2018/4/16.
* desc:
*/
public class ActivityDetailActivity extends BaseActivity implements View.OnClickListener {
private WebView mWebView;
private View mOutView;
private String bannerUrl, title, content, bannerSrc;
private ProgressBar mBar;
private String userId;
private TextView tvTitle;
@Override
protected int getLayoutId() {
return R.layout.activity_detail;
}
@Override
protected void init(Bundle savedInstanceState) {
initView();
initWebView();
}
private void initView() {
mWebView = findViewById(R.id.web_activity);
tvTitle = findViewById(R.id.tv_title);
}
private void initWebView() {
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
mWebView.loadUrl("自己填写个baidu就可以测试~");
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
mBar.setVisibility(View.GONE);
} else {
mBar.setVisibility(View.VISIBLE);
mBar.setProgress(newProgress);
}
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title != null) {
tvTitle.setText(title);
}
}
});
/**
* 监听WebView的加载状态 分别为 : 加载的 前 中 后期
* */
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mOutView.setBackground(CompatUtils.getDrawable(ActivityDetailActivity.this, R.mipmap.ic_tab_shade_bg));
String title = view.getTitle();
if (!TextUtils.isEmpty(title)) {
tvTitle.setText(title);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//本应该加载的H5静态界面
mWebView.loadUrl(url);
return true;
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.iv_back:
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
onBackPressed();
}
default:
break;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
mWebView.goBack();
return true;
} else {
onBackPressed();
}
return super.onKeyDown(keyCode, event);
}
//千万注意这里可没有重写父类的 super.onBackPressed(); ,如果有这个你的设置是无效的,所以记得删除!(至于是否可以将 这行代码移动到末尾处,君请亲测)
@Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
finish();
}
}
}