jQueryMobile设计Android通讯录(第二章)

本文是jQuery Mobile设计Android通讯录系统教程的第二篇,在上一篇教程中,初步介绍了我们要设计的应用的架构和页面结构,并介绍了Jquery Mobile框架中重要的页面元素的知识,以及Android Java应用程序如何跟前端的JavaScript页面进行交互。在本系列教程的第二篇,将介绍如何创建新的通讯录帐号及如何修改和删除已经存在的通讯录名单。

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都阳光房小微创业公司专业提供成都企业网站定制营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

创建通讯录帐号

下面来看下如何新创建通讯录帐号。用户只需要输入自己的姓名,点保存按钮。其中该部分的代码是在ListPage.html中可以找到,代码如下:

 
 
 
 
  1.  
  2.   ... 
  3.  
  4.  
  5.  
  6.   ... 
  7.    
  8.    
  9.     

    Create Account

     
  10.   
 
  •    
  •     
  •  
  •     

    Please enter name of the new account for this application

  •  
  •     Contacts created with this application will be associated with the new account specified below. 
  •     Other contacts can be viewed, however, cannot be deleted or modified with this application. 
  •      
  •        
  •      
  •      
  •       
  •         data-inline="true">Save 
  •      
  •     ... 
  •    
  •    
  •  
  •   
  • ... 
  •  
  • ◆我们把创建帐号这个页面放在页面容器中,这个页面有自己的头部,内容content部分和页脚部分。

    ◆当点SAVE按钮时,将会调用Javasccript中的createAccount()方法。

    ◆在Javasccript中的createAccount()方法中,获得用户的输入的帐号名,即使用

    '#accountName').val()获得其值,然后通过调用后端Android Java应用中的createAccount方法去保存帐户名。跟后端Android Java的交互,在本系列的第一篇教程中有提到,如果不大清楚,请查看第一篇教程。

    下面看下后端的Android Java应用中的createAccount方法如何编写。

     
     
     
     
    1. import android.accounts.AccountManager; 
    2. import android.accounts.Account; 
    3. ... 
    4. public class ContactsActivity extends Activity { 
    5.   ... 
    6.   private String accountType = null; 
    7.   ... 
    8.   public void onCreate(Bundle savedInstanceState) { 
    9.     ... 
    10.     accountType = "com.jquerymobile.demo.contact"; 
    11.     ... 
    12.   } 
    13.  
    14.   public void createAccount(String accountN, String displayPage){ 
    15.     if(accountN != null && !"".equals(accountN)){ 
    16.       accountNaccountName = accountN; 
    17.       AccountManager.get(this).addAccountExplicitly(new Account(accountName,accountType), "dummyPassword", null); 
    18.     } 
    19.     loadPage(displayPage); 
    20.   } 
    21.   ... 

    下面讲解下这段代码:

    ◆实际上帐号的创建是通过android.accounts.AccountManager类去创建的。Android2.0中加入了一个新的包android.accounts,该包主要包括了集中式的账户管理API,用以安全地存储和访问认证的令牌和密码,比如,我们的手机存在多个账户,每个账户下面都有不同的信息,甚至每个账户都可以与不同的服务器之间进行数据同步(例如,手机账户中的联系人可以是一个Gmail账户中的通讯录,可联网进行同步更新)。

    这里首先通过AccountManager.get()获得了它的一个实例,接着调用其addAccountExplicitly方法,创建了一个新的帐号,和密码(这里的密码默认是dummyPassword),当帐号创建完后,将回调HTML页面,这里通过loadPage方法去加载回调HTML页面。

    ◆为了能调用Android API中的创建帐号的功能,必须在AndroidManifest.xml中进行如下设置,声明一个intent-filter:

     
     
     
     
    1.  
    2.    
    3.  

    ◆除此之外,必须在meta-data中声明帐号验证器如下:

     
     
     
     
    1.   android:name="android.accounts.AccountAuthenticator" 
    2.   android:resource="@xml/authenticator" /> 
    Finally, the res/xml/authenticator.xml configuration file (which is value of the android:resourceattribute above) should have an element named account-authenticator where value ofandroid:accountType attribute is set to com.jquerymobile.demo.contact. Note that this is value of theaccountType in ContactsActivity. Summarizing the discussion, first look at the highlighted section inAndroidManifest.xml.

    这里,用android:resource的值,指出了要在res/xml下配置一个验证配置文件authenticator.xml,文件如下:

     
     
     
     
    1.  
    2.   android:accountType="com.jquerymobile.demo.contact" 
    3.   android:icon="@drawable/icon" 
    4.   android:smallIcon="@drawable/icon" 
    5.   android:label="@string/app_name" 
    6. /> 

    其中的android:accountType指出了要验证的帐号的实体类为com.jquerymobile.demo.contact。最后我们综合看下修改后的AndroidManifest.xml如下:

     
     
     
     
    1.  
    2.   package="com.jquerymobile.demo.contact" 
    3.   android:versionCode="1" 
    4.   android:versionName="1.0"> 
    5.      
    6.      
    7.      
    8.      
    9.     
    10.       android:label="@string/app_name"> 
    11.         
    12.           android:name=".authentication.AuthenticationService" 
    13.           android:exported="true"> 
    14.            
    15.              
    16.              
    17.             
    18.               android:resource="@xml/authenticator" /> 
    19.          
    20.         
    21.           android:configChanges="orientation|keyboardHidden" 
    22.           android:label="@string/app_name"> 
    23.              
    24.                
    25.                
    26.              
    27.          
    28.      
    29.    

    在文件中用uses-permission分别设置了帐号的读写或校验的权限,并且声明了帐号的管理服务实现类为com.jquerymobile.demo.contact. authentication.AuthenticationService,在这个类中,将编写一些关于帐号管理操作的业务逻辑。这个帐号业务管理器被定义为Android中的一个服务,代码如下:

     
     
     
     
    1. package com.jquerymobile.demo.contact.authentication; 
    2.  
    3. import android.app.Service; 
    4. import android.content.Intent; 
    5. import android.os.IBinder; 
    6.  
    7. public class AuthenticationService extends Service { 
    8.   public IBinder onBind(Intent intent) { 
    9.     return null; 
    10.   } 

     

    可以看到,这里跟普通的编写Android服务没什么大的区别,这里在onBind()中并没有编写具体的代码,只是返回了null。但如果你需要将创建的帐号跟一些在线服务结合起来,那么则需要在这里编写相关的实现代码,并且还需要实现android.accounts.AbstractAccountAuthenticator接口。这里我们足够用了,所以返回null。现在,运行程序,在输入帐号并保存后,你会发现在Android 手机中的Accounts and sync settings的选项设置中,会发现你刚才新建的帐号,如下图所示:

    #p#

     列出已存在的通讯录

    下面,我们看下,当用户点某个人名的通讯录时,会显示其通讯录的详细情况。先回顾下在本系列教程第一篇中,提到的如下代码:

     
     
     
     
    1. function setContactsList(jsonText){ 
    2.     var tmpJson = $.parseJSON(jsonText); 
    3.     if(tmpJson != null && tmpJson.contacts != null){ 
    4.       var tmpContacts = tmpJson.contacts; 
    5.       for(i = 0; i < tmpContacts.length; i++){ 
    6.         var tmpKey = (tmpContacts[i]).key; 
    7.         var tmpKeyFragment = ''+tmpKey+'
    8. '; 
    9.         contactSelectionsVar.append(tmpKeyFragment); 
    10.         var tmpValues = (tmpContacts[i]).values; 
    11.         if(tmpValues != null){ 
    12.           var j; 
    13.           for(j = 0; j < tmpValues.length; j++){ 
    14.             var tmpDisplayName = tmpValues[j].displayName; 
    15.             var tmpContactId = tmpValues[j].contactId; 
    16.             var tmpLiFragment = '
    17.               tmpContactId + ');return false;">'+tmpDisplayName+'
    18. '; 
    19.             contactSelectionsVar.append(tmpLiFragment); 
    20.           } 
    21.         } 
    22.       } 
    23.     } 
    24.     contactSelectionsVar.listview('refresh'); 
    25.     showList(); 
    26.   } 

    这里,通过JavaScript去调用了showContact方法,用于显示某个人的具体通讯录,代码如下:

     
     
     
     
    1. function showContact(tmpId){ 
    2.   showProgress(); 
    3.   contactSupport.showContact(tmpId,'DetailPage.html'); 

    这里,首先调用了等待图标的显示方法,然后同样调用后端Android Java的showContact方法调出通讯录的详细信息,然后再把结果回调显示到DetailPage.html中而后端Java对应的showContact方法如下:

     
     
     
     
    1. public void showContact(String contactId, String displayPage){ 
    2.   loadPage(displayPage + "?" + contactId); 

    这里再通过loadPage调用前端的JavaScirpt页面,并传入要查看的通讯录的参数contactId,假如现在要看的某人的通讯录的contatId是23,而回显通讯录详细信息的页面是DetailPage.html,则跳转的URL为DetailPage.html?23#p#

    通讯录详细信息页

    通讯录详细信息页Detail.html页面代码如下,它分为三个部分,分别是显示已存在的详细通讯录信息页,空白的用于新建显示录入通讯录的信息页以及在删除通讯录时显示的界面,先来看显示已存在的详细通讯录信息页的代码如下:

     
     
     
     
    1.  
    2.    
    3.    
    4.     

       Details

       
    5.    
    6.  
    7.    
    8.      
    9.         
    10.  
    11.        
    12.          
    13.           
    14.  
    15.             
    16.  
    17.               
    18.  
    19.               
    20.  
    21.             
    22.  
    23.             
    24.  
    25.               
    26.  
    27.               
    28.  
    29.             
    30.  
    31.             
    32.  
    33.               
    34.  
    35.               
    36.  
    37.             
    38.  
    39.           
    40.  
    41.         
    42. First name
      Last
       
    43.                 name
    44. Notes
       
    45.        
    46.  
    47.        
    48.         

      Phone Numbers

       
    49.          
    50.        

    注意如下几点:

    ◆这里的contactId是一个隐藏域,记录当前查看或修改的通讯录的ID号,在更新时必须用到。

    ◆这里的电话号码是一个可以展开输入的伸缩面板,使用的是jQuery Mobile框架中的可伸缩区块的方法。这里,为方便用户输入电话提供了更多的输入选项,当用户点“Phone Number”时,会下拉显示出更多的四种不同的通讯方式以供用户输入。只需要在代码中写入data-role="collapsible" data-collapsed="true"两个jQuery Mobile的属性即可。实际效果如下图:

    我们继续来看,剩下的象EMAIL及“更多”部分,都同样使用了jQuery Mobile中的可收缩区域的技术去实现,具体代码请参考附件,这里不再列出,下面是其中的几个截图如下:

    最后是显示三个新增,修改及删除的按钮及页面的底部,代码如下:

     
     
     
     
    1.  
    2.       
      Save
       
    3.       
      Delete
       
    4.       
      Cancel
       
    5.      
    6.    
    7.  
    8.    
    9.    
    10. ... 
    11.   

     

    ◆注意这里使用了data-role="controlgroup"这一jQuery Mobile提供的属性,将三个按钮都以同一分组的形式放在同一个区域,而data-type="horizontal" 则表示以垂直的方式分组将按钮进行摆放设置。

    ◆三个按钮都通过Javascript实现触发。

    接下来是显示进度条等待的页面,代码如下,比较简单,不详细论述。

     
     
     
     
    1.  
    2.   

      Processing...

       
    3.  
    4.  
    5.  
    6.   

      Please wait.

       
    7.    
    8.      
    9.    
    10.  
    11.  
    12.  

    当用户要删除某条通讯录时,会出现新的页面,以询问用户是否确认删除,代码如下:

     
     
     
     
    1.  
    2.   

      Confirm delete

       
    3.  
    4.  
    5.  
    6.    
    7.     

      Are you sure you want to delete this contact?

       
    8.    
    9.    
    10.     
      Delete
       
    11.     
      Cancel
       
    12.    
    13.  
    14.  
    15.  

    #p#

    用JavaScript控制页面的显示和隐藏

    在本教程的第一讲中,已经讲解了如何通过jQuery及Javscript,控制一个页面容器中各个容器子页的显示和隐藏,这里只是简单提到复习下,详细的请参考第一篇教程。我们可以在jQuery的ready()方法中,定义一系列的变量,分别指代页面容器中各子页的头部,内容部分和页脚部分,然后由于各个部分其实都是div层的结构,所以显示时只需要调用show方法即可,隐藏时调用hide方法即可,下面是部分代码,具体代码请参考下载附件:

     
     
     
     
    1.  

    显示已存在的数据记录

    Having reviewed the structure of content pages in DetailPage.html, let us look into how to populate the contact details for an existing contact. Recall that the ContactsActivity.showContact() method displays DetailPage.html appending id of the contact as an HTTP query string, e.g. DetailPage.html?23. Let us see below how JavaScript code in DetailPage.html will process that information. The related section in jQuery $(document).ready() function is given below.

    在知道了页面结构后,我们现在看下,如何将后端的数据显示在前端的界面中,这样当用户点选一个已存在的通讯录时,会把该通讯录的详细信息显示出来。我们看下之前说的,举例说到的DetailPage.html?23这样方式的链接,看下在DetailPage.html中,是如何用Javascript去读取后端的数据的,关键是看jQuery中的ready()方法中处理的代码,如下:

     
     
     
     
    1. $(document).ready(function () { 
    2.   ... 
    3.   showProgress(); 
    4.   contactIdVar.val(window.location.search.substring(1)); 
    5.   contactSupport.getContact(contactIdVar.val(),'setCurrentContact'); 
    6. }); 

    ◆首先是加载了进度等待图标

    ◆然后用window.location.search.substring(1)获得了当前链接的中?号后的参数,比如对于DetailPage.html?23这个例子来说,获得了23的值,并且赋值给 contactIdVar.val这个变量。

    ◆最后,通过调用后端JAVA应用的ContactsActivity.getContact()方法,传入的是两个参数,一个是当前通讯录的id,另外的setCurrentContact是回调前端显示处理结果的Javascript方法。下面看下ContactsActivity.getContact()方法的实现,如下:

     
     
     
     
    1. public void getContact(String contactId, String contactCallback){ 
    2.   String json = ContactUtility.getContactJSON(contactId, ...); 
    3.   final String callbackFunction = "javascript:" + contactCallback + "('" + json + "')"; 
    4.   loadURL(callbackFunction); 

    这里,通过getContactJSON方法,产生了JSON格式的数据。下面看下如何产生JSON格式的数据。下面是应用中模拟生成的JSON代码,代码如下:

     
     
     
     
    1.   "contactId":"265", 
    2.   "firstName":"Aafjes", 
    3.   "lastName":"Bertus", 
    4.   "note":{"rowId":"2265","text":"Author"}, 
    5.   "ims":[ 
    6.     {"rowId":"2274","protocol":"-1","value":""}, 
    7.     {"rowId":"2275","protocol":"0","value":"bertus@aim"}, 
    8.     {"rowId":"2276","protocol":"5","value":"bertus@google"}, 
    9.     {"rowId":"2277","protocol":"6","value":""}, 
    10.    。 
    11.   ], 
    12.   "phones":[ 
    13.     {"rowId":"2284","type":"1","no":"111-222-3333"}, 
    14.     {"rowId":"2285","type":"2","no":"222-000-9999"}, 
    15.     {"rowId":"2286","type":"3","no":"444-787-9900"}, 
    16.     {"rowId":"2287","type":"7","no":"555-744-9999"} 
    17.   ], 
    18.   "emails":[ 
    19.     {"rowId":"2271","type":"1","value":"bertus@home.xyz"}, 
    20.     {"rowId":"2272","type":"2","value":"bertus@work.xyz"}, 
    21.     {"rowId":"2273","type":"3","value":"bertus@other.xyz"} 
    22.   ], 
    23.   "organizations":[ 
    24.     {"rowId":"2269","type":"1","name":"Publications Inc.","title":"CEO"}, 
    25.     {"rowId":"2270","type":"2","name":"Volunteers Corp.","title":"Member"} 
    26.   ], 
    27.   "addresses":[ 
    28.     {"rowId":"2266","type":"1","street":"Alhambra st.","city":"Alhambra","state":"MI", 
    29.       "country":"USA","zip":"48100","poBox":""}, 
    30.     {"rowId":"2267","type":"2","street":"1 Corporation st","city":"Alhambra","state":"MI", 
    31.       "country":"USA","zip":"48000","poBox":"44456"}, 
    32.     {"rowId":"2268","type":"3","street":"","city":"","state":"", 
    33.       "country":"","zip":"","poBox":""} 
    34.   ] 

    以上的JSON中,contactId, firstName, lastName都是字符串类型,而address,email,phones等都是每个JSON对象中包含了多个对象,形成一个JSON数组。

    ◆注意在以上的对象中,都有一个rowId,它都是不会重复的,用以区别不同的记录,也方便在前端Javascript代码中进行处理。

    ◆在ims数组中,有protocol属性,它代表是用户使用哪种即时通讯工具,这是由Android 通讯录 API给出的定义,定义如下:

    o protocol=-1, 自定义

    o protocol=0, AIM

    o protocol=1, MSN

    o protocol=2, Yahoo

    o protocol=3, Skype

    o protocol=4, QQ

    o protocol=5, Google

    o protocol=6, ICQ

    o protocol=7, Jabber

    ◆同样,在phones数组中的type属性,也是Android 通讯录 API给出的定义,如下:

    o type=1, Home

    o type=2, Mobile

    o type=3, Work

    o type=7, Other

    ◆emails数组中的type属性,API定义如下:

    o type=1, Home

    o type=2, Work

    o type=3, Other

    ◆organizations 数组中的type属性定义如下:

    o type=1, Work

    o type=2, Other

    ◆addresses 数组中的type属性定义如下:

    o type=1, Home

    o type=2, Work

    o type=3, Other#p#

    在JavaScript中解析JSON

    下面我们看下如何在Javascript中对JSON进行解析。

    定义常量

    我们为了设计方便,在Javscirpt中先定义一些常量,以方便代码的书写和阅读,代码如下: