Вопрос по android, java – Как я могу обновить свой ListFragment, когда он возвращается к макету из заднего стека?

9

Сначала я должен упомянуть, что я использую библиотеку ActionBarSherlock для обратной совместимости.

У меня есть активность, которая добавляетListFragment когда это впервые началось. У меня есть обычайLoader который я реализовал и следуетПример AsnycTaskLoader очень близко. мойListFragment реализуетLoaderCallbacks<Cursor> интерфейс. Все соответствующие методы обратного вызова вызываются при добавлении фрагмента (onCreateLoader() , onLoaderFinished() ) и когда он будет заменен (onLoaderReset() ).

мойonActivityCreated(Bundle) Метод выглядит так:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mAccountsDbAdapter = new AccountsDbAdapter(getActivity().getApplicationContext());

    setHasOptionsMenu(true);
    mCursorAdapter = new AccountsCursorAdapter(getActivity()
            .getApplicationContext(), R.layout.list_item_account, null,
            new String[] { DatabaseHelper.KEY_NAME },
            new int[] { R.id.account_name }, 0);

    setListAdapter(mCursorAdapter); 
    getLoaderManager().initLoader(0, null, this);
}

ПозжеListFragment заменяется другим фрагментом B. Когда пользователь нажимает кнопку «Назад», фрагмент B удаляется, а ListFragment добавляется снова. Тем не менее, список пуст и толькоandroid:empty элементы отображаются, и ни один из методов LoaderCallback не вызывается. Я могу использовать отладчик, чтобы определить, чтоgetLoaderManager().initLoader(0, null, this); на самом деле называется, но больше ничего. Когда я изменяю это наgetLoaderManager().restartLoader(0, null, this);обратные вызовы вызываются, но мой список остается пустым (хотя данные есть, представление не обновляется).

Как я могу заставить свой ListFragment обновлять себя, когда он возвращается в макет? Кто-нибудь сталкивался с этим раньше, как вы это исправили?

К вашему сведению, вот мои методы обратного вызова

    @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    return new AccountsCursorLoader(this.getActivity()
            .getApplicationContext());
}

@Override
public void onLoadFinished(Loader<Cursor> loaderCursor, Cursor cursor) {
    mCursorAdapter.swapCursor(cursor);
    mCursorAdapter.notifyDataSetChanged();
}

@Override
public void onLoaderReset(Loader<Cursor> arg0) {
    mCursorAdapter.swapCursor(null);
}

Некоторые заметки:

  1. I cannot use the setListShown(true) methods in the example because I get an IllegalStateException that it cannot be used with a custom content view.
  2. My AccountsCursorAdapter extends a SimpleCursorAdapter and modifies only the bindView() method.
@ Ян-Хенк Я нашел решение своего вопроса (см. Мой ответ ниже). Я не знаю, относится ли это и к вашей ситуации. codinguser
Я столкнулся с той же проблемой, поэтому я также заинтересован в решении этой проблемы. Сейчас я просто начинаю другую деятельность в качестве обходного пути, но в этом не должно быть необходимости. Jan-Henk

Ваш Ответ

7   ответов
0

что ListView просто не был нарисован на экране по какой-то причине. Если бы я пошел на домашний экран, а затем снова переключился на активность, все выглядело бы так, как ожидалось. Поэтому я искал метод, чтобы вызвать перерисовку экрана, и я нашел следующее:

Заставить полноэкранную активность переиздать / перерисовать резюме?

Основываясь на ответе на этот вопрос, я добавил следующий код кonActivityCreated() метод моего ListFragment:

getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 0);

С этим кодом все работает как положено. Но это все еще обходной путь, поэтому, если кто-то даст ответ на этот вопрос с лучшим решением или объяснением, я назначу ему награду.

Спасибо за ответ Ян (и за установление щедрости). Я попробую этот подход, когда доберусь до своей машины. Но дело в том, что даже если вы вызовете перерисовку, если проблема связана с загрузчиком курсора или каким-либо другим механизмом обратного вызова, не будет ли он все еще перерисовывать пустое представление? В моем случае,initLoader() называется, ноonLoadFinished() никогда не вызывается, что (я думаю) означает, что данные фактически не загружены. codinguser
К сожалению, я проверил это, и это не работает для меня. codinguser
5

TL;DR;

вAccountsListFragmentТеперь я создаю свой адаптер базы данных (AccountsDatabaseAdapter) вonCreate() метод и закрыть его вonDestroy() метод, и теперь он работает. Ранее я создавал свой адаптер вonActivityCreated() и приближаетсяonDestroyView(), Обратите внимание, что я не имею в видуListAdapter, а скорее к моей базе данныхAccountsDbAdapter.

Attempt at long explanation:

Ну, я делал это, потому что я думал, что получение контекста черезgetActivity() не будет возможно вonCreate() метод. Оказывается, вы можетеgetActivity() даже доonActivityCreated() называется.

Но я не могу объяснить, почему это сейчас работает, потому чтоLoader имеет свойDatabaseAdapter объект, который он использует для извлечения данных. Если бы я догадался, я бы сказал, что один и тот же объект базы данных возвращается для обоих адаптеров базы данных (база данных кэшируется). Что означало бы, что когда я закрою один вonDestroyView()другой также закрывается, и набор данных курсора становится недействительным, что приводит к пустому представлению списка. Загрузчик не перезагружается, потому что считает, что данные не изменились.

Но даже это объяснение не удовлетворяет меня полностью, потому что некоторые из предложенных здесь решений, которые я пробовал как принудительный перезапуск загрузчика каждый раз, не работали. (вloadInBackground() метод, новыйDatabaseAdapter создается каждый раз).

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

Спасибо всем за помощь в этом!

В моем случае я нашел исправление, которое сработало, но я сам не очень понимаю. Более того, я никогда не сталкивался с этой проблемой, пока создавал несколько похожих кусков кода. Отныне я просто буду всегда создавать экземпляры своих ListAdapters в методах onCreate () моих фрагментов, чтобы избежать этой проблемы.
Завтра я посмотрю на это, но приятно видеть, что вы нашли решение своей проблемы.
хорошая работа @codinguser! Мне также все еще не терпится узнать, почему это работает и почему принудительный перезапуск загрузчика не сработал! Интересно, вы переместили этот же код вonCreateView() если поведение все еще будет работать
Рад, что это работает для вас. Теперь я отмечу это как принятое решение. codinguser
В своем коде я переместил создание ResourceCursorAdapter из onActivityCreated () в метод onCreate (), и это решает проблему для меня. Я также не очень хорошо понимаю это исправление, но я знаю одно: метод onCreate () запускается только один раз, а метод onActivityCreated () - каждый раз, когда фрагмент становится видимым на экране. Кажется вероятным, что это как-то связано с проблемой. Но спасибо за ваше исправление!
0

getLoaderManager() который не будет отправлять обратный вызов в правильное место.

Если вы используете ActionBarSherlock, значит, вы на Android 2.x не так ли? Рассматривали ли вы использовать пакет поддержки Android v4?http://developer.android.com/sdk/compatibility-library.html

Вы бы тогда использовалиandroid.support.v4.app.ListFragment и получите свойLoaderManagerпозвонив по телефонуgetActivity().getSupportLoaderManager()

Я тестирую как под Android 2.2, так и под 4.0.4. Это не работает ни в одном случае. Я тоже пробовалgetActivity().getSupportLoaderManager() и все равно не работает. Спасибо за предложение, хотя. codinguser
1
What the Adapter sees:

по которой вы наблюдаете пустой ListView, заключается в том, чтоthe position of the Cursor that was returned is STILL pointing at the last element, Когда тыswap() курсор на адаптер, адаптер пытается перебрать с помощьюwhile(Cursor.moveToNext()) петля. Поскольку этот цикл всегда оценивает FALSE, ваш ListView дает вам иллюзию пустого курсора.

Распечатайте значенияCursor.getCount() а такжеCursor.getPosition() вonLoadFinished(), Если я прав, эти два значения должны быть равны. Это столкновение индексов создает вышеуказанную иллюзию.

Why does the Adapter see this:

Загрузчики будут повторно использовать Курсор всякий раз, когда это возможно. Если вы запрашиваете загрузчик для набора данных, который не изменился, загрузчик умный и возвращает курсор через onLoadFinished, не делаяany дополнительная работа, даже не устанавливая положение курсора в -1.

ANS ВызовCursor.moveToPosition(-1) вonLoadFinished() вручную, чтобы обойти эту проблему.

1

Пытатьсяforceload ():

getLoaderManager().getLoader( 0 ).forceLoad();
Спасибо, Кристиан. Я пробовал, но, к сожалению, у меня не работает. codinguser
0

знаете ли вы, если у вас возникают те же проблемы при использовании родного CursorLoader? Если нет, то, возможно, вы можете сравнить ваш Loader с источником Android и посмотреть, делают ли они что-то по-другому?

https://github.com/android/platform_frameworks_base/blob/master/core/java/android/content/CursorLoader.java

Добавил один и зарегистрировал его с помощью курсора, как в примере с CursorLoader, но ничего не вышло. codinguser
Большая разница, которую я вижу с вашим, заключается в том, что не используетForceLoadContentObserver (developer.android.com/reference/android/content/…) которыйCursorLoader делает.
Спасибо за ответ. Я не могу протестировать CursorLoader по умолчанию, потому что у меня нет ContentProvider для моей базы данных (и я пытался избежать его создания, поскольку он мне не нужен). Код для кастомаDatabaseCursorLoader который я написал, доступен здесь:goo.gl/flHZk codinguser
2

только после изменения базового набора данных?

Пытаться:

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

Похожие вопросы