WebView详细使用三(WebView初始化和Activity的封装)

一、WebView初始化(接口定义)

WebView初始化主要分为三步:

  1. 设置WebSettings,打开WebView一些默认关闭的设置。
  2. 设置WebViewClient,对一些特殊链接统一处理。
  3. 设置WebChromeClient,对Html标题、页面加载进度、相机相册打开回调等进行一个监听。
    对于以上三点,定义出一个接口来初始化设置:
/**
 * WebView初始化接口定义
 */

public interface IWebViewInit {
    /**
     * 1. 初始化和设置WebView
     */
    WebView initWebView(WebView webView);

    /**
     * 2. 初始化WebViewClient
     */
    WebViewClient initWebViewClient();

    /**
     * 3. 初始化WebChromeClient
     */
    WebChromeClient initWebChromeClient();
}
二、IWebViewInit接口实现类WebViewInitImpl
/**
 * WebView初始化的定义
 */

public class WebViewInitImpl implements IWebViewInit {

    private static final String USER_AGENT = "appXXX";


    private WebChromeClientImpl mWebChromeClient;
    private Activity mActivity;

    public WebViewInitImpl(Activity activity) {
        mActivity = activity;
    }

    @Override
    public WebView initWebView(WebView webView) {
        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        WebSettings webSetting = webView.getSettings();
        webSetting.setUserAgentString(USER_AGENT + webSetting.getUserAgentString());

        webSetting.setDatabaseEnabled(true);
        webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);
        webSetting.setTextSize(WebSettings.TextSize.NORMAL);

        // ===设置JS可用
        webSetting.setJavaScriptEnabled(true);
        // JS打开窗口
        webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
        // ===设置JS可用
        // 可以访问文件
        webSetting.setAllowFileAccess(true);
        // ===缩放可用
        webSetting.setSupportZoom(true);
        webSetting.setDisplayZoomControls(false); //隐藏原生的缩放控件
        webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); //设置缩放功能   //能不能缩放 取决于网页设置
        webSetting.setLoadWithOverviewMode(true);
        webSetting.setBuiltInZoomControls(true);
        // ===缩放可用
        // 支持多窗口
        webSetting.setSupportMultipleWindows(true);
        // ===============缓存
        webSetting.setCacheMode(WebSettings.LOAD_DEFAULT);// 决定是否从网络上取数据。
        webSetting.setAppCacheEnabled(true);
        // ===============缓存
        webSetting.setUseWideViewPort(true);
        webSetting.setAppCacheMaxSize(Long.MAX_VALUE);
        webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);
        // ==定位
        webSetting.setDomStorageEnabled(true);
        webSetting.setGeolocationEnabled(true);
        // ==定位
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              // 适配图片加载不出来的问题
              webSettings.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        return webView;
    }

    @Override
    public WebViewClient initWebViewClient() {
        return new WebViewClientImpl();
    }

    @Override
    public WebChromeClient initWebChromeClient() {
        mWebChromeClient = new WebChromeClientImpl(mActivity);
        return mWebChromeClient;
    }

    /**
     * 页面标题、进度回调
     */
    public void setOnWebChromeListener(WebChromeClientImpl.OnWebChromeListener onWebChromeListener) {
        if (mWebChromeClient != null) {
            mWebChromeClient.setOnWebChromeListener(onWebChromeListener);
        }

    }

    /**
     * 选择相机相册处理
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (mWebChromeClient != null) {
            mWebChromeClient.onActivityResult(requestCode, resultCode, data);
        }
    }
}
三、WebViewClient 实现类WebViewClientImpl
/**
 * 对加载Url回调统一处理
 * 根据公司业务,自行处理
 */

public class WebViewClientImpl extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        if (webView != null && url != null) {
            Context context = webView.getContext();
            if (url.endsWith(".apk")) {
                Uri apkUri = Uri.parse(url);
                Intent intent = new Intent(Intent.ACTION_VIEW, apkUri);
                context.startActivity(intent);
            } else if (url.startsWith("http")) {
                webView.loadUrl(url);
            } else {
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                if (isInstall(context, intent)) {
                    context.startActivity(intent);
                }
            }
        }
        return true;
    }

    // 判断app是否安装
    private boolean isInstall(Context context, Intent intent) {
        return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
    }
}
四、WebChromeClient封装实现类WebChromeClientImpl
/**
 * H5打开相机相册的回调监听
 * 进度条的回调监听
 */

public class WebChromeClientImpl extends WebChromeClient {
    // WebView打开相机相册的请求码
    public static final int FILE_REQUEST_CODE = 0x011;
    /**
     * 进度条的回调监听
     */
    private OnWebChromeListener onWebChromeListener;

    /**
     * 打开相册 本地文件等等
     */
    private ValueCallback<Uri> uploadFile;
    private ValueCallback<Uri[]> uploadFiles;
    private Activity mActivity;

    public WebChromeClientImpl(Activity activity) {
        mActivity = activity;
    }

    /**
     * 进度发生改变
     */
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (onWebChromeListener != null) {
            onWebChromeListener.onProgressChanged(view, newProgress);
        }
    }

    /**
     * 接收到标题
     */
    @Override
    public void onReceivedTitle(WebView view, String title) {
        if (onWebChromeListener != null) {
            onWebChromeListener.onReceivedTitle(view, title);
        }
    }

    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        handleFileChooser(uploadMsg, null);
    }

    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        handleFileChooser(uploadMsg, null);
    }

    // For Android  > 4.1.1
    @Override
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        handleFileChooser(uploadMsg, null);
    }

    // For Android  >= 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        handleFileChooser(null, filePathCallback);
        return true;
    }

    /**
     * 打开相册 本地文件等等
     */
    private void handleFileChooser(ValueCallback<Uri> uploadMsg, ValueCallback<Uri[]> filePathCallback) {
        if (mActivity == null || mActivity.isFinishing()) {
            return;
        }
        uploadFile = uploadMsg;
        uploadFiles = filePathCallback;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        mActivity.startActivityForResult(Intent.createChooser(intent, "请选择"), FILE_REQUEST_CODE);
    }

    /**
     * Activity回调处理
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 处理相机相册选择
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case FILE_REQUEST_CODE:
                    if (null != uploadFile) {
                        Uri result = data == null || resultCode != Activity.RESULT_OK ? null : data.getData();
                        uploadFile.onReceiveValue(result);
                        uploadFile = null;
                    }
                    if (null != uploadFiles) {
                        Uri result = data == null || resultCode != Activity.RESULT_OK ? null : data.getData();
                        uploadFiles.onReceiveValue(new Uri[]{result});
                        uploadFiles = null;
                    }
                    break;
            }
        } else if (resultCode == Activity.RESULT_CANCELED) {
            if (null != uploadFile) {
                uploadFile.onReceiveValue(null);
                uploadFile = null;
            }
            if (uploadFiles != null) {
                uploadFiles.onReceiveValue(null);
                uploadFiles = null;
            }
        }
    }


    // 页面标题、加载进度回调监听接口
    public interface OnWebChromeListener {
        void onReceivedTitle(WebView view, String title);

        void onProgressChanged(WebView view, int newProgress);
    }

    public void setOnWebChromeListener(OnWebChromeListener onWebChromeListener) {
        this.onWebChromeListener = onWebChromeListener;
    }

}

代码有点多,但无非就是做了设置WebSettings、设置WebViewClient、设置WebChromeClient这三件事。

五、WebView在Activity的封装步骤
  1. Activity数据的传递(标题、URL),根据实际项目可以自己扩展。
  2. WebView初始化和加载页面的工具类,方便管理页面跳转等一些通用操作。
  3. Activity的使用封装。
六、Activity数据的传递(标题、URL)
/**
 * WebActivity传递参数的Bean
 */

public class WebData implements Serializable {
    public String url;
    public String title;
}
七、WebView初始化和加载页面的工具类
/**
 * WebView初始化和加载页面的工具类
 */

public class WebUtil {
    private static boolean isInit;

    private WebUtil() {
    }

    private static class Holder {
        private static final WebUtil INSTANCE = new WebUtil();
    }

    public static WebUtil getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * 腾讯X5内核初始化
     */
    public void init(Context context) {
        if (context == null) {
            return;
        }
        if (isInit) {
            return;
        }
        //--搜集本地tbs内核信息并上报服务器,服务器返回结果决定使用哪个内核。
        try {
            QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {
                @Override
                public void onViewInitFinished(boolean arg0) {
                }

                @Override
                public void onCoreInitFinished() {
                }
            };
            //x5内核初始化接口
            QbSdk.initX5Environment(context.getApplicationContext(), cb);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        isInit = true;
    }

    private void loadWebPage(WebView webView, String url) {
        if (webView != null) {
            if (url.startsWith("http")) {
                webView.loadUrl(url);
            }

        } else {
            throw new NullPointerException("WebView is null!");
        }
    }

    private void loadLocalPage(WebView webView, String url) {
        loadWebPage(webView, "file:///android_asset/" + url);
    }

    public void loadPage(WebView webView, String url) {
        //如果是电话协议
        if (url.contains("tel:")) {
            callPhone(webView.getContext(), url);
            return;
        }
        if (URLUtil.isNetworkUrl(url) || URLUtil.isAssetUrl(url)) {
            loadWebPage(webView, url);
        } else {
            loadLocalPage(webView, url);
        }
    }


    private void callPhone(Context context, String uri) {
        final Intent intent = new Intent(Intent.ACTION_DIAL);
        final Uri data = Uri.parse(uri);
        intent.setData(data);
        context.startActivity(intent);
    }

    /**
     * 打开WebActivity
     */
    public void startWebActivity(Activity activity, String url) {
        startWebActivity(activity, url, null);
    }

    /**
     * 打开WebActivity
     */
    public void startWebActivity(final Activity activity, final String url, final String title) {
        // WebView初始化
        WebData data = new WebData();
        data.url = url;
        data.title = title;
        startWebActivity(activity, data);
    }

    /**
     * 打开WebActivity
     */
    public void startWebActivity(Activity activity, WebData data) {
        init(activity);
        Bundle bundle = new Bundle();
        bundle.putSerializable(WebActivity.WEB_DATA, data);
        ActivityUtil.startActivity(activity, WebActivity.class, bundle);
    }
}
八、Activity的使用封装
/**
 * 封装的WebActivity
 */

public class WebActivity extends AppActivity implements WebChromeClientImpl.OnWebChromeListener {

    public static final String WEB_DATA = "WebData";
    private FrameLayout mFlWebContainer;
    private WebViewInitImpl mWebViewInitializer;
    private WebView mWebView;
    // Title的封装对象
    private DefTitleBar mTitleBar;
    private ProgressWebView mProgressWebView;
    /**
     * JS注入的名称
     */
    private static final String JS_NAME = "appCarB";
    /**
     * 打开页面传递过来的参数
     */
    private WebData mWebData;


    @Override
    protected Object getContentLayout() {
        return R.layout.act_web;
    }

    @Override
    protected void initData(Bundle bundle) {
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
        // 获取上个页面传递过来的数据
        mWebData = (WebData) getIntent().getSerializableExtra(WEB_DATA);
        // WebView初始化对象
        mWebViewInitializer = new WebViewInitImpl(this);
        // WebView初始化
        initWebView();
    }

    @SuppressLint("JavascriptInterface")
    private void initWebView() {
        if (mWebView != null) {
            mWebView.removeAllViews();
        } else {
            mProgressWebView = new ProgressWebView(this);
            mWebView = mWebViewInitializer.initWebView(mProgressWebView.getWebView());
            mWebView.setWebViewClient(mWebViewInitializer.initWebViewClient());
            mWebView.setWebChromeClient(mWebViewInitializer.initWebChromeClient());
            // 注入JS交互
            mWebView.addJavascriptInterface(new JSEvent(this), JS_NAME);
        }
    }

    @Override
    protected void initTitle(DefTitleBar titleBar) {
        this.mTitleBar = titleBar;
        if (mTitleBar != null) {
            mTitleBar.setTitleLeftRightPadding(50);
            if (mWebData != null && !TextUtils.isEmpty(mWebData.title)) {
                mTitleBar.setTitle(mWebData.title);
            }
        }
    }

    @Override
    protected void initView(View view) {
        mFlWebContainer = findViewById(R.id.fl_web_container);
        mWebViewInitializer.setOnWebChromeListener(this);
        if (mFlWebContainer.getChildCount() > 0) {
            mFlWebContainer.removeAllViews();
        }
        mFlWebContainer.addView(mProgressWebView);
        // 加载URL
        if (mWebView != null && mWebData != null) {
            // 跳转并进行页面加载
            WebUtil.getInstance().loadPage(mWebView, mWebData.url);
        }
    }

    // 标题回调
    @Override
    public void onReceivedTitle(WebView view, String title) {
        if (mTitleBar != null) {
            if (mWebData != null && !TextUtils.isEmpty(mWebData.title)) {
                return;
            }
            mTitleBar.setTitle(title + "");
        }
    }

    // 页面加载进度回调
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (mProgressWebView == null) {
            return;
        }
        ProgressBar progressbar = mProgressWebView.getProgressbar();
        if (progressbar == null) {
            return;
        }
        if (newProgress == 100) {
            progressbar.setVisibility(View.GONE);
        } else {
            if (progressbar.getVisibility() == View.GONE) {
                progressbar.setVisibility(View.VISIBLE);
            }
            progressbar.setProgress(newProgress);
        }
    }


    @Override
    public void onPause() {
        if (mWebView != null) {
            mWebView.onPause();
        }
        super.onPause();
    }

    @Override
    public void onResume() {
        if (mWebView != null) {
            mWebView.onResume();
        }
        super.onResume();
    }


    @Override
    public void onDestroy() {
        if (mFlWebContainer != null && mWebView != null) {
            mWebView = null;
            mProgressWebView = null;
        }
        super.onDestroy();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (mWebView != null && (keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
            mWebView.goBack(); // 浏览网页历史记录 goBack()和goForward()
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 处理相机相册选择
        if (mWebViewInitializer != null) {
            mWebViewInitializer.onActivityResult(requestCode, resultCode, data);
        }
    }

}
九、在其它Activity页面调用WebActivity
   WebUtil.getInstance().startWebActivity(this,"https://www.xxx.com");
  1. 测试页面效果图没有截出来。
  2. 除了和H5的JS交互未写,至此WebActivity封装完毕。
  3. WebActivity类中相关类的文章:
    打造Android通用的标题栏。
    Android Activity基类的抽取AppActivity(二)。
    WebView详细使用一(带进度的WebView)。
    WebView详细使用二(腾讯X5内核的集成)。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容