专栏首页终身开发者据说这里可以帮你解决许多关于WebView的问题

据说这里可以帮你解决许多关于WebView的问题

使用WebView开发的坑很多,这是众所周知的。本文分别对WebView的三个基本控件(俗称三剑客WebViewClientWebChromeClient,WebView)做了一些封装,方便使用,避免掉坑里。

主要有以下功能:

  1. 自定义出错页面,并实现重新加载事件
  2. 全屏播放视频
  3. 封装更加简单易用生命周期api,使用这些生命周期的方法可以避免很多与H5交互的坑

CustomWebViewClient

WebViewClient中主要是对onReceivedError方法进行重写。这里面的逻辑这样的:

  1. 出错的url如果跟打开的url是一样的,那么这个时候显示自定义的出错页面。这个自定义页面是一个本地静态html。放在assets目录下。
  2. 如果这个出错的url就是本地的静态文件,那么也显示自定义访问出错页面。
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {        if (view == null) {            return; // 极端情况下可能出现,加个保险        }        boolean isMainFrame = view.getUrl() != null && view.getUrl().equals(failingUrl);        String url = view.getUrl();        //这判断非常重要,避免打开一个页面时,显示出来了内容,然后有被重定向到一个无效地址        if (isMainFrame || url.equalsIgnoreCase(CustomWebView.CUSTOM_ERROR_PAGE)) {//或者加载本地时也发生错误            if (view instanceof CustomWebView) {                ((CustomWebView) view).onErrorView(url);            }        } else {            Log.d(TAG, "did not show error view! reload url:" + view.getUrl());        }        Log.d(TAG, "onReceivedError-> errorCode=" + errorCode + ",desc=" + description + ",failingUrl=" + failingUrl);    }

CustomWebChromeClient

在这里主要是实现视频全屏播放的逻辑,重写onShowCustomView()onHideCustomView()方法

 @Override    public void onShowCustomView(View view, CustomViewCallback callback) {        if (view == null) {            return;        }        mActivity = AppUtils.getCurrActivity(view.getContext());        Activity activity = mActivity;        if (activity == null || activity.isFinishing()) {            LogUtils.e("must use activity context to show video view!");            return;        }        if (mCustomView != null && callback != null) {            callback.onCustomViewHidden();            return;        }        try {            view.setKeepScreenOn(true);        } catch (SecurityException e) {            LogUtils.d("WebView is not allowed to keep the screen on");        }        mOriginalOrientation = activity.getRequestedOrientation();        FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView();        mFullscreenContainer = new FrameLayout(activity.getApplicationContext());//        mFullscreenContainer.setBackgroundColor(ContextCompat.getColor(activity.getApplicationContext(), android.R.color.black));        mCustomView = view;        mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS);        decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);        setFullscreen(true, true);//        mCurrentView.setVisibility(View.GONE);        if (view instanceof FrameLayout) {            if (((FrameLayout) view).getFocusedChild() instanceof VideoView) {                mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild();                mVideoView.setOnErrorListener(new VideoCompletionListener());                mVideoView.setOnCompletionListener(new VideoCompletionListener());            }        }        mCustomViewCallback = callback;    }    @Override    public void onHideCustomView() {        if (mCustomView == null || mCustomViewCallback == null) {            return;        }        LogUtils.d("onHideCustomView");//        mCurrentView.setVisibility(View.VISIBLE);        try {            mCustomView.setKeepScreenOn(false);        } catch (SecurityException e) {            LogUtils.d("WebView is not allowed to keep the screen on");        }        setFullscreen(false, false);        Activity activity = mActivity;        if (activity == null || activity.isFinishing()) {            return;        }        FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView();        if (decor != null) {            decor.removeView(mFullscreenContainer);        }        if (API < Build.VERSION_CODES.KITKAT) {            try {                mCustomViewCallback.onCustomViewHidden();            } catch (Throwable ignored) {            }        }        mFullscreenContainer = null;        mCustomView = null;        if (mVideoView != null) {            mVideoView.setOnErrorListener(null);            mVideoView.setOnCompletionListener(null);            mVideoView = null;        }        activity.setRequestedOrientation(mOriginalOrientation);        mActivity = null;    }

CustomWebView

在CustomWebView中封装了生命周期方法,resume(),pause(),destroy() 这几个方法对应于Activity或者Fragment中的生命周期方法。同时还自定义访问出错页面。

有了以上三个基本控件的封装,那么使用起来就非常简单了。

 public class WebViewActivity extends AppCompatActivity {    ...    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_web_view);        ...        mWebView.loadUrl(url);        mWebView.setWebViewClient(new CustomWebViewClient());        mWebView.setWebChromeClient(new CustomWebChromeClient() {            @Override            public void onProgressChanged(WebView view, int newProgress) {                super.onProgressChanged(view, newProgress);                mProgressBar.setProgress(newProgress);                mProgressBar.setVisibility(newProgress >= 100 ? View.GONE : View.VISIBLE);            }        });    }    ...    @Override    protected void onPause() {        super.onPause();        mWebView.pause();    }    @Override    protected void onResume() {        super.onResume();        mWebView.resume();    }}

其他注意

由于WebViewActivity中有实现视频全屏播放的功能,那么在CustomWebView中的初始化中需要对WebView作以下配置

 void settings() {        WebSettings setting = getSettings();        setting.setJavaScriptEnabled(true);        setting.setJavaScriptCanOpenWindowsAutomatically(true);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {            setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);        }        setting.setJavaScriptCanOpenWindowsAutomatically(true);        setting.setAllowFileAccess(false);//        setting.setAllowFileAccessFromFileURLs(true);        setting.setBuiltInZoomControls(false);        setting.setSupportZoom(false);        setting.setDisplayZoomControls(false);        setting.setSaveFormData(false);        setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);        setting.setCacheMode(WebSettings.LOAD_NO_CACHE);// 不使用缓存        setting.setDefaultTextEncodingName("UTF-8");        setting.setRenderPriority(WebSettings.RenderPriority.HIGH);        // 自适应屏幕        setting.setUseWideViewPort(true);        setting.setLoadWithOverviewMode(true);        setting.setDomStorageEnabled(true);//应该设置为true,否则视频的缩略图会被放大    }

以上配置都在实际开发中得到验证的,一般来说,这样配置是可以满足很多需求的。

另外如果需要显示全屏,那么需要在WebViewActivitymanifiest中的configChanges属性配置如下:

     <activity            android:name=".WebViewActivity"            android:label="Web"           android:configChanges="orientation|screenSize|keyboardHidden"            android:screenOrientation="portrait"/>

本文所有代码均在https://github.com/wecodexyz/CustomWebView

本文分享自微信公众号 - 终身开发者(AngryCode),作者:AngryCode

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-12-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何在onCreate中获取View的高度和宽度

    在开发过程中经常需要获取到View的宽和高,可以通过View.getWidth()和View.getHeight()来得到宽高。然而新手们经常在onCreate...

    阳仔
  • App 组件化/模块化之路——构建开发架构思路

    随着业务的发展 App 开发技术也越来越成熟,对开发者来说 App 代码量也迅速地增长到一个数量级。对于如何架构 App 已经每个开发者面临的实际问题。好的架构...

    阳仔
  • Android实现长图截取、拼接和添加水印

    本文的讨论是基于手机在没有root情形。 早期版本的Android是没有提供截屏功能的,到4.0时代后才自带了音量Down+电源键截屏的功能。在开发过程中还可以...

    阳仔
  • ConcurrentHashMap源码解析(JDK1.8)

    package java.util.concurrent; import java.io.ObjectStreamField; import java.io....

    武培轩
  • 简析Spark Streaming/Flink的Kafka动态感知

    Kafka是我们日常的流处理任务中最为常用的数据源之一。随着数据类型和数据量的增大,难免要增加新的Kafka topic,或者为已有的topic增加更多part...

    暴走大数据
  • 小程序实现sql插入语句转换成Laravel迁移语句

    饶文津
  • HDU 6342

    用户2965768
  • EventBus源码分析之订阅流程

    订阅者可以是任何对象,唯一的要求是内部有@Subscribe修饰的方法,该方法是有一定要求的,这可以在后面的源码中看到EventBus对该方法的要求。

    用户1108631
  • 直播卖货小程序源码中,商品分类页面是如何实现的

    在直播卖货小程序源码中,一般都包含商品分类页面,如下图,那么这个页面是如何通过代码实现的呢?下面,小编以iOS版本的开发过程为例,来讲述下实现过程。

    万岳教育系统
  • MediaPlayer(十)--release()流程

    小蚂蚁与大象

扫码关注云+社区

领取腾讯云代金券