Android WebView的基本使用与注意点

在Android开发中,WebView的使用频率越来越高,这里跟大家分享下WebView使用中的一些技巧或者注意点

11月21日更新:

修复Android 6.0网页title获取的bug,具体见-2.网页title的获取

11月4日更新:

新增-9.WebView视频全屏播放

1.WebView顶部展示加载进度

自定义控件,继承WebView

public class ErmWebView extends WebView {
    private ProgressBar progressbar;

    public ErmWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
        progressbar.setProgressDrawable(context.getResources().getDra(R.drawable.style_progressbar_web));//设置样式
        progressbar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 5, 0, 0));
        addView(progressbar);
        setWebChromeClient(new WebChromeClient());
}
    public class WebChromeClient extends android.webkit.WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {//进度
            if (newProgress == 100) {
                progressbar.setVisibility(GONE);
            } else {
                if (progressbar.getVisibility() == GONE)
                    progressbar.setVisibility(VISIBLE);
                    progressbar.setProgress(newProgress);
                }
            super.onProgressChanged(view, newProgress);
    }
}

2.网页title的获取

需要在WebChromeClient的onReceivedTitle()中获取,其他api获取不可靠
webview控件在Android6.0上有一个bug,那就是onReceivedTitle()会调用两次,一次为网页的url,一次为网页真正的title,故这里需要做一个过滤

mWebview.setWebChromeClient(new WebChromeClient(){
        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
            //由于webview在title在6.0上会调用两次,故这里过滤掉title为url的那次
            if (!getUrl().contains(title)) mTitleBar.setTitle(title);//设置title
        }
    });

3.本地图片(文件)选择

web页面中经常需要用户选取手机中的图片(文件)上传,这种默认的js调用,在iOS和PC端是OK的,但是Android端需要处理,否则不会做任何操作

private ValueCallback<Uri> mUploadMessage;//回调图片选择,4.4以下
private ValueCallback<Uri[]> mUploadCallbackAboveL;//回调图片选择,5.0以上

mWebview.setWebChromeClient(new WebChromeClient(){
    //由于是隐藏API,故没有@Override注解

     // For Android 3.0+单参数
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        if (getContext() instanceof Activity) {
            ((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILE_SELECT_CODE);
        }
    }

    // For Android 3.0+多参数
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        if (getContext() instanceof Activity) {
            ((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Browser"), FILE_SELECT_CODE);
        }
    }

    // For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        if (getContext() instanceof Activity) {
            ((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Chooser"), FILE_SELECT_CODE);
        }
    }

    // For Android 5.0+
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        mUploadCallbackAboveL = filePathCallback;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        if (getContext() instanceof Activity) {
            ((Activity) getContext()).startActivityForResult(Intent.createChooser(i, "File Browser"), FILE_SELECT_CODE);
        }
        return true;
    }
}

//将选择结果回调给网页
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) {
        if (Build.VERSION.SDK_INT >= 21) //5.0以上版本处理
            mWebview.getUploadCallbackAboveL().onReceiveValue(null);//取消选择必须回调null,否则web处于阻塞状态,无法继续操作
        else
            mWebview.getUploadMessage().onReceiveValue(null);
        return;
    }
    switch (requestCode) {
        case FILE_SELECT_CODE: {
            if (Build.VERSION.SDK_INT >= 21) {//5.0以上版本处理
                Uri uri = data.getData();
                Uri[] uris = new Uri[]{uri};
                mWebview.getUploadCallbackAboveL().onReceiveValue(uris);//回调给js
            } else {//4.4以下处理
                Uri uri = data.getData();
                Logger.i(uri.toString());
                mWebview.getUploadMessage().onReceiveValue(uri);
            }
        }
        break;
    }
}
  • 以上代码在大部分手机测试通过,但不排除个别机型存在bug的可能

4.宽度自适应

如果网页比较宽,webView就可以左右滑动,用户一屏看不到所有的内容,体验会比较差,我们可以设置页面自适应

    WebSettings settings = getSettings();
    settings.setJavaScriptEnabled(true);//开启js
    
    settings.setUseWideViewPort(true);//宽度自适应
    settings.setLoadWithOverviewMode(true);
    settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
  • html中的图片需要自适应,否则可能会出现图片特别宽,导致整个页面无法自适应

5.自定义网页加载出错页面

对于客户端来说,加载出错包括 网络出错+网页Load出错.默认的出错页面比较丑,并且会直接显示URL,导致我们的url暴露.

mWebview.setWebViewClient(new WebViewClient() {
        @SuppressWarnings("deprecation")
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {//低版本

            mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);//去除默认的404页面
            mEmpty.showErrorType("网页加载出错,请点击重试");
        }

        @TargetApi(android.os.Build.VERSION_CODES.M)//编译版本>23
        @Override
        public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
            onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
        }
    });
  • 多次测试之后,上面这样写才能捕捉到所有加载出错的回调,并且在大部分机型及API版本测试通过.

6.返回键处理

用户希望能够返回到上一个网页,而不是直接退出当前webActivity

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    mLeftTv.setVisibility(View.VISIBLE);
    if (keyCode == KeyEvent.KEYCODE_BACK && mWebview.canGoBack()) {
        mWebview.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

7.退出WebView

WebView的底层调用WebKit内核,加载也会另开线程,所以当WebView所在的Activity退出时,WebView内部的组建和线程可能并没有销毁,导致持续占用资源,甚至视频或者音频还在播放

@Override
protected void onPause() {
    super.onPause();
    if (mWebview != null) mWebview.onPause();//退出关闭webview
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mWebview != null) mWebview.destroy();//退出关闭webview
}
  • 以上代码在大部分手机上含视频播放的web页面测试通过,视频及视频声音不会再播放.但音频web页面退出未测试

8.与JS代码互相调用

有时候JS与原生互相调用各自的方法

mWebview.addJavascriptInterface(new InfoJs(), "infojs");//增加js交互的方法

public class InfoJs {
    @JavascriptInterface
    public void showToast(String s) {
        ToastUtils.showToast("展示toast  " + s);
    }
}

9.WebView视频全屏播放

webview虽然默认支持全屏播放的事件,但是在大部分手机上都是无法全屏的,要么点击全屏是空白,要么没有任何反应,好在WebView有提供全屏及取消全屏的回调事件,所以如果需要支持WebView的全屏播放,就需要处理一下:

1.布局文件中增加一个与WebView同等级的FrameLayout,用于装载全屏的Video控件

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

<FrameLayout
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

2.处理全屏及取消全屏事件:

private class CustomWebViewChromeClient extends WebChromeClient{

    @Override
    public void onShowCustomView(View view, CustomViewCallback callback) {
        fullScreen();
        mWebview.setVisibility(View.GONE);
        mVideo.setVisibility(View.VISIBLE);
        mVideo.addView(view);
        mCallBack=callback;
        mWebTitle.setVisibility(View.GONE);//如果有titleBar,一并隐藏
        super.onShowCustomView(view, callback);
    }

    @Override
    public void onHideCustomView() {
        fullScreen();
        if (mCallBack!=null){
            mCallBack.onCustomViewHidden();
        }
        mWebview.setVisibility(View.VISIBLE);
        mVideo.removeAllViews();
        mVideo.setVisibility(View.GONE);
        mWebTitle.setVisibility(View.VISIBLE);//如果有titleBar,一并显示出来
        super.onHideCustomView();
    }
}

private void fullScreen() {//强制切换屏幕方向
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

关于作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容