刷新ArrayAdapter onResume [notifyDataSetChanged()无效]

10

我正在使用Fragment创建一个联系人列表应用程序,其中一个碎片显示联系人列表中的名称,另一个碎片显示其余详细信息。

这是显示名称列表的类:

public class MyListFragment extends ListFragment {

private ContactStorage contactStorage = new ContactStorage();

public final static String TAG = "FRAGMENTS";
private MainActivity parent;
ArrayAdapter<String> adapter;

ArrayList<String> entries = new ArrayList<String>();

String array[];

public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    View v = inflater.inflate(R.layout.list_layout, null);
    parent = (MainActivity) getActivity();
    entries = contactStorage.getContactListNames();

    adapter = new ArrayAdapter<String>(getActivity().getApplicationContext(), 
            android.R.layout.simple_list_item_1, entries);

    setListAdapter(adapter);
    Log.d(TAG, "Adapter created");
    array = contactStorage.getContactDetails();
    return v;

}

@Override
public void onResume() {

    super.onResume();
    entries = contactStorage.getContactListNames();
    adapter.notifyDataSetChanged();
    Log.d(TAG, "List Frag Resumed");

}
}

我遇到的问题是ArrayAdapter在恢复时不会刷新。

当屏幕旋转时,由于onCreateView()被再次调用,所以没问题,但我需要在onResume()中刷新。我查看了很多这个网站上的内容,却只找到了"使用notifyDataSetChanged()",但它并不起作用。


你期望什么呢?你正在检索“entries”,但没有重新构建你的“adapter”。将两行代码“entries =”和“adapter =”从“onCreate()”移动到“onResume()”(你只需要这样做一次),在“onResume()”中忘记“entries =”和“notify”,然后你就没问题了。 - class stacker
@ClassStacker - 仍然存在同样的问题。直到屏幕旋转才会刷新列表。 - JakeCowton
抱歉,你还需要在那里执行setListAdapter()。 - class stacker
@ClassStacker - 我能理解那样做的方式,但那是否更像是一种“hacky”的绕过方法呢? - JakeCowton
1
我的建议与您接受的建议等效。它并不是什么“hacky”的东西。只是clear()/addAll()方法比整体丢弃慢。 - class stacker
@ClassStacker - 那么你的版本是下面解决方案的优化版本吗? - JakeCowton
2个回答

30

你遇到的问题是覆盖了entries的引用,但在适配器上并没有改变。以下是你可以解决这个问题的方法:

@Override
public void onResume() {

    super.onResume();
    entries.clear();
    entries.addAll(contactStorage.getContactListNames());
    adapter.notifyDataSetChanged();
    Log.d(TAG, "List Frag Resumed");

}

这是一个常见的错误,它是由于当您首次创建列表(在内存中)并且您的entries字段指向该列表时,然后告诉adapter在创建它时查看该内存位置,但是onResume时,您会再次获取联系人列表名称并在内存中创建一个新列表,并告诉条目指向该新列表的内存,您需要做的是用新列表中的条目替换原始列表中的条目,这样adapter仍将引用相同的列表。


太好了!我知道与'.add()'有关的事情,但是我无法在这个主题上得到清晰的答案。非常感谢你! - JakeCowton
1
我还添加了一个更好的关于这个问题的解释。另一种方法是在恢复时使用你的ArrayAdapter adapter.clear(); 然后 adapter.addAll(contactStorage.getContactListNames()); 并完全忘记entries字段。 - Eluvatar
清空列表并再次添加更新的元素在最新版本的Android中会抛出ConcurrentModificationException,因为ArrayAdapter也试图读取列表。清除适配器并使用adapter.addAll()可以正常工作。向@Eluvatar致敬。 - Rengasami Ramanujam

5

notifyDataSetChanged() 对您无效。原因如下:

您的适配器失去了对列表的引用。当您首次初始化适配器时,它会获取您的arrayList的引用并传递给其超类。但是,如果您重新初始化现有的arrayList,则会丢失引用,因此与适配器之间的通信通道也会丢失 :(。

始终为适配器创建并添加新列表。像这样做:

  1. 在全局声明时初始化arrayList。
  2. 直接将列表添加到适配器中,而不检查null和empty条件。直接将适配器设置为列表(不要检查任何条件)。适配器保证无论何时更改arrayList的数据,它都会处理,但从不失去引用。
  3. 每次添加数据到arrayList(如果您的数据完全是新的,则可以在实际添加数据到列表之前调用adapter.clear()和arrayList.clear())但不要设置适配器,即如果新数据填充在arrayList中,则只需adapter.notifyDataSetChanged()

相信文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接