Android SimpleCursorAdapter не обновляется при изменении базы данных

У меня есть Android ListActivity, который поддерживается базой данных Cursor через SimpleCursorAdapter.

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

Проблема в том, что когда обновленное представление исчезает с экрана и перерабатывается, старое значение отображается в представлении, когда оно возвращается в представление. То же самое происходит всякий раз, когда список перерисовывается (меняется ориентация и т. д.).

Я использую notifydatasetchanged() для обновления адаптера курсора, но это кажется неэффективным.

Как мне обновить базу данных, чтобы курсор также обновлялся?


person CodeFusionMobile    schedule 31.12.2009    source источник


Ответы (6)


Вызовите requery() на Cursor, когда вы изменяете данные в базе данных, которые вы хотите отразить в этом Cursor (или что-то, что заполняет Cursor, например ListView через CursorAdapter).

Cursor подобен курсору на стороне клиента ODBC — он содержит все данные, представленные результатом запроса. Следовательно, только потому, что вы изменили данные в базе данных, Cursor не узнает об этих изменениях, пока вы не обновите его через requery().


ОБНОВЛЕНИЕ: весь этот вопрос и набор ответов должны быть удалены из-за возраста, но это, по-видимому, невозможно. Любой, кто ищет ответы на вопросы Android, должен иметь в виду, что Android — это быстро меняющаяся цель, а ответы за 2009 год обычно хуже, чем новые ответы.

Текущее решение состоит в том, чтобы получить свежий Cursor и использовать либо changeCursor(), либо swapCursor() на CursorAdapter, чтобы повлиять на изменение данных.

person CommonsWare    schedule 31.12.2009
comment
Что тогда делает notifydatachanged? - person CodeFusionMobile; 31.12.2009
comment
Уведомление об изменении данных отсутствует. Если вы имеете в виду notifyDataSetChanged() для адаптера, именно так SimpleCursorAdapter сообщает ListView, что данные были изменены. Чтобы процитировать документацию, уведомляет прикрепленное представление о том, что базовые данные были изменены, и оно должно обновиться. Однако ваша проблема не в том, что адаптер сообщает ListView об изменении — ваша проблема в том, что адаптер не знает, что данные изменились. Вызов requery() — это способ решить эту проблему с помощью CursorAdapter. - person CommonsWare; 31.12.2009
comment
Теперь это имеет смысл. Я неправильно понял поток данных. - person CodeFusionMobile; 31.12.2009
comment
requery() устарел. Вместо этого вам следует выпустить новый Cursor и отправить его вашему CursorAdapter через adapter.changeCursor(myCursor). см. документы в уведомлении об устаревании. - person Paul Lammertsma; 14.11.2011

requery больше не рекомендуется. из документации:

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

после получения нового курсора можно использовать theadapter.changeCursor(cursor). это должно обновить представление.

person rony l    schedule 15.06.2011
comment
Спасибо за информацию, у меня был сбой в Honeycomb, связанный с cursor.requery(), возможно, это стоит за этим. - person CodeFusionMobile; 15.06.2011
comment
Обычная ситуация - изменить значение из onClickListener() Меня озадачивает то, что, с одной стороны, мы отправляем начальный курсор через конструктор (т.е. запрос пишется вне Adpater). Но с другой стороны, onlickLister определяется как вложенный класс внутри адаптер. Таким образом, запрос на новый курсор будет написан внутри класса адаптера. Это может привести к дублированию кода. Каков правильный дизайн для написания запроса только один раз в исходном коде? - person Frederic Bazin; 25.09.2012

В случае использования загрузчика и автоматически сгенерированного курсора вы можете вызвать:

getLoaderManager().restartLoader(0, null, this);

в вашей деятельности, сразу после изменения чего-либо в БД, чтобы восстановить новый курсор. Не забудьте также определить обработчики событий:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    CursorLoader cursorLoader =
            new CursorLoader(this,
                    YOUR_URI,
                    YOUR_PROJECTION, null, null, null);
    return cursorLoader;
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    adapter.swapCursor(null);
}
person siefca    schedule 18.07.2012
comment
Перепробовал полдюжины разных вещей из Stack Overflow, и это, наконец, сработало для меня. Спасибо! - person Rudism; 02.05.2013
comment
Это именно тот ответ, который я искал, используя простой адаптер и вызывая swapCursor onLoadFinished. - person Matt; 27.12.2013
comment
Сработало, но мне пришлось добавить это: поскольку фрагмент списка регистрирует щелчок пользователя, вызывающий обновление значений строки, которая затем должна быть вставлена ​​в БД, новый курсор, автоматически созданный новым загрузчиком курсора в onCreateLoader(), должен был быть передан (в моем случае) пользовательскому адаптеру, расширяющему simpleCursorAdapter, потому что я не нашел другого способа уведомить метод getView() (выполняющий обновление представления) о новом курсоре. GetView() использовал старый курсор до перезапуска всего фрагмента. Поэтому я сделал setCursor() в пользовательском адаптере и вызвал его из onCreateLoader(). Тогда это сработало. - person carl; 14.04.2014

Мне не ясно, если вы установите для свойства autoRequery CursorAdapter значение true.

Адаптер проверит свойство autoRequery; если это false, то курсор не изменится.

person gjican    schedule 12.09.2012
comment
protected void init (Context context, Cursor c, boolean autoRequery) Этот метод устарел. Не используйте это, используйте обычный конструктор. Это будет удалено в будущем. - person Mussa; 27.05.2015

requery() уже устарел, просто реализуйте простой метод updateUI() в дочернем классе вашего CursorAdapter и вызывайте его после обновления данных:

private void updateUI(){
    swapCursor(dbHelper.getCursor());
    notifyDataSetChanged();
}
person Jaroslav    schedule 11.02.2015

Это просто.

private Db mDbAdapter;
private Cursor mCursor;
private SimpleCursorAdapter mCursorAd;

.....................................
//After removing the item from the DB, use this
.....................................

 mCursor = mDbAdapter.getAllItems();
 mCursorAd.swapCursor(mCursor);

Или используйте CursorLoader...

person Shwarz Andrei    schedule 27.10.2015