jQuery Mobile设计Android通讯录(第二章)

移动开发 移动应用 Android
本文是jQuery Mobile设计Android通讯录系统教程的第二章,在本文中,我们将介绍如何创建新的通讯录帐号及如何修改和删除已经存在的通讯录名单。

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

创建通讯录帐号

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

  1. <html> 
  2.   ... 
  3. <body> 
  4. <!-- Container Page --> 
  5. <div data-role="page" data-theme="c" id="containerPage"> 
  6.   ... 
  7.   <!--  Create Account --> 
  8.   <div data-role="header" id="hdrAccount" data-nobackbtn="true"  data-theme="c"> 
  9.     <h1>Create Account</h1> 
  10.   </div> 
  11.   <div data-role="content" id="contentAccount"  data-theme="c"> 
  12.     <div align="CENTER"><img src="img/contacts-master-bgd.png"></div> 
  13.     <div align="CENTER"><h4>Please enter name of the new account for this application</h4></div> 
  14.     <div align="CENTER">Contacts created with this application will be associated with the new account specified below. 
  15.     Other contacts can be viewed, however, cannot be deleted or modified with this application.</div> 
  16.     <div align="CENTER" id="accountDiv" data-role="fieldcontain"> 
  17.       <input id="accountName" type="text" /> 
  18.     </div> 
  19.     <div align="CENTER"> 
  20.       <a href="javascript:createAccount();return false;" data-role="button" 
  21.         data-inline="true">Save</a> 
  22.     </div> 
  23.     ... 
  24.   </div> 
  25.   <div data-role="footer" id="ftrAccount"  data-theme="c"></div> 
  26.  
  27. </div> <!-- Container Page Ends Here --> 
  28. ... 
  29. <script> 
  30. ... 
  31.   function createAccount(){ 
  32.     showProgress(); 
  33.     contactSupport.createAccount($('#accountName').val(),'ListPage.html'); 
  34.   } 
  35.   ... 
  36. </script> 

◆我们把创建帐号这个页面放在页面容器中,这个页面有自己的头部,内容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. <intent-filter> 
  2.   <action android:name="android.accounts.AccountAuthenticator"/> 
  3. </intent-filter> 

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

  1. <meta-data 
  2.   android:name="android.accounts.AccountAuthenticator" 
  3.   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. <?xml version="1.0" encoding="utf-8"?> 
  2. <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" 
  3.   android:accountType="com.jquerymobile.demo.contact" 
  4.   android:icon="@drawable/icon" 
  5.   android:smallIcon="@drawable/icon" 
  6.   android:label="@string/app_name" 
  7. /> 

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

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
  3.   package="com.jquerymobile.demo.contact" 
  4.   android:versionCode="1" 
  5.   android:versionName="1.0"> 
  6.     <uses-permission android:name="android.permission.READ_CONTACTS"/> 
  7.     <uses-permission android:name="android.permission.WRITE_CONTACTS"/> 
  8.     <uses-permission android:name="android.permission.GET_ACCOUNTS" /> 
  9.     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> 
  10.     <application android:debuggable="true" android:icon="@drawable/icon" 
  11.       android:label="@string/app_name"> 
  12.         <service 
  13.           android:name=".authentication.AuthenticationService" 
  14.           android:exported="true"> 
  15.           <intent-filter> 
  16.             <action android:name="android.accounts.AccountAuthenticator" /> 
  17.             </intent-filter> 
  18.             <meta-data android:name="android.accounts.AccountAuthenticator" 
  19.               android:resource="@xml/authenticator" /> 
  20.         </service> 
  21.         <activity android:name=".ContactsActivity" 
  22.           android:configChanges="orientation|keyboardHidden" 
  23.           android:label="@string/app_name"> 
  24.             <intent-filter> 
  25.               <action android:name="android.intent.action.MAIN" /> 
  26.               <category android:name="android.intent.category.LAUNCHER" /> 
  27.             </intent-filter> 
  28.         </activity> 
  29.     </application> 
  30. </manifest>   

在文件中用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 = '<li data-role="list-divider">'+tmpKey+'</li>'; 
  8.         contactSelectionsVar.append(tmpKeyFragment); 
  9.         var tmpValues = (tmpContacts[i]).values; 
  10.         if(tmpValues != null){ 
  11.           var j; 
  12.           for(j = 0; j < tmpValues.length; j++){ 
  13.             var tmpDisplayName = tmpValues[j].displayName; 
  14.             var tmpContactId = tmpValues[j].contactId; 
  15.             var tmpLiFragment = '<li><a href="javascript:showContact(' + 
  16.               tmpContactId + ');return false;">'+tmpDisplayName+'</a></li>'; 
  17.             contactSelectionsVar.append(tmpLiFragment); 
  18.           } 
  19.         } 
  20.       } 
  21.     } 
  22.     contactSelectionsVar.listview('refresh'); 
  23.     showList(); 
  24.   } 

这里,通过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. <div data-role="page" data-theme="c" id="containerPage"> 
  2.   <!-- Header for contact details --> 
  3.   <div data-role="header" id="hdrDetail" data-nobackbtn="true" data-theme="c"> 
  4.     <h1><img align="top"  src="img/contacts.png"> Details</h1> 
  5.   </div> 
  6.  
  7.   <div data-role="content" id="contentDetail" data-theme="c"> 
  8.     <form id="contactForm" onsubmit="return false;"> 
  9.       <input id="contactId" type="hidden">  
  10.  
  11.       <div id="namesDiv" data-role="fieldcontain"> 
  12.         <table> 
  13.           <tbody> 
  14.             <tr> 
  15.               <td>First name</td> 
  16.               <td><input id="firstName" type="text"></td> 
  17.             </tr> 
  18.             <tr> 
  19.               <td>Last<br> 
  20.                 name</td> 
  21.               <td><input id="lastName" type="text"></td> 
  22.             </tr> 
  23.             <tr> 
  24.               <td>Notes</td> 
  25.               <td><input id="note" type="text"></td> 
  26.             </tr> 
  27.           </tbody> 
  28.         </table> 
  29.       </div> 
  30.  
  31.       <div data-role="collapsible" data-collapsed="true"> 
  32.         <h3>Phone Numbers</h3> 
  33.         <table id="phonesTable"></table> 
  34.       </div> 

注意如下几点:

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

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

 

通讯录详细信息页

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

通讯录详细信息页

通讯录详细信息页

通讯录详细信息页

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

  1. <div align="CENTER" data-role="controlgroup" data-type="horizontal"> 
  2.       <a href="javascript:generateJson();return false;" data-role="button" ><h5>Save</h5></a> 
  3.       <a id="deleteButton"  href="javascript:showDialog();return false;" data-role="button"  ><h5>Delete</h5></a> 
  4.       <a href="javascript:showListPage();return false;" data-role="button" ><h5>Cancel</h5></a> 
  5.     </div> 
  6.   </div> 
  7.  
  8.   <!-- Footer for contact details --> 
  9.   <div data-role="footer" id="ftrDetail" data-theme="c"></div> 
  10. ... 
  11. </div> <!-- Container page --> 

 

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

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

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

  1. <div data-role="header" id="hdrProgress" data-nobackbtn="true" data-theme="c"> 
  2.   <h1>Processing...</h1> 
  3. </div> 
  4.  
  5. <div data-role="content" id="contentProgress" data-theme="c"> 
  6.   <div align="CENTER"><h4>Please wait.</h4></div> 
  7.   <div align="CENTER"> 
  8.     <img alt="" id="spin" src="img/wait.gif"> 
  9.   </div> 
  10. </div> 
  11.  
  12. <div data-role="footer" id="ftrProgress" data-theme="c"></div> 

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

  1. <div data-role="header" id="hdrDialog" data-nobackbtn="true" data-theme="c"> 
  2.   <h1>Confirm delete</h1> 
  3. </div> 
  4.  
  5. <div data-role="content" id="contentDialog"  data-theme="c"> 
  6.   <div align="CENTER"> 
  7.     <h4>Are you sure you want to delete this contact?</h4> 
  8.   </div> 
  9.   <div align="CENTER" data-role="controlgroup" data-type="horizontal"> 
  10.     <a href="javascript:deleteContact();return false;" data-role="button"><h5>Delete</h5></a> 
  11.     <a href="javascript:showDetail();return false;" data-role="button"><h5>Cancel</h5></a> 
  12.   </div> 
  13. </div> 
  14.  
  15. <div data-role="footer" id="ftrDialog" data-theme="c"></div> 

#p#

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

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

  1. <script> 
  2.   ... 
  3.   //定义变量  
  4.   var contactIdVar; 
  5.  
  6.   <!-- contact details --> 
  7.   var hdrDetailVar; 
  8.   var contentDetailVar; 
  9.   var ftrDetailVar; 
  10.  
  11.  
  12.   $(document).ready(function () { 
  13.        contactIdVar = $('#contactId'); 
  14.  
  15.     <!-- contact details --> 
  16.     hdrDetailVar = $('#hdrDetail'); 
  17.     contentDetailVar = $('#contentDetail'); 
  18.     ftrDetailVar = $('#ftrDetail'); 
  19.  
  20.       ... 
  21.   }   
  22.   function hideDetail(){ 
  23.     hdrDetailVar.hide(); 
  24.     contentDetailVar.hide(); 
  25.     ftrDetailVar.hide(); 
  26.   } 
  27.  
  28.   function showDetail(){ 
  29.     hideDialog(); 
  30.     hideProgress(); 
  31.     hdrDetailVar.show(); 
  32.     contentDetailVar.show(); 
  33.     ftrDetailVar.show(); 
  34.   }   
  35.  
  36. </script> 

显示已存在的数据记录

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中先定义一些常量,以方便代码的书写和阅读,代码如下:

  1. <script> 
  2.   // Constants 
  3.   var STYLED_IN = '<input type="text" class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" id="'
  4.   var TR_O = '<tr><td>'
  5.   var TD_C = '</td></tr>'
  6.   var TD_M = '</td><td>'
  7.   var VALUE = ' value="'
  8.  
  9.   var WORK_ORG_FRAGMENT_SFX = '">'+TD_C; 
  10.  
  11.   var HOME_FRAGMENT_PRX = TR_O+'Home'+TD_M+STYLED_IN; 
  12.   var MOBILE_FRAGMENT_PRX = TR_O+'Mobile'+TD_M+STYLED_IN; 
  13.   var WORK_FRAGMENT_PRX = TR_O+'Work'+TD_M+STYLED_IN; 
  14.   var OTHER_FRAGMENT_PRX = TR_O+'Other'+TD_M+STYLED_IN; 
  15.  
  16.   var HOME_PHONE_FRAGMENT_MID = '_1_No"' + VALUE; 
  17.   var MOBILE_PHONE_FRAGMENT_MID = '_2_No"' + VALUE; 
  18.   var WORK_PHONE_FRAGMENT_MID = '_3_No"' + VALUE; 
  19.   var OTHER_PHONE_FRAGMENT_MID = '_7_No"' + VALUE; 
  20.   ... 

以上要注意的是在变量 STYLED_IN中,增加了jQuery Mobile中特定的样式。jQuery Mobile允许动态向一个表中增加行,但我们发现目前的版本,在动态增加行后,jQuery Mobile并没有将上一层的

标记中的样式自动应用到<input>中去,所以要另外增加。 

定义一些变量

下面定义一些初始变量,并进行初始化。

  1. <script> 
  2.   ... 
  3.   var currentContact; 
  4.   var firstNameVar; 
  5.   var lastNameVar; 
  6.   var noteVar; 
  7.   var contactIdVar; 
  8.   var phonesTableVar; 
  9.   ... 
  10.   $(document).ready(function () { 
  11.     firstNameVar = $('#firstName'); 
  12.     lastNameVar = $('#lastName'); 
  13.     noteVar = $('#note'); 
  14.     contactIdVar = $('#contactId'); 
  15.     phonesTableVar = $('#phonesTable'); 
  16.     ... 
  17.   } 

接下来,我们使用jQuery的parseJSON功能去解析后端程序返回的JSON字符串,其中用变量 currentContact保存JSON字符串,而contactId, firstName, lastName和note则可以直接从JSON中解析出来,比较简单,如下所示:

  1. function setCurrentContact(jsonText){ 
  2.     currentContact = $.parseJSON(jsonText); 
  3.     contactIdVar.val(currentContact.contactId); 
  4.     firstNameVar.val(currentContact.firstName); 
  5.     lastNameVar.val(currentContact.lastName); 
  6. noteVar.val(currentContact.note.text); 

解析对象数组

接下来,我们看下如何解析象Phone,Email,Address这些对象数组类型的JSON元素。这里其实只需要用循环对JSON中的Phone,Email,Address分别进行遍历,然后再用变量保存即可,下面以Phone为例进行说明,分别用 tmpType, tmpRowId, tmpNo保存了解析出来的type,rowId和no。

  1. var phonesArr = currentContact.phones; 
  2.     ...      
  3.  
  4.     if(phonesArr !== null){ 
  5.       for(i = 0; i < phonesArr.length; i++){ 
  6.         var tmpType = (phonesArr[i]).type; 
  7.         var tmpRowId = (phonesArr[i]).rowId; 
  8.         var tmpNo = (phonesArr[i]).no; 
  9.         ... 

根据不同的电话类型,我们构建了对应的HTML片断去展示JSON解析出来的电话号码,比如如果是家庭号码,并且tmpRowId为15和值是222333444,则以如下形式展示:

HOME_FRAGMENT_PRX + tmpRowId + HOME_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX

这实际上出来的效果是如下的HTML代码:

  1. <tr><td>Home</td><td><input type="text" 
  2.   class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" 
  3.   id="15_1_No" value="2223334444"></td></tr> 

最后,我们综合来看下解析JSON字符串的代码中,其中Phone部分的展示方法如下:

  1. var mobilePhoneSet = false
  2. var homePhoneSet = false
  3. var workPhoneSet = false
  4. var otherPhoneSet = false;           
  5.  
  6. if(phonesArr !== null){ 
  7.   for(i = 0; i < phonesArr.length; i++){ 
  8.         var tmpType = (phonesArr[i]).type; 
  9.         var tmpRowId = (phonesArr[i]).rowId; 
  10.         var tmpNo = (phonesArr[i]).no; 
  11.  
  12.     if(tmpType == 1){//home 
  13.       phonesTableVar.append(HOME_FRAGMENT_PRX + tmpRowId + HOME_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX); 
  14.       homePhoneSet = true
  15.     }else if(tmpType == 2){//mobile 
  16.       phonesTableVar.append(MOBILE_FRAGMENT_PRX + tmpRowId + MOBILE_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX); 
  17.       mobilePhoneSet = true
  18.     }else if(tmpType == 3){//work 
  19.       phonesTableVar.append(WORK_FRAGMENT_PRX + tmpRowId + WORK_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX); 
  20.       workPhoneSet = true
  21.     }else if(tmpType == 7){//other 
  22.       phonesTableVar.append(OTHER_FRAGMENT_PRX + tmpRowId + OTHER_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX); 
  23.       otherPhoneSet = true
  24.     } 
  25.   } 

要注意的是,假如通讯录中有的联系人并没有所有的电话类型(比如只有工作电话,而没记载家庭电话),则需要用如下代码:

  1. var HOME_PHONE_FRAGMENT_LOCAL = TR_O+'Home'+TD_M+STYLED_IN+'-1_1_No">'+TD_C; 
  2.   var MOBILE_PHONE_FRAGMENT_LOCAL = TR_O+'Mobile'+TD_M+STYLED_IN+'-1_2_No">'+TD_C; 
  3.   var WORK_PHONE_FRAGMENT_LOCAL = TR_O+'Work'+TD_M+STYLED_IN+'-1_3_No">'+TD_C; 
  4.   var OTHER_PHONE_FRAGMENT_LOCAL = TR_O+'Other'+TD_M+STYLED_IN+'-1_7_No">'+TD_C; 
  5.   ... 
  6.   if(phonesArr !== null){ 
  7.     ... 
  8.   } 
  9.   if(!homePhoneSet){ 
  10.     phonesTableVar.append(HOME_PHONE_FRAGMENT_LOCAL); 
  11.   } 
  12.   if(!mobilePhoneSet){ 
  13.     phonesTableVar.append(MOBILE_PHONE_FRAGMENT_LOCAL); 
  14.   } 
  15.   if(!workPhoneSet){ 
  16.     phonesTableVar.append(WORK_PHONE_FRAGMENT_LOCAL); 
  17.   } 
  18.   if(!otherPhoneSet){ 
  19.     phonesTableVar.append(OTHER_PHONE_FRAGMENT_LOCAL); 
  20.   } 

解析下上面这段代码,假设某个联系人没工作移动电话,那么 workPhoneSet 为false,上面的代码会变为:

  1. <tr><td>Home</td><td><input type="text" 
  2.     class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" id="-1_1_No"></td></tr> 

这实际上是向DIV区域加了一个空白的文本域而已。

最后,当解析完后端返回的JSON字符串后,就在 setCurrentContact最后,调用showDetail()方法,显示出内容页面即可

  1. function setCurrentContact(jsonText){ 
  2.     ... 
  3.     showDetail(); 
  4.   } 

最后,总结下显示已存在的通讯录的页面流向和步骤,如下:

  1. ContactsActivity.loadPage('DetailPage.html?id') 
  2.   -> DetailPage.html:$(document).ready() 
  3.     -> ContactsActivity.getContact(id, 'setCurrentContact') 
  4.       ->  DetailPage.html:setCurrentContact(json) 

删除一条通讯录

当用户点删除按钮时,代码如下:

  1. function deleteContact(){ 
  2.   showProgress(); 
  3.   contactSupport.deleteContact(contactIdVar.val(),'ListPage.html'); 

这里同样也是调用后端JAVA应用的deleteContact方法,并且向后端传入要删除的当前通讯录的ID。而后端的代码如下:

  1. public void deleteContact(String contactId, String displayPage){ 
  2.   ContactUtility.deleteContact(contactId, ...); 
  3.   loadPage(displayPage); 

其中这里的ContactUtility.deleteContact方法,将会在后续的教程中详细讲解。#p#

保存通讯录

在保存已经写好的通讯录后,当用户点保存按钮时,将会将用户的输入产生为JSON格式,然后提交到后端去,代码如下:

  1. function generateJson(){ 
  2.   showProgress(); 
  3.   var jsonText = '{'
  4.   jsonText += '"contactId":"' + contactIdVar.val() + '"'; 
  5.   jsonText += ',"firstName":"' + firstNameVar.val() + '"'; 
  6.   jsonText += ',"lastName":"' + lastNameVar.val() + '"'; 
  7.   jsonText += ',"note":{"rowId":"","text":"' + noteVar.val() + '"}'; 
  8.   ...   

可以看到,实际上就是将用户的输入组织为JSON格式的字符串,注意,其中note中的rowId这里可以设置为空字符串,因为对于新增记录来说,这个字段并不重要。

下面是对电话phones字段的保存代码,如下:

  1. // Phones 
  2.   jsonText += ',"phones":['; 
  3.   $('input[id$="_No"]').each(function(index) { 
  4.     jsonText += '{"rowId":"","type":"'; 
  5.     jsonText += (($(this).attr('id')).split('_'))[1] + '","no":"'; 
  6.     jsonText += $(this).attr('value') + '"'; 
  7.     jsonText += '},'; 
  8.   }); 
  9.   jsonText = addClosingBracket(jsonText); 
  10.   ... 
  11.   jsonText += '}'; 
  12.   contactSupport.saveContact(jsonText,'ListPage.html'); 
  13. }             
  14.  
  15. function addClosingBracket(txt){ 
  16.   if((txt.length - 1) == txt.lastIndexOf(',')){ 
  17.     txttxt = txt.substring(0,txt.length - 1) + ']'; 
  18.   }else{ 
  19.     txt += ']'; 
  20.   } 
  21.   return txt; 

对于电话类型type,这里要将用户的输入进行截取,比如id="15_1_No",则电话类型为1,

这里做了一个简单对所有的<input>元素进行分析截取。

当准备好要向后端提交的JSON格式后,则可以调用后端JAVA应用的ContactsActivity.saveContact()方法进行保存,代码如下:

  1. import org.codehaus.jackson.map.ObjectMapper; 
  2.  
  3. public class ContactsActivity extends Activity { 
  4.   ... 
  5.   public void saveContact(String json, String displayPage){ 
  6.     ObjectMapper mapper = new ObjectMapper(); 
  7.     try{ 
  8.       Contact c = mapper.readValue(json,Contact.class); 
  9.         ContactUtility.saveOrUpdateContact(c, getContentResolver(), accountName, accountType); 
  10.     }catch(Exception e){ 
  11.       ... 
  12.     } 
  13.     loadPage(displayPage); 
  14.   } 
  15.   ... 

其中ObjectMapper类是Jackson JSON解析处理器中的一个负责将JSON转化为POJO实体类的工具类。Jackson JSON(http://jackson.codehaus.org/)是个不错的JSON分析处理工具。其使用很简单,这里通过mapper.readValue(json,Contact.class);

就完成了将JSON字符串解析转为Contract实体类的工作,最后用saveContact()方法进行保存。

小结

在本系列教程的第二篇,讨论了如何读取和编辑存在的通讯记录到前端页面,如何新建通讯录的帐号。在下一篇教程中,将讨论使用Android API去访问通讯录中的相关数据。

责任编辑:佚名 来源: IT168
相关推荐

2011-07-19 17:25:14

jQuery MobiAndroid

2011-07-20 10:56:53

jQuery Mobi手机新闻浏览器

2011-09-05 14:08:21

微信Andriod安卓

2010-06-18 22:42:42

智能手机平台Android网秦

2012-12-21 14:51:52

手机中国

2012-02-13 16:39:03

AndroidWeb App官方文档

2015-12-31 09:22:25

编程故事printf

2011-09-16 14:05:42

Andrioid应用iPhone应用Symbian应用

2022-01-04 15:34:31

鸿蒙HarmonyOS应用

2021-12-10 10:12:44

鸿蒙HarmonyOS应用

2014-01-16 11:14:37

StormTopology

2012-02-09 09:10:44

Path通讯录隐私

2021-08-02 05:20:22

Android 12谷歌 Material Yo

2013-03-18 10:19:27

安卓软件手机通讯录隐私信息

2012-06-05 13:53:03

天天联系华为

2010-12-08 10:15:43

系统架构设计师

2010-09-08 23:11:01

2010-09-26 08:35:01

火种通讯录

2011-09-01 10:21:52

jQuery Mobi元素

2015-07-30 15:58:15

EC企信企业即时通讯
点赞
收藏

51CTO技术栈公众号