Android应用里的Webview控件嵌入

作者: 天兵公园 | 来源:发表于2017-09-05 11:21 被阅读0次

    借之前的文章开发一个新的组件,用于在 app 内愉快的使用 webview。

    比如在微信里,打开微信公众号文章的时候,这里文章的内容的渲染都是由 webview 控件完成的,这比在浏览器里打开友善多了,并且功能更强大,可以调用外部程序,识别二维码,支付等等。假如我们的 app 里有动态内容,会经常在服务端编辑好页面的方式更新,那么使用 webview 展示是最好不过了。

    之前的那篇文章中提到过,可以动态传递对象到含有 webview 的 activity 页面,因为这个 activity 每次只会打开一个实例,为了简化对象的传递,用的是单例存储要传递的对象,代码参考如下:

    public enum WebFrameSettings {
        instance;
    
        private String url;
        private HashMap<String, Object> objs;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public HashMap<String, Object> getObjs() {
            return objs;
        }
    
        public void addObject(String key, Object val) {
            objs.put(key, val);
        }
    
        public void clearObjects() {
            objs.clear();
        }
    
        public void removeObject(String key) {
            objs.remove(key);
        }
    
        WebFrameSettings() {
            url = "";
            objs = new HashMap<>();
        }
    
    }
    

    接下来创建一个 activity,放置 Toolbar 、ProgressBar、WebView 控件。

    界面布局代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="me.xuzhi.webframemodule.WebFrameActivity">
    
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbarWebFrameModule"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:background="@color/webFrameToolbarBackgroundColor"
            android:titleTextColor="@color/webFrameToolbarForcegroundColor"
            app:titleTextColor="@color/webFrameToolbarForcegroundColor">
    
        </android.support.v7.widget.Toolbar>
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentStart="true">
    
            <WebView
                android:id="@+id/webViewWebFrameModule"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@+id/progressBar"/>
    
            <ProgressBar
                android:id="@+id/progressBarWebFrameModule"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="match_parent"
                android:layout_height="2dp"
                android:progress="50"
                android:progressDrawable="@drawable/progress_bar_webframe"/>
    
        </FrameLayout>
    
    </LinearLayout>
    
    

    为这个模块创建一个主题,否则 Toolbar 右上角的三个点都是黑色的。

    <resources>
        <style name="ModuleTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:textColorSecondary">#ffffff</item>
        </style>
    </resources>
    

    为 Toolbar 设置标题和返回图标。

    toolbarWebFrameModule.setTitle(WebFrameSettings.instance.getUrl());
    toolbarWebFrameModule.setNavigationIcon(R.drawable.ic_action_arrow_back);
    

    注意 setNavigationOnClickListener 一定要在 setSupportActionBar 之后调用,否则不生效。

    setSupportActionBar(toolbarWebFrameModule);
    toolbarWebFrameModule.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (webViewWebFrameModule.canGoBack()) {
                webViewWebFrameModule.goBack();
            } else finish();
        }
    });
    

    为 Toolbar 增加菜单。

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          xmlns:tools="http://schemas.android.com/tools"
          tools:context=".MainActivity">
        <item
            android:id="@+id/action_close"
            android:icon="@drawable/ic_action_close"
            android:title="关闭"
            app:showAsAction="always"/>
        <item
            android:id="@+id/action_refresh"
            android:title="刷新"
            app:showAsAction="collapseActionView"/>
        <item
            android:id="@+id/action_share"
            android:title="分享"
            app:showAsAction="collapseActionView"/>
        <item
            android:id="@+id/action_copylink"
            android:title="复制链接"
            app:showAsAction="collapseActionView"/>
        <item
            android:id="@+id/action_openWithBrowser"
            android:title="在浏览器中打开"
            app:showAsAction="collapseActionView"/>
    </menu>
    

    重写 activity 的 onCreateOptionsMenu 方法,将 menu 添加进来。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_webframe, menu);
        return true;
    }
    

    菜单按钮的事件:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_close) {
            finish();
        } else if (id == R.id.action_copylink) {
            doCopyLink();
        } else if (id == R.id.action_openWithBrowser) {
            doOpenWithBrowser();
        } else if (id == R.id.action_refresh) {
            doRefresh();
        } else if (id == R.id.action_share) {
            doShare();
        }
        return true;
    }
    

    关于复制的问题,ClipboardManager 的 setText 方法已经过时了,应该换用 setPrimaryClip 方法。

    private void doCopyLink() {
        try {
            ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clipData = ClipData.newPlainText("link", WebFrameSettings.instance.getUrl());
            cm.setPrimaryClip(clipData);
            Toast.makeText(getApplicationContext(), "复制链接成功", Toast.LENGTH_SHORT).show();
        } catch (Exception ex) {
            Toast.makeText(getApplicationContext(), "复制链接失败", Toast.LENGTH_SHORT).show();
        }
    }
    

    如果 webview 中需要 js 调用 java 方法,可以在单例添加对应的类,webview 在执行 loadUrl 之前会检查是否有对象值。

    HashMap<String, Object> invokeObjects = WebFrameSettings.instance.getObjs();
    if (invokeObjects.size() > 0) {
        Iterator iter = invokeObjects.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            String key = entry.getKey().toString();
            Object val = entry.getValue();
            webViewWebFrameModule.addJavascriptInterface(val, key);
        }
    }
    

    webview 的 onProgressChanged 和 onPageFinished 比较让人头疼,因为加载某个资源出错也会触发,所以只凭两个方法判断页面加载完成还是不科学,这就导致加载进度条的什么时候结束很让人纠结。

    整体效果:

    这个模块我已放到 github 上,戳 https://github.com/yahch/WebFrame

    也可以直接在项目引用:

    allprojects {
            repositories {
                ...
                maven { url 'https://jitpack.io' }
            }
    }
    
    dependencies {
                compile 'com.github.yahch:WebFrame:1.1'
     }
    
    Intent intent = new Intent(MainActivity.this, WebFrameActivity.class);
    WebFrameSettings.instance.setUrl("http://weibo.cn");
    startActivity(intent);
    

    相关文章

      网友评论

        本文标题:Android应用里的Webview控件嵌入

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