XX程序媛学习笔记--Cordova(PhoneGap)Android Native混合开发值传递

本人只是菜鸡一枚,写博客只是为了做一下笔记,年纪大了。如果有说得不准确或者不好的地方请指出!!虚心请教大神指点。如果你觉得本文章太基础没什么意义,也给点掌声鼓励鼓励。

上一篇文章用cordova的plugin使得html可以调用Activity并且获取Activity回调的数据,前文连接:http://blog.csdn.net/tangjiarao/article/details/48288875。但是上一篇文章出现以下一些问题:


1、没有实现混合开发

上一篇文章加载html的页面是cordovaActivity,它继承Activity,且有一个CordovaWebView对象—appView,appView并不是一个真正的View,它是一个Interface。当cordovaActivity调用loadUrl()方法去加载页面的时候,其实是通过appView来加载。以下是CordovaActivity 的部分源码:

public class CordovaActivity extends Activity {
......

 protected CordovaWebView appView;

......


	
/**
 * Load the url into the webview.
 */
public void loadUrl(String url) {
    if (appView == null) {
        init();
    }

    // If keepRunning
    this.keepRunning = preferences.getBoolean("KeepRunning", true);

    appView.loadUrlIntoView(url, true);
}

CordovaWebView是一个接口,它不是一个View,它需要一个像webView一样的容器给它去显示html页面,所以需要通过一个显示容器去实例化它。其中的appView.getView();方法就是用来显示html的真实的View。以下是CordovaActivity 的部分源码:

protected void init() {
        appView = makeWebView();
        createViews();
        if (!appView.isInitialized()) {
            appView.init(cordovaInterface, pluginEntries, preferences);
        }
        cordovaInterface.onCordovaInit(appView.getPluginManager());

        // Wire the hardware volume controls to control media if desired.
        String volumePref = preferences.getString("DefaultVolumeStream", "");
        if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
            setVolumeControlStream(AudioManager.STREAM_MUSIC);
        }
    }

......

protected void createViews() {
        //Why are we setting a constant as the ID? This should be investigated
        appView.getView().setId(100);
        appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        setContentView(appView.getView());

        if (preferences.contains("BackgroundColor")) {
            int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
            // Background of activity:
            appView.getView().setBackgroundColor(backgroundColor);
        }

        appView.getView().requestFocusFromTouch();
    }

    /**
     * Construct the default web view object.
     *
     * Override this to customize the webview that is used.
     */
    protected CordovaWebView makeWebView() {
        return new CordovaWebViewImpl(makeWebViewEngine());
    }

    protected CordovaWebViewEngine makeWebViewEngine() {
        return CordovaWebViewImpl.createEngine(this, preferences);
    }

    protected CordovaInterfaceImpl makeCordovaInterface() {
        return new CordovaInterfaceImpl(this) {
            @Override
            public Object onMessage(String id, Object data) {
                // Plumb this to CordovaActivity.onMessage for backwards compatibility
                return CordovaActivity.this.onMessage(id, data);
            }
        };
    }
    
......

从以下代码看出,appView的大小已经是设定为全屏幕覆盖了,所有修改appView的大小和位置都很不方便,甚至与原生的控件结合都很难

<span style="font-size:10px;">ppView.getView().setLayoutParams(new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));</span>


那应该怎么解决?

2.java只是回调将值返回给html的js回调方法,但是却没有真正地主动去调用js方法。

3.定义插件的方法不规范


所以所以!!先来看看演示效果:

FirstActivity,包含了一个native按钮,还有一个html页面。在对话框输入内容,点击enter。



跳转到NAtive SecondActivity,并且显示信息,在输入框中输入内容,sendBack



SecondActivity Message下方的红色字体,就是从SecondActivity回调回来的



点击确认按钮,在Button Message下方显示的就是主动调用JS方法输出的内容




实现步骤:

(1)刚刚说到用CordovaActivity来结合原生很复杂,所以用cordova提供的一个控件——SystemWebView。它是继承WebView的,在CordovaActivity中的appView.getView();实际上返回的是它。由于它是继承webView的,它就相当于一个控件,你可以在xml中定义它,使用它,确定它的位置。以下是xml布局文件:

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

    <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="确定"/>
    <org.apache.cordova.engine.SystemWebView
        android:id="@+id/cordovaView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
      
</LinearLayout>

再来看看FirstActivity的实现:

public class FirstActivity extends Activity {
    /**
     * 一个加载网页的容器,继承WebView
     */
    private SystemWebView systemWebView;

    private CordovaWebView cordovaWebView;


    private ConfigXmlParser parser;

    private MyCordovaInterfaceImpl myCordovaInterface;

    private Button b;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);

        b =(Button)findViewById(R.id.bt1);

        systemWebView =(SystemWebView)findViewById(R.id.cordovaView);

        //为初始化CordovaWebView提供参数
        parser=new ConfigXmlParser();

        //这里会解析res/xml/config.xml配置文件
        parser.parse(this);

        //实例化CordovaWebView
        cordovaWebView= new CordovaWebViewImpl(new SystemWebViewEngine(systemWebView));

        //为初始化CordovaWebView提供参数,传入本Activity
        myCordovaInterface =new MyCordovaInterfaceImpl(this);

        //初始化CordovaWebView
        cordovaWebView.init(myCordovaInterface, parser.getPluginEntries(), parser.getPreferences());

        //执行加载动作
        cordovaWebView.loadUrl("file:///android_asset/www/InputData.html");


        b.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {

                //调用html的JS方法
                cordovaWebView.loadUrl("javascript:javaCallJs()");
            }
        });
    }

    /**
     *  为初始化CordovaWebView提供参数
     */
    private static class MyCordovaInterfaceImpl extends CordovaInterfaceImpl {

        private Activity mActivity;

        public MyCordovaInterfaceImpl(Activity activity) {

            super(activity);

            mActivity = activity;
        }
        public Activity getActivity() {

            return mActivity;
        }

     }

    /**
     * 接收从SecondActivity传来的数据,并且调用htmlJS方法来显示
     * @param requestCode
     * @param resultCode
     * @param intent
     */
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);

        switch (resultCode) {

            case Activity.RESULT_OK:

                Bundle b=intent.getExtras();
                String str=b.getString("flags");
                cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");

                break;
            default:
                break;
        }

    }

}

有没有觉得实例化CordovaWebView的方式很眼熟,其实就是把CordovaActivity实例化appView的方法抽离出来,再给它一个可以自定义修改位置的SystemWebView,那你就可以自由地放置它的位置


以下代码是主动调用html里面的JS方法,通过loadUrl(javascript:+jscode);方式,这个jscode可以是一个方法也可以是一串js代码。

b.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {

                //调用html的JS方法
                cordovaWebView.loadUrl("javascript:javaCallJs()");
            }
        });

onActivityResult()是用来接收从SecondActivity回传的信息,在上一篇文章里面,我们把它写在插件里面来接收回调,那现在为什么要把onActivityResult()方法写在FirstActivity呢?对于这个问题我也很烦恼,我试了很多次,查找了网上很多资料,看了源码又改过源码,但是SecondActivity回传的信息却一直返回到FirstActivity中,所以我唯有在FirstActivity中接收这个回调Intent,然后主动调用JS方法来达到修改html的效果。但是如果用cordovaActivity加载的时候却不会出现这个问题。(这个问题也因为我能力不够,而且也没有深切理解源码而无法解决,但是我会努力去跟进的!!)

好!反正我的解决方法是在FirstActivity里接收回调信息:
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);

        switch (resultCode) {

            case Activity.RESULT_OK:

                Bundle b=intent.getExtras();
                String str=b.getString("flags");
                cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");

                break;
            default:
                break;
        }

    }
InputData.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" />
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />

    <title>InputData</title>

    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" charset="utf-8">

    document.addEventListener("deviceready", onDeviceReady, true);

    function onDeviceReady(){
    //获取输入框和按钮对象
        var text1 = document.getElementById("name");
        var text2 = document.getElementById("Number");
        var text3 = document.getElementById("Age");
        var btn =document.getElementById("Enter");
        var output = document.getElementById("output");
        //给对象加上监听
        btn.addEventListener('click', onClick);

        function onClick(){
        //获取输入框值
            var Name =text1.value;
            var Number =text2.value;
            var Age =text3.value;
            var success = function(message) {
                alert("Success" + message);
                output.innerHTML = message;
            };
            var error = function(message) { alert("Oopsie! " + message); };

            //用插件调用值
            //dataTransportPlugins.createEvent(Name, Number, Age, success, error);
             navigator.dataTransportJs.demo(Name, Number, Age, success, error);
        }
    }
    </script>
    <script type="text/javascript" charset="utf-8">
        function javaCallBackJs(str){

            var output = document.getElementById("output2");
            output.innerHTML = str;
        }
        function javaCallJs(){

            var output = document.getElementById("output1");
            output.innerHTML = "this is from Button of FirstActivity"
        }
    </script>
</head>


<body>

<p>
    <label for="name"> Name: </label>
    <input type="text" name="input" id="name" value=""  />
</p>
<p>
    <label for="name"> Number: </label>
    <input type="text" name="input" id="Number" value="" />
</p>
<p>
    <label for="name"> Age: </label>
    <input type="text" name="input" id="Age" value="" />
</p>

<button id="Enter">Enter</button>
<p>
    Button Message : <div id="output1" style="color:red;"></div>
</p>
<p>
    SeconeActivity Message : <div id="output2" style="color:red;"></div>
</p>
</body>
</html>


下面两个JS方法分别是secondActivity回传时候调用的和按确认Button时候调用的方法:

<script type="text/javascript" charset="utf-8">
        function javaCallBackJs(str){

            var output = document.getElementById("output2");
            output.innerHTML = str;
        }
        function javaCallJs(){

            var output = document.getElementById("output1");
            output.innerHTML = "this is from Button of FirstActivity"
        }
    </script>

本例用到的插件类DataTransportPlugin 和 secondActivity代码跟上一篇文章是一样的,这里就不写出来了


最后就看看怎么规范的注册插件,在上一篇文章是通过暴露js接口来引入插件:

<script type="text/javascript" src="js/dataTransportJs.js"></script>
现在的方法是通过在assets/www/cordova_plugins.js引入定义的js文件

cordova.define('cordova/plugin_list', function(require, exports, module) {
module.exports = [
    {
        "file": "plugins/cordova-plugin-whitelist/whitelist.js",
        "id": "cordova-plugin-whitelist.whitelist",
        "runs": true
    },
    {
        "file": "plugins/cordova-plugin-whitelist/dataTransportJs.js",
        "id": "org.apache.cordova.dataTransportJs",
        "merges": [
                "navigator.dataTransportJs"
        ]
    },
];
module.exports.metadata = 
// TOP OF METADATA
{
    "cordova-plugin-whitelist": "1.0.0"
}
// BOTTOM OF METADATA
});
module.exports中第二个实体就是定义的js,其中:
file:是js插件接口的路径,统一都放在plugins文件夹下
id:是定义的一个id
merges:是在html中调用的插件(可以看html中调用的的语句)

dataTransportJs.js代码其实跟上一篇文章中的差不多,只是多了一些定义,如果你会node.js就很容易能理解。
dataTransportJs.js实现代码:
//定义一个模块
cordova.define("org.apache.cordova.dataTransportJs", function(require, exports, module) {

//加载模块
var exec = require('cordova/exec');  


module.exports = {  

    demo: function(Name, Number, Age, successCallback, errorCallback) {
        exec(
                    successCallback,
                    errorCallback,
                    'DataTransportPlugin',
                    'dataTransport',
                    [{
                        "Name": Name,
                        "Number": Number,
                        "Age": Age,
                    }]
                );
    },  
};  
});  
最后还是需要在config.xml中定义:
<feature name="DataTransportPlugin">
        <param name="android-package" value="ivy.cordova.example.plugins.DataTransportPlugin" />
    </feature>

看到这里你可能会有个疑问,既然只是用一个SystemWebView,那为什么要用cordova,直接在Activity里面用WebView就好了。是的!我也这么认为!所以到现在我都还没有体会到cordova的好处= =。等项目做完后应该就有结论了!
 
参考博客:
http://blog.csdn.net/aaawqqq/article/details/20480615

代码下载:
http://download.csdn.net/detail/tangjiarao/9095385

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值