浅谈跨平台移动应用开发AppCan多窗口机制

         AppCan官方网站:http://www.appcan.cn/

         AppCan最大的一个特点,就是其多窗口机制,笔者之前写过一篇关于AppCan UI框架的文字,主要是介绍如何使用UI2.0框架进行页面构建。这篇要阐述的是关于AppCan页面之间串联,通讯,交互等方面的内容。

         多窗口,其对应用底层代码,就是多个webview。AppCan引擎有个webview管理机制:多个webview是放到一个堆栈中的,并对其生命周期进行管理;以及何时创建新webview,何时释放webview等优化控制。为什么有多窗口的概念呢,大家试回忆客户端的使用体验,一个新闻列表页,用户用手指翻了几屏的内容,突然看到一条感兴趣的新闻,那么用户会点此条新闻,看具体内容。看完详情后,返回到列表内容时,应该保证其保留之前的状态,用户可以继续翻滚屏幕。在具体开发的时候,我们知道,最方便的写法是列表页是一个html页面,详情页又是一个html页面,这样,如果是单个webview去加载两个html页面,在页面切换时必然会重新刷新网页,如此就达不到上述所说的效果了。因此,跟浏览器的多标签页功能类似,AppCan引入了多webview的机制。下述截图是AppCan多窗口机制的demo:

 

      多窗口之间的通讯:

      这里的多窗口包括主窗口和浮动窗口,主窗口之上可以有多个浮动窗口。主窗口关闭后,其上所有浮动窗口也都会关闭。所有的窗口都是有名字的,在config.xml文件中配置的起始页所在的窗口名字为”root”,而其它窗口都是通过uexWindow.open打开的,需要定义一个唯一的名字;uexWindow.open 打开的是一个主窗口,浮动窗口则通过uexWindow.openPopover创建。一个主窗口上的多个浮动窗口名字是唯一的,但不同主窗口上的浮动窗口名字可以相同。浮动窗口可以有弹动效果,可以有数学变化:放大,旋转,移动等。给开发提供了一个创作空间,制作出体验好的效果。在应用的开发过程中,经常会涉及到窗口之间的通讯,比如从网络获取一个数据,根据返回的数据,让其它窗口执行相应的变化,这就需要用到窗口间通讯机制:

  • 主窗口之间通讯:uexWindow.evaluateScript(inWindowName,inType,inScript)
  • 主窗口与浮动窗口之间通讯:

    uexWindow.evaluateScript(inWindowName,inType,inScript)    

    uexWindow.evaluatePopoverScript(inWndName,inPopName,inScript)

       在上述窗口通讯方法中,最后一个参数是目标窗口的执行脚本:inScript,有开发者会问,这个脚本是否可以携带形参的内容,答案是可以的,但是有限制,一般少量的纯数字和常用字符串可以传递。如果是特殊字符和汉字,传输不过去。那么如何传输呢,一种做法是通过window.localStorage传递,在此方法中设置一个localStorage,在彼窗口中获取这个localStorage。

       窗口切换动画:

       客户端中的页面切换多少都会有动画效果,这在单webview中,div之前切换可以通过css去实现动画效果,当然这个效果会差些。而AppCan的多webview机制,可以有条件的把切换动画效果交由原生去实现,使用方法也很简单,只要配置uexWindow.open的第四个参数即可,其具体数字代表什么效果可在官网开发文档中找到。但是浮动窗口的动画却不是通过配置参数实现的,浮动窗口提供一套数学变换方法,需要再浮动窗口页面上自行写变换效果,由事件触发器去触发这些方法执行:uexWindow.makeTranslation、uexWindow.makeScale、uexWindow.makeRotate、uexWindow.makeAlpha,通过组合这些变换,能够写出更多的动画效果。

       浮动窗口:

       浮动窗口能够解决的事情很多,比如解决手机上不支持局部div滚动;解决oauth机制;解决上下拉刷新效果等问题。甚至解决在pad上布局的问题。

       在手机上,原生滚动是整个webview的行为,一般的css写滚动效果是:style="overflow:scroll;",但这个在手机webview上不支持。浮动窗口,作为叠加在主窗口之上的一块区域,其大小位置是可设定的,其背景初始也是透明的,因此,可以当做主窗口的一部分进行展示。

      很常见的手机上的加载数据的效果是下拉,或上拉刷新数据。这个原生实现比较简单,如果用js+css去实现,也是可以模拟的,但效果不佳。此时,浮动窗口如果可以实现上拉下拉,而且是用原生实现的,其效果会提升一个档次。而且通过uexWindow.setBounceParams方法,还可以设置上拉或下拉的参数配置:刷新箭头、不同状态下显示的文字等。

      还有一种很常见的设计是:点击左上角或右上角某个按钮,从侧边滑出一个菜单栏,即所谓的侧边菜单栏。这种效果也可以通过浮动窗口去实现:通过设定浮动窗口的显示位置,按钮点击后,浮动窗口执行横向滑动动画。

      在第三方开放平台中,基本上用的都是OAuth验证机制,比如新浪微博,腾讯微博,开发者在开发微博分享功能时,需要实现OAuth机制,一种是通过开放平台提供的sdk进行集成,这个可用AppCan的插件扩展机制实现。还有就是通过服务器去实现OAuth验证机制。但是,通过对OAuth机制的研究,AppCan还有更方便的方式去实现:uexWindow.onOAuthInfo(windowName,url)方法,是能够监听某一个窗口(windowName)的url变化的,即当监听的窗口url发生变化时,会触发uexWindow.onOAuthInfo方法。比如说,在主窗口中uexWindow.openPopover浮动窗口时,flag参数设为1,那么主窗口就能监听此浮动窗口的url变化:加载第三方开放平台授权界面,授权成功返回token值,token值过期时间等都能在url中体现,这就提供了一个实现OAuth验证的方式。

 

      开发者首先了解了AppCan的多窗口机制,那么进行app开发时,就能省去不少的麻烦事。常用的效果实现,页面布局的问题,数据交互的问题等,都可以利用多窗口机制去实现。说到数据交互,这里顺便提下客户端与服务器端的数据交互问题:

 

     客户端与服务器端数据交互:

      如果Hybrid App的框架是把html,css,js等资源文件放到本地,那么此种架构与服务器数据通讯就属于跨域请求。又由于Hybrid App一般用到浏览器引擎作为base,势必会联想到同源策略问题。然而,同源策略是浏览器的行为,而非浏览器引擎的行为,诸如Hybrid  App这种采用浏览器引擎为base的架构,其实是可以直接跨域请求。

      AppCan提供服务器数据交互接口: uexXmlHttpMgr,其模拟了form表单提交,可get/post提交,可用于文件和文字表单域同时上传:

    uexXmlHttpMgr.open(inXmlHttpID,inMethods,inUrl,inTimeOut)

    uexXmlHttpMgr.setPostData(inXmlHttpID,inDataType,inKey,inValue)

    uexXmlHttpMgr.send(inXmlHttpID)

    uexXmlHttpMgr.onData(inOpCode,inStatus,inResult)

    uexXmlHttpMgr.close(inXmlHttpID)

     我们还是拿写UI框架时的例子来说明,

     如果请求的url为:http://192.168.1.163/case/

     返回数据如下:

{
    "1": {
        "title": "惠特曼任职期被指时日不多:或被惠普解职",
        "time": "2012-11-30"
    },
    "2": {
        "title": "息称微软Surface平板销量不佳 订单已减半",
        "time": "2012-11-30"
    },
    "3": {
        "title": "iPad mini被指自相残杀:蚕食iPad销量 ",
        "time": "2012-11-30"
    },
    "4": {
        "title": "消息称微软Surface平板销量不佳 订单已减半",
        "time": "2012-11-30"
    },
    "5": {
        "title": "苹果推出iTunes 11:十年来最大更新 ",
        "time": "2012-11-30"
    }
}

  

       获取数据后,需要解析到界面中,其效果如:

代码如何实现呢?以下是具体的包括请求,数据返回进行解析,并拼装到页面中的代码:

var tmp = '<ul ontouchstart="zy_touch(\'btn-act\')" class="${first:"uc-t"} ${last:"uc-b"} ub ubb b-gra c-m2 t-bla ub-ac lis">'+
                    '<li class="ui-icon-link"></li>'+
                    '<ul class="ub-f1 ub ub-ver" style="margin-left:0.5em;">'+
                       ' <li class="ulev1">${title} </li>'+
                        '<ul class="ub ub-ac t-gra ulev-2">'+
                           ' <li class="ub-f4"></li>'+
                            '<li class="ub-f1">${time}</li>'+
                        '</ul>'+
                    '</ul>'+
                '</ul>';
var url = "http://192.168.1.163/case/";
function getdata(){
uexXmlHttpMgr.open("1","get",url,6000);
uexXmlHttpMgr.onData=function(inOpCode,inStatus,inResult){
if(inStatus==1){
var json = JSON.parse(inResult);
var html = zy_tmpl(tmp,json,zy_tmpl_count(json),null);
$$("lisid").innerHTML = html;
}
}
uexXmlHttpMgr.send("1");
} 

  

posted @ 2012-12-26 09:22  新哥  阅读(3214)  评论(1编辑  收藏  举报