Cursor
Изучим объект Cursor. Не путайте его с курсором мыши, который бегает у вас на экране.
Работа с курсором
- Курсор — это набор строк в табличном виде
- Для доступа курсора вы должны использовать метод moveToFirst(), так как курсор размещается перед первой строкой
- Вы должны знать названия столбцов
- Вы должны знать типы столбцов
- Все методы доступа к массивам основываются на номере столбца, поэтому сначала нужно преобразовать название столбца в номер столбца
- Курсор является случайным (random cursor) — вы можете переходить вперед, назад и со строки на строку
- Поскольку курсор является случайным, у него можно запрашивать количество строк (row count)
Класс Cursor содержит немало возможностей для навигации (но не ограничивается только ими):
- moveToFirst() — перемещает курсор на первую строку в результате запроса;
- moveToNext() — перемещает курсор на следующую строку;
- moveToLast() — перемещает курсор на последнюю строку;
- moveToPrevious() — перемещает курсор на предыдущую строку;
- getCount() — возвращает количество строк в результирующем наборе данных;
- getColumnIndexOrThrow() — возвращает индекс для столбца с указанным именем (выбрасывает исключение, если столбец с таким именем не существует);
- getColumnName() — возвращает имя столбца с указанным индексом;
- getColumnNames() — возвращает массив строк, содержащий имена всех столбцов в объекте Cursor;
- moveToPosition() — перемещает курсор на указанную строку;
- getPosition() — возвращает текущую позицию курсора
Также Android предоставляет следующие методы:
- isBeforeFirst()
- isAfterLast() — полезный метод, сигнализирующий о достижении конца запроса. Используется в циклах
- isClosed()
И другие методы, о которых можно узнать в документации или из примеров.
Курсор обязательно следует закрывать методом close() для освобождения памяти.
Наглядно о курсорах
Чтобы было проще понять, что такое курсоры, представляйте их в виде таблицы. Пусть у нас есть таблица из столбцов: _id (идентификатор) и catname (имя котов). Допустим, мы ввели в базу имена четырех котов и таблица базы данных выглядит таким образом:
_id | catname |
---|---|
1 | Мурзик |
2 | Васька |
3 | Барсик |
4 | Рыжик |
Как было сказано выше, при работе с курсорами необходимо вызвать метод moveToFirst() (перейти к первой строке), после чего таблица будет выглядеть следующим образом:
_id | catname |
---|---|
1 | Мурзик |
2 | Васька |
3 | Барсик |
4 | Рыжик |
Как видите, после вызова метода первая строчка таблицы подсвечена. Именно данные этой строки и содержит сейчас курсор. Можно проверить следующим образом. Добавим новую кнопку в проект и напишем код:
На первой строке содержатся данные 1, Мурзик. Мы не знаем, как хранятся данные в курсоре, но нам это и не нужно. С помощью метода getColumnIndex() с указанием имени колонки мы можем извлечь данные, которые хранятся в них.
Теперь вызовем метод moveToNext() (перейти к следующей строке). Таблица будет выглядеть уже так:
_id | catname |
---|---|
1 | Мурзик |
2 | Васька |
3 | Барсик |
4 | Рыжик |
Код для проверки:
Если вызвать метод moveToNext() ещё раз, то переместимся на третью позицию. А теперь представьте ситуацию, что у нас в базе более ста котов, и чтобы узнать имя 85-го кота, нам придётся 85 раз вызывать метод. Не удобно. К счастью, есть метод moveToPosition() (перейти в позицию), в котором сразу можно указать нужную строку (отсчет идет от 0):
А таблица выглядит уже так:
_id | catname |
---|---|
1 | Мурзик |
2 | Васька |
3 | Барсик |
4 | Рыжик |
Надеюсь, вы поняли общий принцип работы с курсором. Теперь вы можете понять, как выглядит курсор после вызова метода moveToLast() (перейти на последнюю запись).
Если нам надо получить имена всех котов из таблицы базы данных, то нужно последовательно вызывать методы moveToNext(). Это проще сделать через цикл. Условием для остановки цикла является проверка возвращаемого значения метода. Если вернётся значение false, значит мы дошли до конца таблицы. В данном случае не нужно вызывать метод moveToFirst(), чтобы не пропустить первую запись:
Цикл можно переписать по другому. Метод isAfterLast() возвращает true, когда курсор с последней записи пытается переместиться в никуда. А пока курсор возвращает false, можно двигать его на следующую позицию. Пример будет выглядеть так:
В примерах мы извлекали строковое значение записи через метод getString():
По аналогии можно получить числовое значение, например, номер ресурса изображения.
Думаю, приведённых примеров достаточно, чтобы понять с чем едят курсоры. Они совсем не страшные.
Устаревшие методы (deprecated)
Начиная с Android 3.0, многие методы для работы с курсором считаются устаревшими.
- startManagingCursor()
- stopManagingCursor()
- managedQuery()
- reQuery()
При использовании устаревших методов вы можете получить исключение типа:
Кроме того, студия будет подчёркивать устаревшие методы, от которых желательно избавляться в новых проектах.
Наиболее распространён метод managedQuery(), в сети постоянно натыкаюсь на примеры с использованием данного метода.
Обычно, код выглядит следующим образом:
Данный код следует переработать следующим образом:
Метод reQuery() следует заменить на вызов LoaderManager.
Класс CursorLoader и связанный с ним LoaderManager гарантируют, что запросы будут выполняться асинхронно.
Мне пока не приходилось использовать данный приём в своей практике, поэтому просто скопирую из других источников:
- реализуйте интерфейс в вашем классе как LoaderManager.LoaderCallbacks
- в методе onCreate() инициализируйте loader как First implement the interface in your class as getLoaderManager().initLoader(0, null, this);
- вместо reQuery используйте getLoaderManager().restartLoader(0, null, this);
- переопределите три метода onCreateLoader(), onLoadFinished(), onLoaderReset()
MatrixCursor
Иногда попадаются примеры с использованием класса MatrixCursor. Сам пока не изучал, оставлю вам в качестве домашнего задания. Небольшой пример на память:
Источник
SimpleCursorAdapter
Класс SimpleCursorAdapter позволяет назначать данные курсора, используемые источником данных, для компонентов.
Существует две версии адаптера: android.support.v4.widget.SimpleCursorAdapter и android.SimpleCursorAdapter.
Схема работы адаптера представлена на рисунке.
В левой части представлен компонент на основе класса AdapterView, например, ListView, состоящих из отдельных элементов TextView. В правой части находятся данные, которые могут поступать из базы данных или контент-провайдера. Адаптер берёт по порядку первый элемент списка и первый ряд выбранного столбца из таблицы данных и привязывает их с идентификатором разметки отдельного элемента списка.
Класс SimpleCursorAdapter — это подкласс класса CursorAdapter, который был разработан для облегчения отображения столбцов класса Cursor непосредственно на компоненты TextView или ImagesView, определенные в XML-разметке.
Общий код для адаптера:
Конструктор класса получает следующие параметры:
- параметр Context, в котором выполняется компонент ListView или ему подобный
- идентификатор ресурса разметки, который используется для отображения каждого элемента в ListView
- класс Cursor, который обеспечивает доступ к данным, — этому аргументу можно присвоить значение null, если класс Cursor определяется позже
- массив String, включающий отображаемые названия столбцов
- массив типа int, включающий идентификаторы соответствующих ресурсов интерфейса
- параметр типа int — флаг
Для создания класса SimpleCursorAdapter сначала определите массивы, включающие имена столбцов, для отображения на компоненты GUI. Также следует определить ID ресурсов для компонентов GUI, которые отображают данные из именованных столбцов. В коде создается массив String, показывающий, что может отображаться лишь именованный столбец. Затем создается параллельный массив типа int, содержащий ID ресурсов для соответствующих компонентов GUI. Потом создается класс SimpleCursorAdapter.
Размеры массивов fromColumns и toViews должные совпадать. К примеру, вы можете задать два столбца для извлечения данных и соответственно должны указать два идентификатора, например, для TextView для текста и ImagesView для картинки.
Имейте в виду, что один из конструкторов адаптера теперь устарел. Вы его можете иногда встречать в старых проектах, но копировать в свой проект его не стоит. Дело в том, что его работа происходит в одном потоке с интерфейсом программы и может серьёзно затормозить работу списка. Теперь рекомендуется использовать новый класс CursorLoader, который можно считать аналогом AsyncTask для адаптеров, использующий асинхронные операции. Второй конструктор имеет дополнительный параметр flags. Для быстрой переделки старого кода без использования CursorLoader вы можете просто добавить в код данный флаг CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER или SimpleCursorAdapter.FLAG_AUTO_REQUERY или просто использовать значение 0.
Если стандартный адаптер вас не устраивает, можно его переопределить. Кусок кода из одного проекта.
Источник
Полный список
В Уроке 52 использованы устаревшие на данный момент методы запроса данных от БД и связки Activity с Cursor. Вместо них рекомендуется использовать CursorLoader, который будет асинхронно читать данные и возвращать Cursor. Этот урок будет являться копией Урока 52 только с использованием CursorLoader.
CursorLoader представляет собой наследника класса AsyncTaskLoader и по умолчанию заточен на работу с ContentProvider, т.к. при работе требует Uri. Мы же в этом примере используем его для работы со своей БД. Для этого нам придется его расширить и вставить свою реализацию в его основной метод.
Приложение урока — это список, который отображает содержимое БД. Кнопкой можно записи добавлять, а контекстным меню — удалять.
Project name: P1361_CursorLoader
Build Target: Android 2.3.3
Application name: CursorLoader
Package name: ru.startandroid.develop.p1361cursorloader
Create Activity: MainActivity
В strings.xml добавим строки:
Кнопка добавления записи и список
Layout пункта списка item.xml:
Статичная картинка и текст.
Работу с БД вынесем в отдельный класс DB.java:
Здесь создание БД, управление подключением и методы по чтению/добавлению/удалению записей.
В onCreate подключаемся к БД, создаем SimpleCursorAdapter, добавляем контекстное меню к списку и создаем CursorLoader. Я везде для лоадера буду использовать >
В onButtonClick добавляем запись в БД, получаем лоадер и просим его получить для нас новый курсор с данными.
onCreateContextMenu – создание контекстного меню.
В onContextItemSelected мы реализуем удаление записи из БД. И после удаления снова просим лоадер дать нам новый курсор с данными.
В onDestroy отключаемся от БД.
Далее идут колбэк-методы интерфейса LoaderCallbacks.
В onCreateLoader создаем Loader и даем ему на вход объект для работы с БД.
В onLoadFinished мы получаем результат работы лоадера – новый курсор с данными. Этот курсор мы отдаем адаптеру методом swapCursor.
MyCursorLoader – наш лоадер, наследник класса CursorLoader. У него мы переопределяем метод loadInBackground, в котором просто получаем курсор с данными БД. Ну и я 3-х секундной паузой сэмулировал долгое чтение БД для наглядности асинхронной работы.
Все сохраняем, запускаем пример. Работает и добавление по нажатию кнопки и удаление через контекстное меню. Работает с задержкой в 3 секунды, но при этом не тормозит интерфейс, т.к. работа выполняется асинхронно.
Кроме асинхронной загрузки, CursorLoader:
— закрывает старый курсор при успешном получении нового
— закрывает курсор при уничтожении лоадера (т.е. и при выходе из приложения)
— при переходе в состояние «стартован» проверяет метку, которую ставит Observer и запускает работу, если данные изменились
— при переходе в состояние «стартован» стартует работу, если еще не было получено никаких результатов (например при первом запуске).
На следующем уроке:
— читаем данные с сенсоров
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Using multicursor in Android Studio
One of the most useful methods for editing multiple lines of code or XML in Android Studio (or IntelliJ) is multicursor. This is a feature that makes it possible to edit multiple lines at the same time. There are a few shortcuts you need to remember before you can really take advantage of multicursor, and this is basically a cheat sheet with the most useful multicursor-related shortcuts.
Multiple carets
There are a few different ways to add and remove carets, depending on the situation.
With the mouse, use Shift+Alt+Click to add a caret and Shift+Alt+Click on a caret to remove it. If you need a faster way to add a bunch of carests, you can press Alt while clicking and dragging vertically to select a block or a line.
With the keyboard, press Ctrl (Windows & Linux) / Alt (macOS) twice, and then without releasing it, press up or down arrow keys. To delete all the existing carets, except the primary one, press Esc .
Multiple selections
There are also several ways to select multiple occurrences of the same text.
You can manually create new selections by pressing Shift+Alt and double-clicking the left mouse button. If you want to select matching pieces of text, press Alt+J (Windows & Linux) / Ctrl+G (macOS) to automatically find and select the next occurrence of the currently selected text.
If you need to select all the occurrences, you can speed it up by using Shift+Ctrl+Alt+J (Windows & Linux) / Ctrl+Cmd+G (macOS).
Editing
When you make multiple selection, you can edit all the pieces of text at the same time, and use Esc to deselect all, except the primary one.
Источник