简体   繁体   English

当从后端堆栈返回布局时,如何刷新ListFragment?

[英]How can I refresh my ListFragment when it returns to the layout from back stack?

First I should mention that I am using the ActionBarSherlock library for backwards compatibility. 首先我要提一下,我正在使用ActionBarSherlock库来实现向后兼容性。

I have an activity which adds a ListFragment when it is first started. 我有一个活动,它在首次启动时添加了ListFragment I have a custom Loader which I implemented and follows the AsnycTaskLoader example very closely. 我有一个自定义Loader ,我实现并非常密切地遵循AsnycTaskLoader示例 My ListFragment implements the LoaderCallbacks<Cursor> interface. 我的ListFragment实现了LoaderCallbacks<Cursor>接口。 All the appropriate callback methods are called when the fragment is added ( onCreateLoader() , onLoaderFinished() ) and when it is replaced ( onLoaderReset() ). 当加入片段被称为所有适当的回调方法( onCreateLoader() onLoaderFinished()当它被替换( onLoaderReset()

My onActivityCreated(Bundle) method looks like this: 我的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);
}

Later on, the ListFragment is replaced with another Fragment B. When the user presses the back button, Fragment B is removed and the ListFragment is added again. 稍后, ListFragment将替换为另一个片段B.当用户按下后退按钮时,将删除片段B并再次添加ListFragment。 However, the list is empty and only the android:empty elements are displayed and none of the LoaderCallback methods are called. 但是,列表为空,只显示android:empty元素,并且不调用任何LoaderCallback方法。 I can use the debugger to determine that getLoaderManager().initLoader(0, null, this); 我可以使用调试器来确定getLoaderManager().initLoader(0, null, this); is actually called, but nothing else. 实际上是被称为,但没有别的。 When I change it to getLoaderManager().restartLoader(0, null, this); 当我将其更改为getLoaderManager().restartLoader(0, null, this); , the callbacks get called, but still my list remains empty (although there is data, the view is not refreshed). ,回调被调用,但我的列表仍然是空的(虽然有数据,视图不刷新)。

How can I get my ListFragment to refresh itself when it is returned to the layout? 如何在返回布局时让ListFragment自行刷新? Has anyone encountered this before, how did you fix it? 有没有人遇到过这个,你是怎么解决的?

FYI, here are my callback methods 仅供参考,这是我的回调方法

    @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);
}

Some notes: 一些说明:

  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. 我不能在示例中使用setListShown(true)方法,因为我得到一个IllegalStateException ,它不能与自定义内容视图一起使用。
  2. My AccountsCursorAdapter extends a SimpleCursorAdapter and modifies only the bindView() method. 我的AccountsCursorAdapter扩展了SimpleCursorAdapter并仅修改了bindView()方法。

Eureka!!! 尤里卡! I found it (by accident, of course) 我找到了(当然是偶然的)

TL;DR; TL; DR;

In the AccountsListFragment , I now create my database adapter ( AccountsDatabaseAdapter ) in the onCreate() method and close it in the onDestroy() method and it now works. AccountsListFragment中 ,我现在在onCreate()方法中创建我的数据库适配器( AccountsDatabaseAdapter )并在onDestroy()方法中将其关闭,现在它可以正常工作。 Previously I was creating my adapter in the onActivityCreated() and closing in onDestroyView() . 以前我在onActivityCreated()创建我的适配器并在onDestroyView()关闭。 Note that I am not referring to the ListAdapter , but rather to my database interfacing AccountsDbAdapter . 请注意,我不是指ListAdapter ,而是指我的数据库连接AccountsDbAdapter

Attempt at long explanation: 尝试很长的解释:

Well, I was doing this because I thought that getting context through getActivity() will not be possible in the onCreate() method. 好吧,我这样做是因为我认为在onCreate()方法中无法通过getActivity()获取上下文。 It turns out you can getActivity() even before onActivityCreated() is called. 事实证明,即使在调用onActivityCreated()之前,你也可以使用getActivity()

But I cannot really explain why this now works because the Loader has its own DatabaseAdapter object which it uses to retrieve the data. 但我无法解释为什么现在这样可行,因为Loader有自己的DatabaseAdapter对象,用于检索数据。 If I were to guess, I would say that the same database object is returned for both database adapters ( the database is cached ). 如果我猜,我会说两个数据库适配器都返回相同的数据库对象( 数据库被缓存 )。 Which would mean that when I close one in onDestroyView() , the other is also closed and the cursor data set becomes invalid which results in an empty list view. 这意味着当我在onDestroyView()关闭一个时,另一个也关闭,光标数据集变为无效,这导致空列表视图。 The loader does not reload because it thinks the data has not changed. 加载程序不会重新加载,因为它认为数据没有更改。

But even this explanation does not satisfy me completely, because some of the suggested solutions here which I tried like force restarting the loader each time did not work. 但即使这样的解释也不能完全满足我的要求,因为我尝试的一些建议的解决方案每次强制重启加载器都不起作用。 (in the loadInBackground() method, a new DatabaseAdapter is created each time). (在loadInBackground()方法中,每次都会创建一个新的DatabaseAdapter )。

Anyway, if you happen to have a better understanding of what is going on, please hammer away in the comments. 无论如何,如果您碰巧对正在发生的事情有了更好的了解,请在评论中聆听。

Thanks everyone for the help with this! 感谢大家对此的帮助!

Have you tried forcing the loader to restart in your activity, just after underlying data set is changed? 您是否尝试在基础数据集更改后强制加载器重新启动您的活动?

Try: 尝试:

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

尝试forceload()

getLoaderManager().getLoader( 0 ).forceLoad();

What the Adapter sees: 适配器看到了什么:

The reason why you observe an empty ListView is because the position of the Cursor that was returned is STILL pointing at the last element . 您观察空ListView的原因是因为返回的Cursor的位置是STILL指向最后一个元素 When you swap() the Cursor to an Adapter, the Adapter tries to iterate using a while(Cursor.moveToNext()) loop. 当您将Cursor swap()转换为适配器时,适配器尝试使用while(Cursor.moveToNext())循环进行迭代。 Because that loop always evaluates FALSE, your ListView gives you the illusion of an empty Cursor. 因为该循环总是计算FALSE,所以ListView会给你一个空Cursor的错觉。

Print the values of Cursor.getCount() and Cursor.getPosition() in onLoadFinished() . onLoadFinished()打印Cursor.getCount()Cursor.getPosition()的值。 If I am correct, these two values should be equal. 如果我是正确的,这两个值应该相等。 This clash of indexes creates the above illusion. 这种索引的冲突造成了上述错觉。

Why does the Adapter see this: 为什么适配器看到这个:

Loaders will re-use a Cursor whenever possible. 加载器将尽可能重用光标。 If you request a Loader for a set of data that has not changed, the loader is smart and return the Cursor via onLoadFinished without doing any additional work, not even setting the position of the Cursor to -1. 如果您为一组未更改的数据请求Loader,则加载程序是智能的并通过onLoadFinished返回Cursor而不进行任何额外的工作,甚至不将Cursor的位置设置为-1。

ANS Call Cursor.moveToPosition(-1) in onLoadFinished() manually to work around this problem. ANS呼叫Cursor.moveToPosition(-1)onLoadFinished()手动来解决这个问题。

In my case I found out that the ListView simply was not drawn on the screen for some reason. 在我的情况下,我发现由于某种原因,ListView根本没有在屏幕上绘制。 If I went to the home screen and then switched back to the activity everything would appear as expected. 如果我进入主屏幕然后切换回活动,一切都会按预期显示。 So I searched for a method to force a redraw of the screen and I found the following: 所以我搜索了一种强制重绘屏幕的方法,我找到了以下内容:

Force a full screen Activity to remeasure/redraw on resume? 强制全屏活动重新测量/重绘简历?

Based on the answer to that question I added the following code to the onActivityCreated() method of my ListFragment: 基于该问题的答案,我将以下代码添加到ListFragment的onActivityCreated()方法中:

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

With this code in place everything works as expected. 有了这个代码,一切都按预期工作。 But this is still a workaround, so if someone gives an answer to this question with a better solution or explanation, I will award them the bounty. 但这仍然是一种解决方法,所以如果有人用更好的解决方案或解释给出这个问题的答案,我会奖励他们赏金。

The problem could come from getLoaderManager() which would not send the callback to the correct place. 问题可能来自getLoaderManager() ,它不会将回调发送到正确的位置。

If you are using ActionBarSherlock then it means you are on Android 2.x right? 如果您使用的是ActionBarSherlock,那么这意味着您使用的是Android 2.x吗? Have you considered using Android support package v4? 您是否考虑过使用Android支持包v4? http://developer.android.com/sdk/compatibility-library.html http://developer.android.com/sdk/compatibility-library.html

You would then use android.support.v4.app.ListFragment and get your LoaderManager by calling getActivity().getSupportLoaderManager() 然后,您将使用android.support.v4.app.ListFragment并通过调用getActivity().getSupportLoaderManager()获取您的LoaderManager getActivity().getSupportLoaderManager()

Since you are using a custom loader, do you know if you have the same issues when using the native CursorLoader? 由于您使用的是自定义加载器,因此在使用本机CursorLoader时是否存在相同的问题? If not, then perhaps, you can compare your Loader with the Android source and see if they are doing something differently? 如果没有,那么也许,您可以将您的Loader与Android源进行比较,看看他们是否采取了不同的做法?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 即使使用notifyDataSetChanged,我的ListFragment中的ArrayAdapter也不会刷新 - ArrayAdapter in my ListFragment won't refresh even when I use notifyDataSetChanged 如何使用另一个Activity中的内容提供者刷新ListFragment? - How to refresh a ListFragment using a content provider from another Activity? 如何在ListFragment上实现pull刷新 - How to implement pull to refresh on a ListFragment 如何添加ListFragment使用到布局 - How to add a ListFragment Using to layout 如何扩展Fragment并使子类与ListFragment一起扩展我的基类? - How can I extend Fragment and have a subclass extend my base class along with ListFragment? 如何从操作栏设置启动任务/布局 - How can I launch a task/layout from my actionbar settings 如何将 animation 添加到弹回堆栈? - How can I add animation to pop back stack? 将Android活动从堆栈中移到最前面时,如何刷新(重新初始化)它们? 所以要再次运行onCreate等 - When bringing Android activities from the stack to the front, how do I refresh (reinitialize) them? So to run onCreate again etc 当我单击工具栏后按时,我如何 go 从片段 B 回到 A? - How can i go back to A from fragment B when I click on the toolbar back press? 刷新页面时如何停止执行我的操作? - How to stop my action from executing when I refresh my page?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM