Статьи

Создание приложения для контактов с помощью jQuery Mobile и Android SDK — часть 2

Этот туториал покажет, где мы остановились в первой части , демонстрируя, как создать учетную запись для вновь созданных контактов. Мы также опишем, как редактировать и удалять существующие контакты.

В первой части этого руководства мы представили пример приложения, которое будет реализовано. Мы также дали обзор элементов страницы в jQuery Mobile Framework и объяснили, как экраны в примере приложения построены с использованием этих элементов. Мы также рассмотрели основные концепции интеграции пользовательского интерфейса, состоящего из HTML и JavaScript, с внутренними классами Java, которые реализуют бизнес-функциональность.


Теперь мы рассмотрим, как создать учетную запись для связи с учебным приложением. Экран «Создать учетную запись» показан на рисунке 1. Пользователь вводит имя учетной записи в поле ввода и нажимает кнопку «Сохранить». Код HTML и JavaScript для экрана «Создание учетной записи» является частью ListPage.html . Соответствующие разделы показаны ниже.

01
02
03
04
05
06
07
08
09
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
<html>
  …
<body>
<B><!— Container Page —></B>
<div data-role=»page» data-theme=»c» id=»containerPage»>
  …
  <B><!— Create Account —></B>
  <div data-role=»header» id=»hdrAccount» data-nobackbtn=»true» data-theme=»c»>
    <h1>Create Account</h1>
  </div>
  <div data-role=»content» id=»contentAccount» data-theme=»c»>
    <div align=»CENTER»><img src=»img/<B>contacts-master-bgd.png</B>»></div>
    <div align=»CENTER»><h4>Please enter name of the new account for this application</h4></div>
    <div align=»CENTER»>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.</div>
    <div align=»CENTER» id=»accountDiv» data-role=»fieldcontain»>
      <input id=»<B>accountName</B>» type=»text» />
    </div>
    <div align=»CENTER»>
      <a href=»<B>javascript:createAccount();</B>return false;»
        data-inline=»true»><B>Save</B></a>
    </div>
    …
  </div>
  <div data-role=»footer» id=»ftrAccount» data-theme=»c»></div>
   
</div> <!— Container Page Ends Here —>
<script>
  function <B>createAccount()</B>{
    showProgress();
    contactSupport.createAccount($(‘#accountName’).val(),’ListPage.html’);
  }
  …
</script>
  • Обратите внимание, что «Создать учетную запись» представлена ​​страницей содержимого с собственными заголовками, разделами содержимого и нижнего колонтитула, размещенными внутри страницы контейнера.
  • Когда пользователь нажимает кнопку Сохранить, createAccount() функция createAccount() .
  • createAccount() получает значение имени учетной записи, введенное пользователем в поле ввода accountName , и передает его методу ContactsActivity.createAccount() в качестве первого параметра. Второй параметр, передаваемый методу — это HTML-файл обратного вызова для загрузки, ListPage.html .

Метод ContactsActivity.createAccount() приведен ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.accounts.AccountManager;
import android.accounts.Account;
public class ContactsActivity extends Activity {
  …
  private String accountType = null;
  …
  public void onCreate(Bundle savedInstanceState) {
    …
    <B>accountType = «com.jquerymobile.demo.contact»;</B>
    …
  }
   
  public void createAccount(String accountN, String displayPage){
    if(accountN != null && !»».equals(accountN)){
      accountName = accountN;
      <B>AccountManager</B>.get(this).addAccountExplicitly(new Account(accountName,<B>accountType</B>), «dummyPassword», null);
    }
    loadPage(displayPage);
  }
  …
}
  • Фактическое создание учетной записи выполняется android.accounts.AccountManager который является одной из точек входа в Android Java API для управления учетными записями пользователей на устройстве. Сначала мы addAccountExplicitly() экземпляр AccountManager , вызывая AccountManager.get() а затем вызываем addAccountExplicitly() чтобы создать учетную запись пользователя с нужным именем и типом.
  • После создания учетной записи HTML-файл обратного вызова ListPage.html загружается через loadPage(displayPage) .
  • Вспомните из части 1 «Начальная загрузка ListPage.html», загрузка ListPage.html вызовет вызов ContactACtivity.getAllContacts() . Поскольку учетная запись была найдена, этот метод сгенерирует отформатированную в JSON строку списка контактов и передаст эту строку в функцию JavaScript setContactsList() в ListPage.html. Мы рассмотрели эту функцию в части 1 «Экран списка контактов».

Чтобы иметь возможность создать учетную запись, мы должны реализовать службу Android, которая имеет следующую цель в файле AndroidManifest.xml .

1
2
3
<intent-filter>
  <action android:name=»android.accounts.AccountAuthenticator»/>
</intent-filter>

Кроме того, этот сервис должен иметь следующее описание meta-data в AndroidManifest.xml .

1
2
3
<meta-data
  android:name=»android.accounts.AccountAuthenticator»
  android:resource=»<B>@xml/authenticator</B>» />

Наконец, файл конфигурации res/xml/authenticator.xml (который является значением атрибута android:resource выше) должен иметь элемент с именем account-authenticator где значение атрибута android:accountType установлено в com.jquerymobile.demo.contact . Обратите внимание, что это значение accountType в ContactsActivity . Подводя итоги обсуждения, сначала посмотрите на выделенный раздел в AndroidManifest.xml .

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

Относительно значения атрибута package мы определяем класс обслуживания как .authentication.AuthenticationService . Следовательно, полное имя класса — com.jquerymobile.demo.contact.authentication.AuthenticationService . Затем мы предоставляем обязательный элемент intent-filter и атрибут android:resource в элементе meta-data . Файл конфигурации res/xml/authenticator.xml указан ниже. В этом файле важно то, что значение атрибута android:accountType совпадает с типом учетной записи, которую ContactsActivity использует для создания учетной записи.

1
2
3
4
5
6
7
<?xml version=»1.0″ encoding=»utf-8″?>
<account-authenticator xmlns:android=»http://schemas.android.com/apk/res/android»
  <B>android:accountType=»com.jquerymobile.demo.contact»</B>
  android:icon=»@drawable/icon»
  android:smallIcon=»@drawable/icon»
  android:label=»@string/app_name»
/>

Наконец, реализация сервиса приведена ниже. Это тривиальная реализация без каких-либо ценных функций. Единственный реализованный метод — это абстрактный onBind() из Service . Если вам нужна дополнительная функциональность, например, возможность связать эту учетную запись с онлайн-службой, вам необходимо предоставить необходимую бизнес-логику, расширив различные другие методы класса android.app.Service . Вам также может понадобиться реализовать класс аутентификатора, расширяющий android.accounts.AbstractAccountAuthenticator . Для целей данного учебного приложения этой реализации сервиса достаточно.

01
02
03
04
05
06
07
08
09
10
11
package com.jquerymobile.demo.contact.authentication;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
 
public class AuthenticationService extends Service {
  public IBinder onBind(Intent intent) {
    return null;
  }
}

После создания учетной записи ее имя можно просмотреть на экране «Настройки учетных записей и синхронизации» для контактов на устройстве Android. Это показано на рисунке ниже.

Название аккаунта

Рисунок 11. Имя учетной записи.


Чтобы отобразить сведения о существующем контакте, пользователь нажимает на элемент списка для этого контакта на экране «Список контактов» (часть 1, рисунок 2). Напомним, из раздела «Экран списка контактов» следующий код создает элементы списка для контактов. Интерес представляет showContact() JavaScript showContact() в определении тега <a> для каждого конкретного контакта. Когда пользователь нажимает на элемент списка, идентификатор контакта в этом элементе списка передается в showContact() . (Идентификатор контакта является уникальным идентификатором этого контакта в базе данных контактов устройства Android.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function setContactsList(jsonText){
  var tmpJson = $.parseJSON(jsonText);
  if(tmpJson != null && tmpJson.contacts != null){
    var tmpContacts = tmpJson.contacts;
    for(i = 0; i < tmpContacts.length; i++){
      var tmpKey = (tmpContacts[i]).key;
      var tmpKeyFragment = ‘<li data-role=»list-divider»>’+tmpKey+'</li>’;
      contactSelectionsVar.append(tmpKeyFragment);
      var tmpValues = (tmpContacts[i]).values;
      if(tmpValues != null){
        var j;
        for(j = 0; j < tmpValues.length; j++){
          var tmpDisplayName = tmpValues[j].displayName;
          var tmpContactId = tmpValues[j].contactId;
          var tmpLiFragment = ‘<li><a href=»<B>javascript:showContact(‘ +
            tmpContactId + ‘);</B>return false;»>’+tmpDisplayName+'</a></li>’;
          contactSelectionsVar.append(tmpLiFragment);
        }
      }
    }
  }
  contactSelectionsVar.listview(‘refresh’);
  showList();
}

Список функций JavaScript showContact() приведен ниже. Он показывает экран Progress и вызывает метод ContactsActivity.showContact() , передавая ему идентификатор отображаемого контакта и страницу отображения обратного вызова, DetailPage.html.

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

Связанный код в классе ContactsActivity показан ниже.

1
2
3
public void showContact(String contactId, String displayPage){
  loadPage(displayPage + «?» + contactId);
}

Метод showContact() отображает страницу обратного вызова, значение параметра displayPage , добавляя параметр contactId в виде строки HTTP-запроса. Например, если значение contactId равно 23, а displayPage равно DetailPage.html то URL-адрес для загрузки — DetailPage.html?23 .

В DetailPage.html есть три страницы содержимого. Один из них представляет контактную информацию для экранов «Существующий контакт» и «Пустой контакт» на рисунке 3, другой — для экрана «Подтверждение удаления» (рисунок 4), а последний — для экрана «Ход выполнения» (рисунок 5). Эти цифры в первой части урока.

Контактная информация

Давайте посмотрим на фрагменты HTML-кода, чтобы просмотреть контактную информацию на странице контента. Первый фрагмент показан ниже.

01
02
03
04
05
06
07
08
09
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
<div data-role=»page» data-theme=»c» id=»containerPage»>
  <!— Header for contact details —>
  <div data-role=»header» id=»hdrDetail» data-nobackbtn=»true» data-theme=»c»>
    <h1><img align=»top» src=»<B>img/contacts.png</B>»> Details</h1>
  </div>
   
  <!— Content for contact details —>
  <div data-role=»content» id=»contentDetail» data-theme=»c»>
    <form id=»contactForm» onsubmit=»return false;»>
      <input id=»<B>contactId</B>» type=»hidden»>
       
      <div id=»namesDiv» data-role=»fieldcontain»>
        <table>
          <tbody>
            <tr>
              <td>First name</td>
              <td><input id=»<B>firstName</B>» type=»text»></td>
            </tr>
                         
            <tr>
              <td>Last<br>
                name</td>
              <td><input id=»<B>lastName</B>» type=»text»></td>
            </tr>
             
            <tr>
              <td>Notes</td>
              <td><input id=»<B>note</B>» type=»text»></td>
            </tr>
          </tbody>
        </table>
      </div>
       
      <div <B>data-role=»collapsible» data-collapsed=»true»</B>>
        <h3>Phone Numbers</h3>
        <table id=»<B>phonesTable</B>»></table>
      </div>
      …
  • В строке заголовка есть изображение. (Это изображение значка, созданное из элементов графического интерфейса Android в http://www.matcheck.cz/androidguipsd/ .)
  • На странице содержимого есть форма для хранения всех полей контактов. Есть поля ввода для имени и фамилии и примечания для контакта.
  • contactId является скрытой формой ввода. Соответствует уникальному идентификатору контакта в базе данных контактов устройства Android.
  • Номера телефонов хранятся в табличной структуре в разборном блоке jQuery Mobile. Складной блок служит контейнером для логически связанных элементов пользовательского интерфейса. Пользователь может развернуть или свернуть блок, чтобы отобразить / скрыть элементы, которые он переносит. Первоначально блок свернут.

На следующем рисунке показан экран «Существующий контакт» с элементами пользовательского интерфейса, соответствующими приведенному выше фрагменту кода. Разборный блок для телефонов расширен, показывая четыре различных типа телефонных номеров для контакта. Обратите внимание, что эти телефонные номера были динамически добавлены в список phonesTable качестве части загрузки контактных данных. Мы рассмотрим соответствующий код JavaScript позже.

Существующий контакт - часть 1

Рисунок 12. Существующий контакт — часть 1.

Мы продолжаем просматривать страницу содержимого для получения контактной информации со следующим списком.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   
  …
  <div data-role=»collapsible» data-collapsed=»true»>
    <h3>Emails</h3>
    <table id=»<B>emailsTable</B>»></table>
  </div>
   
  <B><div data-role=»collapsible» data-collapsed=»true»></B>
  <h3><B>More</B></h3>
    <div data-role=»collapsible» data-collapsed=»true» id=»addressDiv»>
     <h3>Addresses</h3>
     <table id=»<B>addressTable</B>»></table>
    </div>
     
    <div data-role=»collapsible» data-collapsed=»true»>
      <h3>Organizations</h3>
      <table id=»<B>orgsTable</B>»></table>
    </div>
     
    <div data-role=»collapsible» data-collapsed=»true»>
      <h3>IMs</h3>
     <table id=»<B>imsTable</B>»></table>
    </div>
  </div>
</form>
  • Для электронных писем определен еще один складной блок, который очень похож на блок для телефонных номеров. Он содержит таблицу с идентификатором emailsTable которая позже будет динамически заполнена электронными письмами контакта.
  • Мы видим пример вложенных складных блоков. Разборный блок верхнего уровня с заголовком «Дополнительно» содержит три вложенных складных блока для адресов, организаций и мгновенных сообщений (адресов мгновенных сообщений) контакта.
  • Каждый из разборных блоков для адресов, организаций и IM содержит таблицу для соответствующего контента. Эти таблицы заполняются динамически при загрузке контента.

На рисунках 13-16 показан экран «Существующий контакт» с элементами пользовательского интерфейса, соответствующими приведенному выше фрагменту кода. На рисунке 13 развернутый блок для электронной почты расширен, показывая три различных типа электронных писем для контакта.

Существующий контакт - часть 2

Рисунок 13. Существующий контакт — часть 2.

На следующем рисунке показано, что свернутый блок электронной почты свернут, а свернутый блок «Больше» раскрыт. Складные блоки под надписью «Больше» находятся в свернутом виде.

Существующий контакт - часть 3

Рисунок 14. Существующий контакт — часть 3.

На следующем рисунке блок для адресов в разделе «Дополнительно» был расширен. Существует три типа адресов: дом, работа, другое.

Существующий контакт - часть 4

Рисунок 15. Существующий контакт — часть 4.

На следующем рисунке блоки для организаций (слева) и IM (справа) в разделе «Дополнительно» были расширены. Существует два типа организаций (работа, другие) и десять типов IM для контакта.

Существующий контакт - часть 4

Рисунок 16. Существующий контакт — часть 5.

Последний фрагмент кода для страницы с контактной информацией приведен ниже. Это три кнопки, показанные на рисунке 14 для операций сохранения, удаления и отмены.

01
02
03
04
05
06
07
08
09
10
11
12
13
      
    …
    <div align=»CENTER» <B>data-role=»controlgroup» data-type=»horizontal»</B>>
      <a href=»<B>javascript:generateJson();</B>return false;»
      <a id=»deleteButton» href=»<B>javascript:showDialog();</B>return false;»
      <a href=»<B>javascript:showListPage();</B>return false;»
    </div>
  </div>
   
  <!— Footer for contact details —>
  <div data-role=»footer» id=»ftrDetail» data-theme=»c»></div>
</div> <!— Container page —>
  • Обратите внимание на атрибут data-role="controlgroup" в <div> . Это позволяет нам сгруппировать три кнопки вместе. Определение атрибута data-type="horizontal" указывает, что кнопки должны быть расположены горизонтально. (Детали сгруппированных кнопок в документации по jQuery Mobile.)
  • Каждая кнопка активирует соответствующий метод JavaScript.

Экран прогресса

HTML-код, определяющий экран Progress в DetailPage.html , приведен ниже.

01
02
03
04
05
06
07
08
09
10
11
12
<div data-role=»header» id=»hdrProgress» data-nobackbtn=»true» data-theme=»c»>
  <h1>Processing…</h1>
</div>
 
<div data-role=»content» id=»contentProgress» data-theme=»c»>
  <div align=»CENTER»><h4>Please wait.</h4></div>
  <div align=»CENTER»>
    <img alt=»» id=»spin» src=»img/wait.gif»>
  </div>
</div>
 
<div data-role=»footer» id=»ftrProgress» data-theme=»c»></div>

Как видно выше, экран Progress имеет простую реализацию с описательным текстом и изображением вращающегося колеса, wait.gif .

Подтвердите удаление

HTML-код для третьей страницы содержимого в DetailPage.html , соответствующий экрану «Подтвердить удаление», показан ниже. Этот экран отображается, когда пользователь нажимает кнопку «Удалить» на рис. 14, чтобы удалить отображаемый в данный момент контакт.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<div data-role=»header» id=»hdrDialog» data-nobackbtn=»true» data-theme=»c»>
  <h1>Confirm delete</h1>
</div>
 
<div data-role=»content» id=»contentDialog» data-theme=»c»>
  <div align=»CENTER»>
    <h4>Are you sure you want to delete this contact?</h4>
  </div>
  <div align=»CENTER» <B>data-role=»controlgroup» data-type=»horizontal»</B>>
    <a href=»<B>javascript:deleteContact();</B>return false;»
    <a href=»<B>javascript:showDetail();</B>return false;»
  </div>
</div>
 
<div data-role=»footer» id=»ftrDialog» data-theme=»c»></div>
  • Подобно кнопкам «Сохранить», «Удалить», «Отмена», которые обсуждались выше, мы используем сгруппированные кнопки для «Удалить» и «Отмена».
  • Если пользователь нажимает кнопку Отмена, операция удаления отменяется, и пользователю отображается экран «Существующий контакт».
  • Если пользователь нажимает на «Удалить», выполняется операция удаления, и пользователь попадает на экран «Список контактов» с обновленным списком контактов.

Отображение / скрытие страниц контента

В любой момент времени должна отображаться только одна из страниц содержимого с контактными данными, «Подтвердить удаление» и «Ход выполнения». Следующий листинг кода в DetailPage.html описывает, как осуществляется отображение / скрытие этих страниц содержимого.

  • Мы определяем переменные JavaScript для разделов заголовка, содержимого и нижнего колонтитула и инициализируем их в функции jQuery $(document).ready() с помощью селекторов id.
  • Затем мы определяем соответствующие функции hide Page () , show Page () . При вызове функции show Page () для одной страницы содержимого отображаются заголовки, разделы содержимого и нижнего колонтитула страницы, а также скрываются разделы верхнего и нижнего колонтитула содержимого и других страниц.
  • Экран «Подтвердить удаление» не требуется отображать для экрана «Пустой контакт», который отображается при добавлении нового контакта. Когда отображается экран «Пустой контакт», идентификатор контакта является пустой строкой. Функция showDialog() немедленно возвращается без отображения экрана «Подтвердить удаление», если обработанный в настоящее время идентификатор контакта является пустой строкой.
01
02
03
04
05
06
07
08
09
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<script>
  …
  // Commonly used variables
  var contactIdVar;
   
  <!— contact details —>
  var hdrDetailVar;
  var contentDetailVar;
  var ftrDetailVar;
   
  <!— progress page —>
  var hdrProgressVar;
  var contentProgressVar;
  var ftrProgressVar;
   
  <!— confirm delete —>
  var hdrDialogVar;
  var contentDialogVar;
  var ftrDialogVar;
  …
   
  $(document).ready(function () {
    // Initialize commonly used variables
    contactIdVar = $(‘#contactId’);
     
    <!— contact details —>
    hdrDetailVar = $(‘#hdrDetail’);
    contentDetailVar = $(‘#contentDetail’);
    ftrDetailVar = $(‘#ftrDetail’);
     
    <!— progress page —>
    hdrProgressVar = $(‘#hdrProgress’);
    contentProgressVar = $(‘#contentProgress’);
    ftrProgressVar = $(‘#ftrProgress’);
     
    <!— confirm delete —>
    hdrDialogVar = $(‘#hdrDialog’);
    contentDialogVar = $(‘#contentDialog’);
    ftrDialogVar = $(‘#ftrDialog’);
    …
  }
   
  function hideDialog(){
    hdrDialogVar.hide();
    contentDialogVar.hide();
    ftrDialogVar.hide();
  }
   
  function showDialog(){
    <B>if(» === contactIdVar.val()){
      return;
    }</B>
    hideProgress();
    hideDetail();
    hdrDialogVar.show();
    contentDialogVar.show();
    ftrDialogVar.show();
  }
   
  function hideDetail(){
    hdrDetailVar.hide();
    contentDetailVar.hide();
    ftrDetailVar.hide();
  }
   
  function showDetail(){
    hideDialog();
    hideProgress();
    hdrDetailVar.show();
    contentDetailVar.show();
    ftrDetailVar.show();
  }
   
  function hideProgress(){
    hdrProgressVar.hide();
    contentProgressVar.hide();
    ftrProgressVar.hide();
  }
     
  function showProgress(){
    hideDialog();
    hideDetail();
    hdrProgressVar.show();
    contentProgressVar.show();
    ftrProgressVar.show();
  }
  …
 
</script>

Изучив структуру содержательных страниц в DetailPage.html , давайте рассмотрим, как заполнить контактную информацию для существующего контакта. Напомним, что метод ContactsActivity.showContact() отображает DetailPage.html идентификатор контакта в виде строки HTTP-запроса, например DetailPage.html?23 . Давайте посмотрим ниже, как код JavaScript в DetailPage.html будет обрабатывать эту информацию. Соответствующий раздел в функции jQuery $(document).ready() приведен ниже.

1
2
3
4
5
6
$(document).ready(function () {
  …
  showProgress();
  contactIdVar.val(<B>window.location.search.substring(1)</B>);
  contactSupport.getContact(contactIdVar.val(),’setCurrentContact’);
});
  • Когда страница загружается, экран прогресса отображается сразу.
  • Затем значение переменной contactIdVar устанавливается contactIdVar идентификатору контакта из строки запроса. Например, если страница загружена как DetailPage.html?23 , значение переменной contactIdVar устанавливается contactIdVar 23 . Разбор строки запроса выполняется через window.location.search.substring(1) .
  • Наконец, ContactsActivity.getContact() вызывается путем передачи двух параметров: (1) идентификатор контакта и (2) функция обратного вызова setCurrentContact . В результате ContactsActivity получит сведения о контакте для указанного идентификатора и передаст результаты обратно в функцию JavaScript setCurrentContact() в DetailPage.html .

Метод ContactsActivity.getContact() показан ниже. Он получает строку контактных данных в формате JSON и передает ее обратно в запрошенную функцию JavaScript.

1
2
3
4
5
public void getContact(String contactId, String contactCallback){
  String json = ContactUtility.getContactJSON(contactId, …);
  final String callbackFunction = «javascript:» + contactCallback + «(‘» + json + «‘)»;
  loadURL(callbackFunction);
}

Формат JSON для деталей контакта показан ниже.

JSON Форматированная строка для контактной информации
01
02
03
04
05
06
07
08
09
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
{
  «contactId»:»265″,
  «firstName»:»Aafjes»,
  «lastName»:»Bertus»,
  «note»:{«rowId»:»2265″,»text»:»Author»},
  «ims»:[
    {«rowId»:»2274″,»protocol»:»-1″,»value»:»»},
    {«rowId»:»2275″,»protocol»:»0″,»value»:»bertus@aim»},
    {«rowId»:»2276″,»protocol»:»5″,»value»:»bertus@google»},
    {«rowId»:»2277″,»protocol»:»6″,»value»:»»},
    {«rowId»:»2278″,»protocol»:»7″,»value»:»»},
    {«rowId»:»2279″,»protocol»:»1″,»value»:»bertus@msn»},
    {«rowId»:»2280″,»protocol»:»8″,»value»:»»},
    {«rowId»:»2281″,»protocol»:»4″,»value»:»»},
    {«rowId»:»2282″,»protocol»:»3″,»value»:»»},
    {«rowId»:»2283″,»protocol»:»2″,»value»:»»}
  ],
  «phones»:[
    {«rowId»:»2284″,»type»:»1″,»no»:»111-222-3333″},
    {«rowId»:»2285″,»type»:»2″,»no»:»222-000-9999″},
    {«rowId»:»2286″,»type»:»3″,»no»:»444-787-9900″},
    {«rowId»:»2287″,»type»:»7″,»no»:»555-744-9999″}
  ],
  «emails»:[
    {«rowId»:»2271″,»type»:»1″,»value»:»[email protected]»},
    {«rowId»:»2272″,»type»:»2″,»value»:»[email protected]»},
    {«rowId»:»2273″,»type»:»3″,»value»:»[email protected]»}
  ],
  «organizations»:[
    {«rowId»:»2269″,»type»:»1″,»name»:»Publications Inc.»,»title»:»CEO»},
    {«rowId»:»2270″,»type»:»2″,»name»:»Volunteers Corp.»,»title»:»Member»}
  ],
  «addresses»:[
    {«rowId»:»2266″,»type»:»1″,»street»:»Alhambra st.»,»city»:»Alhambra»,»state»:»MI»,
      «country»:»USA»,»zip»:»48100″,»poBox»:»»},
    {«rowId»:»2267″,»type»:»2″,»street»:»1 Corporation st»,»city»:»Alhambra»,»state»:»MI»,
      «country»:»USA»,»zip»:»48000″,»poBox»:»44456″},
    {«rowId»:»2268″,»type»:»3″,»street»:»»,»city»:»»,»state»:»»,
      «country»:»»,»zip»:»»,»poBox»:»»}
  ]
}
  • Поля contactId , firstName , lastName являются простыми строками, note — это объект, а каждый из ims (адресов мгновенных сообщений), phones , emails , organizations и addresses — это массивы объектов.
  • Для любого объекта поле rowId является целым числом. rowId уникален для всех объектов в конкретном массиве. Например, два объекта в массиве ims имеют одинаковый rowId . Уникальность rowId помогает нам определять уникальные элементы пользовательского интерфейса в коде JavaScript.
  • Объекты в массиве ims имеют атрибут protocol , который обозначает вид IM, который представляет каждый объект. protocol является целочисленным. API контактов Android определяет следующие константы для протоколов IM (см. Документацию ).
    • протокол = -1, пользовательский
    • протокол = 0, AIM
    • протокол = 1, MSN
    • протокол = 2, Yahoo
    • протокол = 3, скайп
    • протокол = 4, QQ
    • протокол = 5, Google
    • протокол = 6, аська
    • протокол = 7, джаббер
  • Объекты в массиве phones имеют атрибут type , который указывает тип телефона, который представляет каждый объект. type является целочисленным. API контактов Android определяет следующие константы для типов телефонов (см. Документацию ).
    • type = 1, Home
    • тип = 2, мобильный
    • тип = 3, работа
    • type = 7, Other
  • Объекты в массиве emails имеют атрибут type , который указывает тип электронного письма, который представляет каждый объект. type является целочисленным. API контактов Android определяет следующие константы для типов электронной почты (см. Документацию ).
    • type = 1, Home
    • тип = 2, работа
    • type = 3, Other
  • Объекты в массиве organizations имеют атрибут type , который обозначает тип организации, который представляет каждый объект. type является целочисленным. API контактов Android определяет следующие константы для типов организации (см. Документацию ).
    • тип = 1, работа
    • type = 2, Other
  • Объекты в массиве addresses имеют атрибут type , который обозначает тип адреса, который представляет каждый объект. type является целочисленным. API контактов Android определяет следующие константы для типов адресов (см. Документацию ).
    • type = 1, Home
    • тип = 2, работа
    • type = 3, Other

Реализация функции JavaScript setCurrentContact() в DetailPage.html приведена ниже. Эта функция отвечает за анализ текста JSON выше и динамическое заполнение различных элементов пользовательского интерфейса, которые мы обсуждали ранее. Для краткости мы рассмотрим часть кода, которая устанавливает значения имени, фамилии, примечаний и телефонных номеров. Другие элементы пользовательского интерфейса анализируются и заполняются аналогичным образом.

Поскольку элементы пользовательского интерфейса создаются на основе различных фрагментов HTML, используемых в качестве шаблонов, мы начнем с определения констант для этих фрагментов, как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<script>
  // Constants
  <B>var STYLED_IN = ‘<input type=»text» class=»ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c» id=»‘;</B>
  var TR_O = ‘<tr><td>’;
  var TD_C = ‘</td></tr>’;
  var TD_M = ‘</td><td>’;
  var VALUE = ‘ value=»‘;
   
  var WORK_ORG_FRAGMENT_SFX = ‘»>’+TD_C;
   
  var HOME_FRAGMENT_PRX = TR_O+’Home’+TD_M+STYLED_IN;
  var MOBILE_FRAGMENT_PRX = TR_O+’Mobile’+TD_M+STYLED_IN;
  var WORK_FRAGMENT_PRX = TR_O+’Work’+TD_M+STYLED_IN;
  var OTHER_FRAGMENT_PRX = TR_O+’Other’+TD_M+STYLED_IN;
   
  var HOME_PHONE_FRAGMENT_MID = ‘_1_No»‘ + VALUE;
  var MOBILE_PHONE_FRAGMENT_MID = ‘_2_No»‘ + VALUE;
  var WORK_PHONE_FRAGMENT_MID = ‘_3_No»‘ + VALUE;
  var OTHER_PHONE_FRAGMENT_MID = ‘_7_No»‘ + VALUE;
  …

Что примечательно выше, это переменная с именем STYLED_IN мы явно добавляем специфичные стили jQuery Mobile. Платформа jQuery Mobile позволяет динамически добавлять строки в таблицу. Однако мы обнаружили, что он не применяет автоматически информацию о стиле из <div> родительского уровня к элементам <input> в строках таблицы, динамически добавляемых в таблицу. В результате, когда мы динамически добавляем строки в определение таблицы, мы явно применяем стиль к элементам <input> в этих строках.

Следуя определению констант, мы определяем несколько переменных ниже, которые инициализируются при загрузке страницы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<script>
  …
  // Commonly used variables
  var currentContact;
  var firstNameVar;
  var lastNameVar;
  var noteVar;
  var contactIdVar;
  var phonesTableVar;
  …
  $(document).ready(function () {
    // Initialize commonly used variables
    firstNameVar = $(‘#firstName’);
    lastNameVar = $(‘#lastName’);
    noteVar = $(‘#note’);
    contactIdVar = $(‘#contactId’);
    phonesTableVar = $(‘#phonesTable’);
    …
  }
  • firstNameVar представляет элемент ввода для имени.
  • lastNameVar представляет элемент ввода для фамилии.
  • noteVar представляет элемент ввода для заметок.
  • contactIdVar представляет идентификатор контакта.
  • phonesTableVar представляет собой таблицу внутри элемента свертываемого блока для телефонных номеров.

Мы используем parseJSON() jQuery parseJSON() для анализа отформатированной в JSON строки контактных данных. Переменная currentContact устанавливается на объект JavaScript, который содержит проанализированную строку в формате JSON. contactId , firstName , lastName и note извлекаются, и соответствующие переменные UI устанавливаются в эти значения, как показано ниже.

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

База данных контактов Android определяет различные типы телефонов. Из этого учебного пособия основное внимание уделяется следующим предметам: «Домой», «Мобильный», «Работа» и «Другое». Каждый телефон идентифицируется тремя переменными: type (тип телефона, как было рассмотрено ранее), rowId (идентификатор телефона в базе данных контактов на устройстве) и no (номер телефона). В нашем приложении номера телефонов хранятся в массиве phonesArr . Пожалуйста, рассмотрите код, приведенный ниже. Мы перебираем массив и для каждого телефона извлекаем type , rowId и no для телефона. Затем мы устанавливаем временные переменные tmpType , tmpRowId , tmpNo для этих значений.

01
02
03
04
05
06
07
08
09
10
11
12
var i;
// Phones
var phonesArr = currentContact.phones;
 
if(phonesArr !== null){
  for(i = 0; i < phonesArr.length; i++){
    var tmpType = (phonesArr[i]).type;
    var tmpRowId = (phonesArr[i]).rowId;
    var tmpNo = (phonesArr[i]).no;
    …

В зависимости от типа каждого телефона мы создаем фрагмент HTML, чтобы определить строку таблицы с информацией об этом телефоне. Например, для номера домашнего телефона, если значение tmpRowId равно 15, а значение tmpNo равно 2223334444, тогда фрагмент

HOME_FRAGMENT_PRX + tmpRowId + HOME_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX
становится

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

Выше 15_1_No внимание, что id элемента ввода составлен как 15_1_No где 15 соответствует идентификатору номера телефона в базе данных контактов, 1 означает тип телефона, а No означает, что этот идентификатор относится к номеру телефона. Уникальность переменной rowId для телефонов гарантирует, что атрибут id является уникальным в документе. Фрагмент добавляется в phonesTableVar через jQuery append() . Детали показаны ниже.

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

Важно, чтобы мы создали поля ввода для всех четырех типов телефонных номеров. Если у контакта есть хотя бы один номер определенного типа, приведенный выше код создаст поле ввода для этого типа телефона. Если у контакта нет номера телефона определенного типа, мы записываем эту информацию и создаем поле ввода для этого типа телефона после выполнения кода выше. Это сделано в листинге кода ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
   
var HOME_PHONE_FRAGMENT_LOCAL = TR_O+’Home’+TD_M+STYLED_IN+’-1_1_No»>’+TD_C;
var MOBILE_PHONE_FRAGMENT_LOCAL = TR_O+’Mobile’+TD_M+STYLED_IN+’-1_2_No»>’+TD_C;
var WORK_PHONE_FRAGMENT_LOCAL = TR_O+’Work’+TD_M+STYLED_IN+’-1_3_No»>’+TD_C;
var OTHER_PHONE_FRAGMENT_LOCAL = TR_O+’Other’+TD_M+STYLED_IN+’-1_7_No»>’+TD_C;
if(phonesArr !== null){
  …
}
if(!homePhoneSet){
  phonesTableVar.append(HOME_PHONE_FRAGMENT_LOCAL);
}
if(!mobilePhoneSet){
  phonesTableVar.append(MOBILE_PHONE_FRAGMENT_LOCAL);
}
if(!workPhoneSet){
  phonesTableVar.append(WORK_PHONE_FRAGMENT_LOCAL);
}
if(!otherPhoneSet){
  phonesTableVar.append(OTHER_PHONE_FRAGMENT_LOCAL);
}

Чтобы интерпретировать вышеуказанный код, давайте рассмотрим рабочий телефонный номер.
Если у контакта нет рабочего номера телефона, значение workPhoneSet равно false . Код выше
добавляет WORK_PHONE_FRAGMENT_LOCAL к phonesTableVar . WORK_PHONE_FRAGMENT_LOCAL переменную, WORK_PHONE_FRAGMENT_LOCAL внимание, что значение WORK_PHONE_FRAGMENT_LOCAL выглядит следующим образом.

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

Это просто создаст пустое поле ввода, где атрибут id имеет значение -1 вместо фактического идентификатора базы данных, определенного через поле rowId .

Наконец, заполнив все элементы пользовательского интерфейса сведениями о контакте, мы отображаем страницу контента для контактных данных. Это конец функции JavaScript setCurrentContact() .

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

Чтобы подвести итог обсуждению выше, следующая последовательность вызовов методов имеет место для отображения существующего контакта.

1
2
3
4
ContactsActivity.loadPage(‘DetailPage.html?<em>id</em>’)
  -> DetailPage.html:$(document).ready()
    -> ContactsActivity.getContact(<em>id</em>, ‘setCurrentContact’)
      -> DetailPage.html:setCurrentContact(json)

Чтобы удалить контакт, пользователь нажимает кнопку «Удалить», показанную на рисунке 14. Это вызывает функцию JavaScript showDialog() которая, как обсуждалось ранее, отображает экран «Подтверждение удаления» на рисунке 4. Если пользователь нажимает кнопку «Отмена», никаких действий не происходит. берется и showDetail() функция JavaScript showDetail() . Появится экран «Существующий контакт».

На экране «Подтвердить удаление», если пользователь нажимает кнопку «Удалить», deleteContact() функция JavaScript deleteContact() , список которых приведен ниже. Видно, что у пользователя отображается экран Progress и вызывается метод ContactsActivity.deleteContact() с двумя параметрами. Первый — это идентификатор контакта, который нужно удалить, а второй — страница отображения обратного вызова, ListPage.html , которая будет отображаться после удаления контакта. То есть после удаления контакта мы бы хотели, чтобы экран «Список контактов» был показан пользователю.

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

Соответствующий код для метода ContactsActivity.deleteContact() прост. Он вызывает метод ContactUtility.deleteContact() , передавая идентификатор удаляемого контакта, а затем загружает нужную страницу обратного вызова в WebView . (Метод ContactUtility.deleteContact() будет рассмотрен позже.)

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

Чтобы сохранить контакт, пользователь нажимает кнопку «Сохранить», показанную на рисунке 14. Это вызывает функцию JavaScript generateJson() , для которой частичный листинг показан ниже.Основное назначение функции — создать строку в формате JSON для редактируемого в данный момент контакта в том же формате, который показан в разделе «Заполнение контактных данных» выше, и передать его в серверную часть Java для сохранения в базе данных контактов. Для краткости мы будем смотреть на генерации contactId, firstName, lastName, noteи phonesполя , как остальная часть кода очень похож.

1
2
3
4
5
6
7
8
function generateJson(){
  showProgress();
  var jsonText = '{';
  jsonText += '"contactId":"' + contactIdVar.val() + '"';
  jsonText += ',"firstName":"' + firstNameVar.val() + '"';
  jsonText += ',"lastName":"' + lastNameVar.val() + '"';
  jsonText += ',"note":{"rowId":"","text":"' + noteVar.val() + '"}';
  …
  • contactId, firstNameИ lastNameполя генерируются в виде простых типов строк путем считывания значений соответствующих элементов пользовательского интерфейса.
  • noteПоле генерируются как тип объекта , где rowIdпередаются , как пустые, потому что поле не важно на Java фонового при сохранении контакта. textПоле установлено значение соответствующего элемента пользовательского интерфейса.

phonesПоле создается как массив типов объектов ( как показано ниже).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
  // Phones
  jsonText += ',"phones":[';
  $('input[id$="_No"]').each(function(index) {
    jsonText += '{"rowId":"","type":"';
    jsonText += (($(this).attr('id')).split('_'))[1] + '","no":"';
    jsonText += $(this).attr('value') + '"';
    jsonText += '},';
  });
  jsonText = <B>addClosingBracket(jsonText);</B>   
  …
  jsonText += '}';
  contactSupport.saveContact(jsonText,'ListPage.html');           
}
 
function addClosingBracket(txt){
  if((txt.length - 1) == txt.lastIndexOf(',')){
    txt = txt.substring(0,txt.length - 1) + ']';
  }else{
    txt += ']';
  }
  return txt;
}
  • Для каждого объекта в массиве rowIdполе передается как пустое, потому что это поле не имеет значения в серверной части Java при сохранении контакта.
  • Значение typeполя определяется путем разбора idатрибута <input>элемента, соответствующего телефону. Например, если id="15_1_No"тогда значение typeравно 1 (Home). Значением noатрибута является просто значение <input>элемента.
  • addClosingBracket()Функция просто срывает последнюю запятую из phonesмассива объектов и добавляет закрывающую скобку « }».
  • Когда генерируется отформатированная строка JSON для контакта, ContactsActivity.saveContact()метод вызывается путем передачи этой строки и страницы обратного вызова для отображения ListPage.html, которая будет показана после сохранения контакта. То есть после того, как контакт сохранен, мы бы хотели, чтобы экран «Список контактов» был показан пользователю.

ContactsActivity.saveContact()Метод показан ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
import org.codehaus.jackson.map.ObjectMapper;
 
public class ContactsActivity extends Activity {
  …
  public void saveContact(String json, String displayPage){
    ObjectMapper mapper = new ObjectMapper();
    try{
      Contact c = mapper.readValue(json,Contact.class);
      ContactUtility.saveOrUpdateContact(c, ...);
    }catch(Exception e){
      …
    }
    loadPage(displayPage);
  }
  …
}

Этот ObjectMapperкласс является точкой входа в JSON Processor Jackson для преобразования строки в формате JSON в представление объекта. Как мы покажем позже, Contactкласс представляет собой сложную структуру данных, совместимую с форматом JSON, представляющим контакт. Другими словами, его поля и объекты соответствуют именам полей и типам данных в формате JSON, представляющем контакт. При наличии этой совместимости ObjectMapper.readValue()метод преобразует текст JSON в экземпляр Contactкласса и возвращает этот экземпляр. Затем ContactsActivity.saveContact()метод передает этот Contactэкземпляр ContactUtility.saveOrUpdateContact()методу для сохранения контакта в базе данных. Наконец, загружается нужная страница обратного вызова, то есть WebViewэкран «Список контактов».


Во второй части учебного цикла мы продемонстрировали, как создать учетную запись для вновь созданных контактов. Мы также описали, как редактировать и удалять существующие контакты.В части 3 мы продолжим обучение, объяснив, как добавить новый контакт. Мы также обсудим, как использовать API Java Java для доступа и управления контактами на устройстве Android.