Android ListView not refreshing after notifyDataSetChanged

My ListFragment code

public class ItemFragment extends ListFragment {


private DatabaseHandler dbHelper;
private static final String TITLE = "Items";
private static final String LOG_TAG = "debugger";
private ItemAdapter adapter;
private List<Item> items;




@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.item_fragment_list, container, false);
return view;
}


@Override
public void onCreate(Bundle savedInstanceState) {
super.setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
items = dbHelper.getItems();
adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
this.setListAdapter(adapter);


}






@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter.notifyDataSetChanged();
}


@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if(dbHelper != null) { //item is edited
Item item = (Item) this.getListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), AddItemActivity.class);
intent.putExtra(IntentConstants.ITEM, item);
startActivity(intent);
}
}
}

My ListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >


<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />


</LinearLayout>

But this does not refresh the ListView. Even after restarting app the updated items are not shown. My ItemAdapter extends BaseAdapter

public class ItemAdapter extends BaseAdapter{


private LayoutInflater inflater;
private List<Item> items;
private Context context;


public ProjectListItemAdapter(Context context, List<Item> items) {
super();
inflater = LayoutInflater.from(context);
this.context = context;
this.items = items;


}


@Override
public int getCount() {
return items.size();
}


@Override
public Object getItem(int position) {
return items.get(position);
}


@Override
public long getItemId(int position) {
return position;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder = null;
if(convertView == null) {
holder = new ItemViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent,false);
holder.itemName = (TextView) convertView.findViewById(R.id.topText);
holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
convertView.setTag(holder);
} else {
holder = (ItemViewHolder) convertView.getTag();
}
holder.itemName.setText("Name: " + items.get(position).getName());
holder.itemLocation.setText("Location: " + items.get(position).getLocation());
if(position % 2 == 0) {
convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
} else {
convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
}
return convertView;
}


private static class ItemViewHolder {
TextView itemName;
TextView itemLocation;
}
}

Can someone help please?

199931 次浏览

如果已经设置了适配器,再次设置它将不会刷新列表视图。相反,首先检查 listview 是否有适配器,然后调用适当的方法。

我认为在设置列表视图时创建适配器的新实例并不是一个好主意。相反,创建一个对象。

BuildingAdapter adapter = new BuildingAdapter(context);


if(getListView().getAdapter() == null){ //Adapter not set yet.
setListAdapter(adapter);
}
else{ //Already has an adapter
adapter.notifyDataSetChanged();
}

你也可以尝试在 UI Thread 上运行刷新列表:

activity.runOnUiThread(new Runnable() {
public void run() {
//do your modifications here


// for example
adapter.add(new Object());
adapter.notifyDataSetChanged()
}
});

如果你想在 onResume()onCreate()或者其他函数中更新列表视图并不重要,首先你必须意识到你不需要创建一个新的适配器实例,只需要再次用你的数据填充数组。 这个想法与此类似:

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;


@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);


myListView = (ListView) findViewById(R.id.my_list);


titles = new ArrayList<String>()


for(int i =0; i<20;i++){
titles.add("Title "+i);
}


adapter = new MyListAdapter(this, titles);
myListView.setAdapter(adapter);
}




@Override
public void onResume(){
super.onResume();
// first clear the items and populate the new items
titles.clear();
for(int i =0; i<20;i++){
titles.add("New Title "+i);
}
adapter.notifySetDataChanged();
}

所以根据这个答案,你应该在你的 Fragment中使用相同的 List<Item>。在第一个适配器初始化中,用项目填充列表,并将适配器设置为列表视图。在此之后,在您的项目的每一个变化,您必须清除的价值,从主 List<Item> items,然后填充它与您的新项目,并调用 notifySetDataChanged();

这就是它的工作原理:)。

你在 onResume()中将重新加载的条目分配给全局变量条目,但是这在 ItemAdapter类中不会反映出来,因为它有自己的实例变量称为“条目”。

为了刷新 ListView,在接受列表数据即项目的 ItemAdapter类中添加一个 resh ()

class ItemAdapter
{
.....


public void refresh(List<Item> items)
{
this.items = items;
notifyDataSetChanged();
}
}

update onResume() with following code

@Override
public void onResume()
{
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
**adapter.refresh(items);**
}

试试这样:

this.notifyDataSetChanged();

而不是:

adapter.notifyDataSetChanged();

您必须将 notifyDataSetChanged()转换为 ListView,而不是转换为适配器类。

adpter.notifyDataSetInvalidated();

在 Activity 类的 onPause()方法中尝试此方法。

在 onResume ()中更改此行

items = dbHelper.getItems(); //reload the items from database

items.addAll(dbHelper.getItems()); //reload the items from database

问题是您从来没有告诉您的适配器关于新项目列表。如果您不想向适配器传递一个新列表(看起来似乎不想) ,那么只需在 clear()之后使用 items.addAll。这将确保您修改的列表与适配器引用的列表相同。

看看 ItemFragment中的 onResume方法:

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); // reload the items from database
adapter.notifyDataSetChanged();
}

在调用 notifyDataSetChanged()之前更新的不是适配器的字段 private List<Item> items;,而是片段的相同声明的字段。适配器仍然存储对在创建适配器时传递的项目列表的引用(例如,在片段的 onCreate 中)。 The shortest (in sense of number of changes) but not elegant way to make your code behave as you expect is simply to replace the line:

    items = dbHelper.getItems(); // reload the items from database

    items.addAll(dbHelper.getItems()); // reload the items from database

一个更优雅的解决方案:

1)从 ItemFragment中删除项目 private List<Item> items;-我们只需要在适配器中保持对它们的引用

2) change onCreate to:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setHasOptionsMenu(true);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
setListAdapter(adapter);
}

3)在 ItemAdapter 中添加方法:

public void swapItems(List<Item> items) {
this.items = items;
notifyDataSetChanged();
}

4)更改简历:

@Override
public void onResume() {
super.onResume();
adapter.swapItems(dbHelper.getItems());
}

AlexGo 的回答让我大开眼界:

getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
messages.add(m);
adapter.notifyDataSetChanged();
getListView().setSelection(messages.size()-1);
}
});

List Update 之前在从 GUI 事件触发更新时对我有用,因此在 UI 线程中。

但是,当我从另一个事件/线程更新列表时——即来自应用程序外部的调用,更新将不在 UI 线程中,并且忽略了对 getListView 的调用。如上所述使用 runOnUiThread 调用更新对我来说非常有效。谢谢!

adapter.setNotifyDataChanged()

应该能行。

Try this

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}

如果列表包含在适配器本身中,那么调用更新列表的函数也应该调用 notifyDataSetChanged()

从 UI Thread 运行这个函数对我来说很有用:

适配器内的 refresh()函数

public void refresh(){
//manipulate list
notifyDataSetChanged();
}

然后从 UI 线程运行这个函数

getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.refresh()
}
});