Этот туториал покажет, где мы остановились в первой части , демонстрируя, как создать учетную запись для вновь созданных контактов. Мы также опишем, как редактировать и удалять существующие контакты.
В первой части этого руководства мы представили пример приложения, которое будет реализовано. Мы также дали обзор элементов страницы в 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
Ответы
Метод 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 строку списка контактов и передаст эту строку в функцию JavaScriptsetContactsList()
в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
В 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 позже.
Мы продолжаем просматривать страницу содержимого для получения контактной информации со следующим списком.
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 развернутый блок для электронной почты расширен, показывая три различных типа электронных писем для контакта.
Рисунок 13. Существующий контакт — часть 2.На следующем рисунке показано, что свернутый блок электронной почты свернут, а свернутый блок «Больше» раскрыт. Складные блоки под надписью «Больше» находятся в свернутом виде.
Рисунок 14. Существующий контакт — часть 3.На следующем рисунке блок для адресов в разделе «Дополнительно» был расширен. Существует три типа адресов: дом, работа, другое.
Рисунок 15. Существующий контакт — часть 4.На следующем рисунке блоки для организаций (слева) и IM (справа) в разделе «Дополнительно» были расширены. Существует два типа организаций (работа, другие) и десять типов IM для контакта.
Рисунок 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
получит сведения о контакте для указанного идентификатора и передаст результаты обратно в функцию JavaScriptsetCurrentContact()
в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
Разбор JSON через JavaScript
Реализация функции 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
экран «Список контактов».
Заключительные замечания к части 2 этого урока
Во второй части учебного цикла мы продемонстрировали, как создать учетную запись для вновь созданных контактов. Мы также описали, как редактировать и удалять существующие контакты.В части 3 мы продолжим обучение, объяснив, как добавить новый контакт. Мы также обсудим, как использовать API Java Java для доступа и управления контактами на устройстве Android.