【Android】 WebView 常用功能及解决方案集合
1. 加载空白页
重写 onReceivedSslError() 方法
比如处理 https 请求时,webView 默认时不处理 https 请求的,页面显示空白。
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed(); //表示等待证书响应
// handler.cancel(); //表示挂起连接,为默认方式
// handler.handleMessage(null); //可做其他处理
}
});
2. WebView 与父控件滑动冲突问题
比如在一个 ViewPager 中添加了 WebView,WebView 加载了一个可滑动的界面(查看图片滑动等),当网页滑动的时候,就直接切换了另一个 Tab。就可以用 onOverScrolled
方法处理。
当 WebView 滑动时会触发 overScrollBy()
方法,在 overScrollBy()
方法里面会调用 onOverScrolled()
方法。 其中,前两个参数是 距离原点的 x 和 y 的距离,后两个是 当 View 滑动到 两侧 或 上下 时为 true 当点击的时候允许 父控件(ViewPager)滑动,当滑动到边界的时候禁止父控件滑动。
/**
* Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to
* respond to the results of an over-scroll operation.
*
* @param scrollX New X scroll value in pixels
* @param scrollY New Y scroll value in pixels
* @param clampedX True if scrollX was clamped to an over-scroll boundary
* @param clampedY True if scrollY was clamped to an over-scroll boundary
*/
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
if (clampedX) { // 滑动到两侧 允许 ViewPager 滑动
requestDisallowInterceptTouchEvent(false);
}
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//disables ViewPager when user presses down
requestDisallowInterceptTouchEvent(true);
return true;
}
return true;
}
3. WebView 注入 Js 代码,实现功能
在直接加载 html 内容的时,有时候缺少一些交互,那么可以通过 WebView 注入 JS 代码的方式实现功能。以下举了几个例子。
3.1 实现点击图片滑动查看
在信息流当中经常穿插着一些网页界面,提供更丰富的内容,如何能让 H5 的界面里的图片,向原生开发一样可以产生图片集并且左右滑动呢?
思路
- 提取网页中的
img
标签,形成图片集 - 为每个图片添加点击事件
Js 方法如下,可以看到里面也调用了原生方法,用来设置 图片集 和 打开查看图片界面,其中图片链接用 (;)分号隔开
function pic(){
var imgList = "";
var imgs = document.getElementsByTagName("img");
for(var i = 0;i < imgs.length; i ){
var img = imgs[i];
imgList = imgList img.src ";";
img.onclick = function(){
window.android.goToGallery(this.src);
}
}
window.android.setImgUrls(imgList);
}
但是要通过 WebView 把这个代码段注入进去,注意这里要把代码转化成字符串,有些符号需要转义处理。Android 也要处理 setImgUrls()
和 goToGallery()
方法
private void addJs() {
mWebVew.loadUrl("javascript:(" getImageJS() ")()");
}
private String getImageJS(){
return
"function pic(){"
" var imgList = \"\";"
" var imgs = document.getElementsByTagName(\"img\");"
" for(var i = 0;i < imgs.length; i ){"
" var img = imgs[i];"
" imgList = imgList img.src \";\";"
" img.onclick = function(){"
" window." JS_NAME ".goToGallery(this.src);"
" }"
" }"
" window." JS_NAME ".setImgUrls(imgList);"
"}";
}
@JavascriptInterface
public void goToGallery(String url) {
// 传到展示图片的viewPager mUrls.indexOf(url) 可以获取当前图片的 index
}
@JavascriptInterface
public void setImgUrls(String imgLists) {
String[] urls = imgLists.split(";");//url拼接成的字符串,有分号隔开
for (String url : urls) {
mUrls.add(url);
}
}
3.2 H5 链接重新处理
除了上面有图片需求,有时候H5自己带的连接也需要重新处理,比如跳转到 App 的界面
思路:获取 <a>
标签,为连接标签重新设置点击方式,同时原来的链接点击取消掉,参考代码如下:
function link(){
var href = document.getElementsByTagName("a");
for (var i = 0; i < href.length; i ) {
var a = href[i];
a.onclick = function(){
window.andorid.toWebView(this.value);
};
a.value = a.href;
a.href = "javascript:void(0)";
}
}
4. 音视频播放问题
当网页支持播放时,退出会发现声音会继续播放
有很多帖子给出方案是在 onPause 方法暂停 和 在 onResume 恢复
protected void onPause() {
if(webView != null){//暂停WebView
webView.onPause();
webView.pauseTimers();
}
super.onPause();
}
@Override
protected void onResume() {
if(webView != null){//恢复WebView
webView.onResume();
webView.resumeTimers();
}
super.onResume();
}
在实战过程中,由于我是在一个ViewPager 嵌入一个 含有 WebView 的 Fragment ,当切换 tab 的时候 是不会触发 onResume 的, 所以采用如下方案,下面用到了 reload()
方法,因为 直接用 WebView 的 resume 之类的也没有啥作用。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isPause && isVisibleToUser){ // (onResume) onPause 后不 reload
isPause = false;
return;
}
if (mWebView != null && !isPause && !isVisibleToUser) { // 切出时 调用 reload 停止播放
mWebView.reload();
}
}
@Override
public void onPause() {
super.onPause();
isPause = true;
}
5. 视频全屏功能
如果 WebView 含有视频资源,如果要点击视频可以全屏播放可以通过处理 WebChromeClient 中的两个方法来实现
@Override
public void onHideCustomView() {
//退出全屏
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// 进入全屏
// 当网页中有视频的时候,点击全屏按键会调用这个方法,可设置网页播放全屏
}
private View mCustomView;
private ViewGroup mRootViewGroup;
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// 进入全屏
if (mCustomView != null) {
return;
}
mCustomView = view;
mCustomView.setLayoutParams(new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT));
// 或者 getActivity().getWindow().getDecorView(); 获取根视图并addView
mRootViewGroup = getActivity().findViewById(android.R.id.content); 获取根视图并addView
mRootViewGroup .addView(mCustomView); // 添加到根视图
if (getActivity() != null) {
//设置横屏
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
//设置全屏
getActivity().getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
@Override
public void onHideCustomView() {
//退出全屏
if (mCustomView == null) {
return;
}
//移除全屏视图并隐藏
mRootViewGroup.removeView(mCustomView);
mCustomView = null;
if (getActivity() != null) {
//设置竖屏
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
//清除全屏
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
注意:在使用上面方法处理的时候,还需要考虑物理返回键。
有的视频容器按键异常,比如不可点击之类的,可以添加如下设置:
settings.setMediaPlaybackRequiresUserGesture(false);//SDK>18 是否支持手势控制网页媒体,比如视频的全屏
6. WebView 软键盘遮挡问题
如果通过 H5 可能做些互动功能,比如评论之类,此时会调用软键盘,会发现软键盘遮挡会遮挡输入框(这个问题很眼熟,在正常页面有时也会遮挡,真是问题不分控件) 有很多大神给的方案是创建一个 KeyBoardListener 类来处理,在我使用的项目中会在 ViewPager 中添加WebView 所以就需要改写一下,需要处理上面 tab的空间,参考方法类如下: 使用方式
KeyBoardListener mKeyBoardListener = KeyBoardListener.getInstance(getActivity(), height);
mKeyBoardListener.addListener();
KeyBoardListener.java
package cn.ninebot.webview;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import cn.ninebot.commonlibs.utils.DisplayUtils;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
public class KeyBoardListener {
private Activity activity;
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private static KeyBoardListener keyBoardListener;
private static int mHeight = 0;
private static int mStatusBarHeight = 0;
private int contentHeight;
private boolean isFirst = true;
private boolean isAddListener = false;
/**
* 这个传进来的 height 是 tablayout 控件的高度, 因为 tablayout 被键盘弹起来,
* 可以传入这个值对其进行操作
*/
public static KeyBoardListener getInstance(Activity activity, int height) {
keyBoardListener = new KeyBoardListener(activity);
mHeight = height;
if (height != 0) {
mStatusBarHeight = DisplayUtils.getStatusBarHeight();
}
return keyBoardListener;
}
public KeyBoardListener(Activity activity) {
super();
this.activity = activity;
init();
}
private void init() {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
public void addListener() {
if (!isAddListener) {
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
isAddListener = true;
isFirst = true;
}
}
public void removeListener() {
if (mChildOfContent != null && isAddListener) {
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
isAddListener = false;
}
}
ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
if (isFirst) {
contentHeight = mChildOfContent.getHeight();
isFirst = false;
}
possiblyResizeChildOfContent();
}
};
//重新调整跟布局的高度
private void possiblyResizeChildOfContent() {
if (activity.getRequestedOrientation() == SCREEN_ORIENTATION_PORTRAIT) { //竖屏
int usableHeightNow = computeUsableHeight();
//当前可见高度和上一次可见高度不一致 布局变动
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference mStatusBarHeight mHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
// keyboard probably just became hidden
frameLayoutParams.height = contentHeight;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
7. WebView 安全警告
在使用 WebView 加载网页时,有时候会出现警告:您要访问的网站包含有害应用。
这是 WebView 的安全浏览保护策略,在 Android 8.0(API Level 26)开始的默认策略,被应用在所有 App 的 WebView 当中。
Google 会自己维护一套“不安全”网站的列表,并通过 Google Play 服务,同步到所有的设备上。当你要访问某些被标记为“不安全”的网站时,它就会以此“红屏”警告用户。
注意这是默认策略,虽然出发点是为了保护用户,但是有时候我们自己的 App 还是要有自主管控的权利。
在 Android 8.0(API Level 26)中,可以通过以下两种方法关闭安全策略:
方法一:
<manifest>
<meta-data
android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="false" />
<application> ... </application>
</manifest>
方法二: WebView 的安全策略是默认开始的,如果想要关闭它,需要通过 WebSettings 这个类,其中有 setSafeBrowsingEnabled(boolean)
方法,可以用于设置是否开启安全模式。
settings.setSafeBrowsingEnabled(false);// 是否开启安全模式
此方法是一种全局的策略,也就是要么开启、要么关闭。
相关参考:
8. WebView 监听下载
WebView 是可以监听网页的下载链接,但是默认不会开启,所以点击不会有动作,可以使用系统 DownloadListener 可以监听下载。
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
// url 你要访问的下载链接
// userAgent 是HTTP请求头部用来标识客户端信息的字符串
// contentDisposition 为保存文件提供一个默认的文件名
// mimetype 该资源的媒体类型
// contentLength 该资源的大小
// 这几个参数都是可以通过抓包获取的
// 用手机默认浏览器打开链接
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
9. Android P 以上 进入WebView可能 crash
在 Android P 上应用 WebView 可能会出现如下错误
Caused by: java.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported
因为Android P 行为变更,多进程 webView 不能使用同一个目录,需要为不同进程 webView 设置不同目录。为不同进程设置不同的目录即可解决问题
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
boolean isMainProcess = getApplicationContext().getPackageName().equals(getCurrentProcessName());
if (!isMainProcess) {
WebView.setDataDirectorySuffix("any-folder-name");
}
}
Admob banner ad not loading in android P
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanejic
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24