Android中WebView页面交互

标签: android | 发表时间:2015-12-15 09:58 | 作者:暗黑物质
出处:http://segmentfault.com/blogs

代码

在android内打开一个网页的时候,有时我们会要求与网页有一些交互。而这些交互是在基于javaScript的基础上。那么我们来学习一下android如何与网页进行JS交互。完整代码如下:

  import android.annotation.SuppressLint;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.JavascriptInterface;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import org.json.JSONObject;

/**
 * 软件内通用打开网页的容器页面
 *
 * @author ZRP
 */
public class CommonWebActivity extends BaseActivity {

    protected CustomFrameLayout customFrameLayout;
    protected TextView errorTxt;
    protected View refresh;// 刷新按钮
    protected WebView webView;

    protected String url = "";// 网址url
    protected String param = "";// 交互参数,如json字符串

    protected WebChromeClient chromeClient = new WebChromeClient() {
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                // 判断有无网络
                if (!NetUtil.isAvaliable()) {
                    customFrameLayout.show(R.id.common_net_error);
                    refresh.setVisibility(View.VISIBLE);
                    refresh.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            webView.loadUrl(url);
                        }
                    });
                } else {
                    // 判断网络请求网址是否有效
                    if (!URLUtil.isValidUrl(url)) {
                        customFrameLayout.show(R.id.common_net_error);
                        errorTxt.setText("无效网址");
                    } else {
                        customFrameLayout.show(R.id.common_web);
                    }
                }
            }
        }

        // 获取到url打开页面的标题
        public void onReceivedTitle(WebView view, String title) {
            setBackView(R.id.back_view, title);
        }

        // js交互提示
        public boolean onJsAlert(WebView view, String url, String message, android.webkit.JsResult result){
            return super.onJsAlert(view, url, message, result);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        url = getIntent().getStringExtra("url");
        param = getIntent().getStringExtra("param");

        setContentView(R.layout.common_web_activity);
        initView();
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void initView() {
        setBackView(R.id.back);

        customFrameLayout = (CustomFrameLayout) findViewById(R.id.web_fram);
        customFrameLayout.setList(new int[]{R.id.common_web, R.id.common_net_error, R.id.common_loading});
        customFrameLayout.show(R.id.common_loading);
        refresh = findViewById(R.id.error_btn);
        errorTxt = (TextView) findViewById(R.id.error_txt);
        webView = (WebView) findViewById(R.id.common_web);

        webView.getSettings().setDefaultTextEncodingName("utf-8");
        webView.getSettings().setJavaScriptEnabled(true);
        synCookies();//格式化写入cookie,需写在setJavaScriptEnabled之后
        webView.setWebChromeClient(chromeClient);
        webView.setWebViewClient(new WebViewClient() {// 让webView内的链接在当前页打开,不调用系统浏览器
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        webView.addJavascriptInterface(new JavaScriptInterface(), "zrp");
        webView.loadUrl(url);
    }

    /**
     * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
     * 需要在当前用户退出登录的时候进行清除
     */
    private void synCookies() {
    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        CookieManager.getInstance().setCookie(url, split[i]);
    }
   }

    /**
     * 回调网页中的脚本接口。
     *
     * @param notify 传给网页的通知内容。
     */
    public void onNotifyListener(String notify) {
        if (webView != null) {
            webView.loadUrl("javascript:onCommandListener('" + notify + "')");
        }
    }

    /**
     * android js交互实现
     * String ret = zrp.command("");
     * <p/>
     * webView.loadUrl("javascript:onCommandListener('param')");
     */
    public class JavaScriptInterface {

        @JavascriptInterface
        public void command(String jsonString) {
            if (TextUtils.isEmpty(jsonString)) {
                return ;
            }

            //根据网页交互回传的json串进行操作

        }
    }
}

其中CustomFrameLayout为界面切换控件,分别在无网络,网址错误等情况下进行提示:

  import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

/**
 * 用于状态切换的布局,只显示1个状态
 */
public class CustomFrameLayout extends FrameLayout {
    private int[] list;

    public CustomFrameLayout(Context context) {
        super(context);
        initView();
    }

    public CustomFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    /**
     * 设置子面板id数组
     *
     * @param list
     */
    public void setList(int[] list) {
        this.list = list;
        show(0);
    }

    /**
     * 显示某个面板
     *
     * @param id
     */
    public void show(int id) {
        if (list == null) {
            for (int i = 0; i < getChildCount(); ++i) {
                View view = getChildAt(i);

                if (id == view.getId()) {
                    view.setVisibility(View.VISIBLE);
                } else {
                    view.setVisibility(View.GONE);
                }
            }

            return;
        }

        for (int aList : list) {
            View item = findViewById(aList);
            if (item == null) {
                continue;
            }
            if (aList == id) {
                item.setVisibility(View.VISIBLE);
            } else {
                item.setVisibility(View.GONE);
            }
        }
    }

    /**
     * 隐藏所有面板
     */
    public void GoneAll() {
        if (list == null) {
            for (int i = 0; i < getChildCount(); ++i) {
                View view = getChildAt(i);

                view.setVisibility(View.GONE);
            }

            return;
        }

        for (int aList : list) {
            View item = findViewById(aList);
            if (item == null) {
                continue;
            }
            item.setVisibility(View.GONE);
        }
    }

    /**
     * 切换。
     * @param index
     */
    public void showOfIndex(int index) {
        for (int i = 0; i < getChildCount(); ++i) {
            View view = getChildAt(i);

            if (index == i) {
                view.setVisibility(View.VISIBLE);
            } else {
                view.setVisibility(View.GONE);
            }
        }
    }

    public void initView() {

    }
}

界面布局为:

  <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include layout="@layout/common_back" />

    <com.zrp.test.CustomFrameLayout
        android:id="@+id/web_fram"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

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

        <include layout="@layout/common_net_error" />

        <include layout="@layout/common_loading" />
    </com.zrp.test.CustomFrameLayout>

</LinearLayout>

测试分析

好了,这时候我们来添加一些数据进行测试:

用上面的方法为web页面中的param赋值,在asset文件夹中放入要进行测试的html文件,url赋值为: url = "file:///android_asset/html.html";
如上html.html文件源码为:

  <!DOCTYPE html>
<html>
<head>
    <title>测试容器调用</title>
</head>
<body>

<div class="main">
<button onclick="test2()">调用容器方法</button>
</div>

<script type="text/javascript">
   function test(){
       alert("容器调用web方法");
   }
    function test2(){
        alert("test2() begin");
        zrp.command("{'type':1,'text':'hello boy'}");
        alert("test2() end");
    }
</script>

</body>
</html>

java调用js:可通过在java代码中异步执行 webView.loadUrl("javascript:test()");来调用网页代码中的test()方法。

js调用java:在网页代码中可以看到button的点击事件添加了 zrp.command("{'type':1,'text':'hello boy'}");来回传一个json串给java页面,即js通过以上的方法调用了java的方法。

如果webView展示页面的时候需要给网页传递cookie,即各种登录信息,则需要在调用webView.loadUrl(url)之前一句调用如下方法: 注意!多个cookie值必须多次进行setCookie!

设置cookie方法一:

  /**
 * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
 * 需要在当前用户退出登录的时候进行清除
 */
private void synCookies() {
    CookieSyncManager.createInstance(this);
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true);
    cookieManager.removeSessionCookie();

    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        cookieManager.setCookie(url, split[i]);
    }
    CookieSyncManager.getInstance().sync();
}

在当前用户退出登录的时候清除掉保存的cookie信息。

  /**
 * 清除当前用户存储到cookies表中的所有数据
 */
private void removeCookie() {
    CookieSyncManager.createInstance(this);
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.removeAllCookie();
    CookieSyncManager.getInstance().sync();
}

注意: 在调用设置Cookie之后不能再设置如下的这类属性,否则设置cookie无效。

  webView.getSettings().setBuiltInZoomControls(true);  
webView.getSettings().setJavaScriptEnabled(true);  

但是!在如上设置cookie之后,每次设置cookie的时候都有延时,要是点击频率较快的时候,会出现cookie没有被设置进去的问题!

设置方法二:
经过一番查找,找到如下两个解决办法:

  1. http://blog.csdn.net/swust_chenpeng/article/details/37699841,这个是开了一个异步任务来进行等待,然后再次设置cookie,感觉没有从根源上解决这个问题啊。。。但是思路值得参考

  2. http://stackoverflow.com/questions/22637409/android-how-to-pass-cookie-to-load-url-with-webview,有用!设置之后立竿见影。

  /**
 * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
 * 需要在当前用户退出登录的时候进行清除
 */
private void synCookies() {
    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        CookieManager.getInstance().setCookie(url, split[i]);
    }
}

同时,在退出登录的时候清除cookie, CookieManager.getInstance().removeAllCookie();,清除当前用户存储到cookies表中的所有数据

相关 [android webview 交互] 推荐:

Android中WebView页面交互

- - SegmentFault 最新的文章
在android内打开一个网页的时候,有时我们会要求与网页有一些交互. 而这些交互是在基于javaScript的基础上. 那么我们来学习一下android如何与网页进行JS交互. protected View refresh;// 刷新按钮. protected String url = "";// 网址url.

WebView JS 交互

- - ITeye博客
WebView加jquery做页面会怎么样呢. // 创建WebView对象. // 把programList添加到js的全局对象window中,. // 这样就可以使用window.programList来获取数据. * 定义js回调java函数. // 绑定键盘的向上,向下按钮事件触发相应的js事件.

Android WebView 用法

- - 移动开发 - ITeye博客
Android和iOS系统都提供了标准的浏览器控件,在Android中是WebView,iOS中为UIWebView. 在iOS中你实例化一 个UIWebView即可调用loadRequest来加载一个网页,但是在Android中你不仅需要创建一个WebView,还需要做一些其他的事 情,建议初次使用WebView的读者按照以下步骤使用:.

android 使用html5作布局文件: webview跟javascript交互

- - 移动开发 - ITeye博客
android 使用html5作布局文件 http://www.cnblogs.com/andgoo/archive/2011/10/11/2207936.html. Android中webview跟JAVASCRIPT中的交互 http://www.oschina.net/question/4873_27156.

Android WebView的坑总结

- - CSDN博客推荐文章
       最近把做好的iPad HybridApp向Android迁移,碰到的坑太多了,让我这个折腾过Android接近4年的老鸟都头疼. 现在把前人遇到的都列出来,再慢慢解决自己的,目前已经解决了android键盘覆盖问题,下面最棘手的问题就是屏幕高度的适配问题了. 1、 Andrid4.1事件穿透BUG 原因不明.

Android: 在WebView中获取网页源码

- - ITeye博客
 使能javascript:. 编写自己的WebViewClient,并在onPageFinished中提取网页源码. 运行,可以看到在showSource(String html)中打印了网页源码. 已有 0 人发表留言,猛击->> 这里<<-参与讨论. —软件人才免语言低担保 赴美带薪读研.

Android WebView中的JavaScript代码使用

- - 博客园_首页
  上一篇博文: Android WebView使用基础已经说了一些Android中WebView的基本使用.   本篇文章主要介绍WebView中的JavaScript代码的执行相关,已经JS代码与Android代码的互相调用.   (因为本人对Web开发并不是很熟悉,所以如果有哪些地方说得不对,还请指正.

Android WebView 常见的九个问题

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 目前Android WebView问题越来越多,接下来由爱内测(www.ineice.com)的技术工程师为我们介绍几种常见的Android WebView问题:. 1.为WebView自定义错误显示界面: /** * 显示自定义错误提示页面,用一个View覆盖在WebView */ protected void showErrorPage() { LinearLayout webParentView = (LinearLayout)mWebView.getParent();.

[Android]用WebView访问证书有问题的SSL网页

- - idv2
在WebView里加载SSL网页很正常,也没什么难度. 但如果要加载的SSL页面的证书有问题,比如过期、信息不正确、发行机关不被信任等,WebView就会拒绝加载该网页. PC上的浏览器会弹出证书错误的对话框,提示你是否要无视错误继续浏览. 实际上在WebView里也可以这样做,以实现加载证书有问题的页面.

android WebView, WebChromeClient和WebViewClient加载网页基本用法

- - CSDN博客推荐文章
WebView, WebChromeClient和WebViewClient加载网页基本用法.  webview是android中的浏览器控件,在一些手机应用中常会用到b/s模式去开发应用,这时webview的强大功能就会显示出来.  1、webview的几点设置.  权限设置:.AndroidManifest.xml中必须使用许可"android.permission.INTERNET",否则会出Web page not available错误.