Android WebView的基本使用与注意点

作者: uncochen | 来源:发表于2016-11-03 16:40 被阅读0次

    在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);
        }
    }
    

    关于作者

    相关文章

      网友评论

        本文标题:Android WebView的基本使用与注意点

        本文链接:https://www.haomeiwen.com/subject/fkfiuttx.html