Android использует технологию SQLite для своей локальной базы данных. Это работает довольно хорошо. Тем не менее, случайные причуды существуют по сравнению с полнофункциональной реляционной базой данных. Одна из таких странностей заключается в том, что SQLite не поддерживает какой-либо тип даты. К счастью, он поддерживает функции даты и способен хранить даты в различных форматах. Этот учебник предоставит вам метод для работы с датами в контексте добавления дат в базу данных приложения «TutList» и (наконец) показ списка учебников, отсортированных по дате.
Это руководство основано на предыдущих руководствах, включая ускоренный курс SQLite для разработчиков Android и продолжение серии наших мероприятий TutList с последним руководством « Основы Android: работа с поставщиками контента» . Если у вас возникли проблемы со сном, не стесняйтесь задавать вопросы в разделе комментариев — многие читают и отвечают, в том числе и мы сами. Кроме того, не забудьте о справочнике по Android SDK .
Окончательный пример кода, прилагаемый к этому учебному пособию, доступен для загрузки с открытым исходным кодом с хостинга кодов Google .
Шаг 0: Начало работы
В этом руководстве предполагается, что вы начнете с того места, где было остановлено предыдущее руководство из серии «Совместимость с Android: индикаторы списка на сотовой основе» . Вы можете скачать этот код и работать оттуда, или вы можете скачать код для этого урока и следовать. В любом случае, будьте готовы, загрузив один или другой проект и импортировав его в Eclipse.
Шаг 1: Обновление базы данных
Чтобы хранить и извлекать даты через поставщика контента, нам нужно обновить базу данных приложения, чтобы хранить даты, связанные с каждой записью контента. Изменение базы данных означает, что вам потребуется обновить версию базы данных, добавить имя нового столбца в качестве константы, предоставить новую начальную схему и выполнить код обновления существующей базы данных. Все эти изменения влияют на класс TutListDatabase.
Начните с обновления версии базы данных до 3.
1
|
private static final int DB_VERSION = 3;
|
Затем добавьте новый столбец для даты, указав имя этого столбца в Java.
1
|
public static final String COL_DATE = «tut_date»;
|
Обновите схему, чтобы включить новый столбец.
1
2
3
4
5
6
|
private static final String CREATE_TABLE_TUTORIALS = «CREATE TABLE «
+ TABLE_TUTORIALS + » (» + ID
+ » INTEGER PRIMARY KEY AUTOINCREMENT, » + COL_TITLE
+ » TEXT NOT NULL, » + COL_URL + » text UNIQUE NOT NULL, «
+ COL_DATE + » INTEGER NOT NULL DEFAULT (strftime(‘%s’,’now’))»
+ «);»;
|
Мы выбрали тип INTEGER для хранения даты. Мы могли бы выбрать REAL для хранения юлианских дат или TEXT для хранения дат в виде строк. Мы используем выражение SQLite «strftime (‘% s’, ‘now’))», которое вставляет текущее время в виде целого числа в базу данных. Значение указывается в секундах и определяется как время Unix, секунды с начала UTC 1970 года.
Наконец, обновите старые схемы внутри метода onUpgrade ().
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 2 && newVersion == 3) {
this value is mid february 2011
db.execSQL(«alter table «+ TABLE_TUTORIALS + » add column » + COL_DATE + » INTEGER NOT NULL DEFAULT ‘1297728000’ «);
} else {
Log.w(DEBUG_TAG, «Upgrading database. Existing contents will be lost. [«
+ oldVersion + «]->[» + newVersion + «]»);
db.execSQL(«DROP TABLE IF EXISTS » + TABLE_TUTORIALS);
onCreate(db);
}
}
|
Обратите внимание, что измененная схема отличается от исходной схемы. SQLite не позволяет использовать выражения или значения текущей даты в качестве значений по умолчанию при изменении таблицы. Использование постоянного значения (мы выбрали середину февраля этого года) является единственным вариантом, за исключением создания новой таблицы и копирования данных — дорогостоящая операция при большом количестве данных. См. Документацию SQLite по ALTER TABLE для получения дополнительной информации об ограничениях с помощью операции ADD COLUMN. Помните об этих ограничениях при разработке схемы базы данных приложения и планировании изменений или обновлений в более позднее время.
Это завершает изменения базы данных, необходимые для поддержки данных даты статьи. Это позволит обновлять старые приложения без потери уже загруженных данных. Хотя это может показаться бесполезным в этом приложении, правильное выполнение этого задания и привыкание к нему сохранят проблемы позже. Кроме того, ваши пользователи будут счастливее.
Шаг 2. Обновление контент-провайдера
При обновлении базы данных приложения вы захотите просмотреть поставщика контента, чтобы увидеть, какие изменения необходимо внести, чтобы привести их в соответствие. В этом случае никаких изменений не требуется. Мы просто добавили новый столбец для даты, который не должен отражаться ни в каких типах URI или других модификациях запросов у поставщика контента.
Легкий шаг, а? Это не всегда так просто, как вы увидите в следующий раз, когда мы пройдем это упражнение.
Шаг 3: Обновление парсера
В классе TutListDownloaderService мы используем Pull Parser для извлечения данных из XML-канала и вставки их в базу данных. Нам нужно получить дату из канала, преобразовать ее в соответствующий формат для базы данных, а затем добавить ее как часть каждой записи базы данных.
В методе xmlParse () добавьте следующую проверку в цепочку проверок для события START_TAG:
01
02
03
04
05
06
07
08
09
10
11
12
|
if (tutorials.getName().equals(«pubDate»)) {
tutorials.next();
DateFormat parser = new SimpleDateFormat(«E, dd MMM yyyy»);
try {
Date date = parser.parse(tutorials.getText());
tutorialData.put(TutListDatabase.COL_DATE,
date.getTime() / 1000);
} catch (ParseException e) {
Log.e(DEBUG_TAG, «Error parsing date: «
+ tutorials.getText());
}
}
|
Вот пример того, как выглядит дата в XML:
1
|
<pubDate>Fri, 20 May 2011 11:30:23 +0000</pubDate>
|
Поскольку нас интересует только дата, мы анализируем ее с помощью класса SimpleDateFormat. После вызова метода parse () у нас появляется стандартный объект Date. Поскольку объект Date хранит значения в миллисекундах, а не в секундах, мы делим его на 1000, прежде чем сохранить результат в базе данных.
Шаг 4: Изменение макета ListView
Дата должна отображаться в ListView вместе с заголовком каждой статьи. В настоящее время ресурс макета list_item.xml содержит один TextView. Теперь измените этот макет, чтобы использовать LinearLayout (вертикально ориентированный), чтобы разместить заголовок над датой. Вот список обновленных файлов list_item.xml.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout
xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:orientation=»vertical»>
<TextView
android:id=»@+id/title»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:textSize=»24dp»
android:padding=»6dp» />
<TextView
android:id=»@+id/date»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:textSize=»18dp»
android:padding=»4dp»
android:gravity=»right» />
</LinearLayout>
|
Не забудьте внести это изменение в альтернативный файл list_item.xml, который также находится в каталоге / layout-v11.
Шаг 5: Обновление фрагмента ListView
В классе TutListFragment необходимо внести несколько изменений, чтобы добавить поддержку сортировки в элемент управления ListView. Проекция курсора должна быть обновлена, чтобы включить столбец даты. Значения привязки для адаптера также должны быть обновлены для добавления данных о дате, а также должен быть настроен новый объект TextView, который будет отображать дату. Наконец, необходимо добавить пользовательский класс ViewBinder, иначе отображаемые даты будут выглядеть неправильно.
Начните с обновления проекции Cursor и добавьте предложение сортировки в методе onCreateLoader (), например:
01
02
03
04
05
06
07
08
09
10
|
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { TutListDatabase.ID, TutListDatabase.COL_TITLE,
TutListDatabase.COL_DATE };
Uri content = TutListProvider.CONTENT_URI;
CursorLoader cursorLoader = new CursorLoader(getActivity(),
content , projection, null, null, TutListDatabase.COL_DATE+» desc»);
return cursorLoader;
}
|
Сортировка по убыванию даты означает, что самые новые элементы появятся вверху.
Затем измените переменные привязки (которые теперь находятся на уровне класса), используемые классом SimpleCursorAdapter, следующим образом:
1
2
3
|
private static final String[] UI_BINDING_FROM = { TutListDatabase.COL_TITLE,
TutListDatabase.COL_DATE };
private static final int[] UI_BINDING_TO = { R.id.title, R.id.date };
|
На этом этапе код должен работать. Тем не менее, результат будет выглядеть так:
Необработанная информация о дате, хотя и отсортирована правильно, не очень полезна для большинства пользователей. Решение состоит в том, чтобы преобразовать необработанное значение обратно в отображаемую дату, используя язык пользователя. Для этого мы можем реализовать объект ViewBinder.
Шаг 6: Реализация ViewBinder
Поскольку мы используем объект SimpleCursorAdapter, мы реализуем собственный класс SimpleCursorAdapter.ViewBinder. Класс ViewBinder позволяет настраивать сопоставление столбца с его объектом View, как указано в предоставленных массивах связывателей. Это означает, что мы можем изменить то, что происходит во время привязки столбца даты к объекту TextView, не вмешиваясь ни в какие другие привязки. Вот пример реализации пользовательского ViewBinder, который выполняет это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
private class TutorialViewBinder implements SimpleCursorAdapter.ViewBinder {
@Override
public boolean setViewValue(View view, Cursor cursor, int index) {
if (index == cursor.getColumnIndex(TutListDatabase.COL_DATE)) {
// get a locale based string for the date
DateFormat formatter = android.text.format.DateFormat
.getDateFormat(getActivity().getApplicationContext());
long date = cursor.getLong(index);
Date dateObj = new Date(date * 1000);
((TextView) view).setText(formatter.format(dateObj));
return true;
} else {
return false;
}
}
}
|
Входящими параметрами для единственного метода, который нам нужно реализовать, setViewValue, являются объект View из предоставленного нами массива UI_BINDING_TO, объект Cursor, с которым работает адаптер, и индекс данных, найденных в Cursor.
Мы используем индекс и объект Cursor, чтобы определить, является ли привязываемое значение датой. Если нет, возвращается false, чтобы указать, что привязка по умолчанию должна иметь место. Если это так, мы переформатируем дату. Использование класса android.text.format.DateFormat позволяет нам получить объект DateFormat для конкретной локали. Поскольку значение хранится в базе данных в секундах, мы должны преобразовать его в миллисекунды. Затем мы можем вызвать setText () для представления, чтобы написать результирующую строку.
Наконец, этот класс ViewBinder должен быть назначен адаптеру. Сразу после инициализации адаптера в методе onCreate () вызовите метод setViewBinder ():
1
|
adapter.setViewBinder(new TutorialViewBinder());
|
Теперь, когда вы запускаете приложение, дата отображается в формате, определенном для локали пользователя (или в формате даты, который вы установили в настройках).
Вывод
Из этого руководства вы узнали, как работать с датами в Android при чтении их из каналов XML, а также при чтении и записи в базы данных SQLite. Кроме того, вы узнали, как вносить пользовательские изменения в отдельные значения, отображаемые в ListView. В контексте TutList вы изменили приложение так, чтобы оно сохраняло даты, проанализированные в XML-ленте учебных пособий, и используете эти даты, чтобы показывать пользователю самые последние руководства в порядке публикации.
Как всегда, мы с нетерпением ждем ваших отзывов!
Об авторах
Разработчики мобильных приложений Лорен Дарси и Шейн Кондер являются соавторами нескольких книг по разработке Android: углубленная книга по программированию под названием « Разработка беспроводных приложений для Android» и « Самс научи себя разрабатывать приложения для Android за 24 часа» . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте [email protected] , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .