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

已标记关键词 清除标记
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页