Android ListView не обновляется после notifyDataSetChanged
мой ListFragment код
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);
}
}
}
Мой 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>
но это не обновить ListView. Даже после перезапуска приложения обновленные элементы не отображаются. Мой ItemAdapter выходит 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;
}
}
может кто-нибудь помочь пожалуйста?
11 ответов:
посмотри на свою
onResumeметодItemFragment:@Override public void onResume() { super.onResume(); items.clear(); items = dbHelper.getItems(); // reload the items from database adapter.notifyDataSetChanged(); }то, что вы только что обновили перед вызовом
notifyDataSetChanged()это не поле адаптераprivate List<Item> items;но тождественно объявленное поле фрагмента. Адаптер по-прежнему хранит ссылку на список элементов, которые вы передали при создании адаптера (например, в onCreate фрагмента). Самый короткий (в смысле количества изменений), но не элегантный способ заставить ваш код вести себя так, как вы ожидаете, - это просто заменить линия:items = dbHelper.getItems(); // reload the items from databaseС
items.addAll(dbHelper.getItems()); // reload the items from databaseболее элегантное решение:
1) удалить элементы
private List<Item> items;СItemFragment- нам нужно сохранить ссылки на них только в адаптер2) Измените onCreate на:
@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) измените свой onResume на:
@Override public void onResume() { super.onResume(); adapter.swapItems(dbHelper.getItems()); }
вы назначаете перезагруженные элементы глобальным переменным элементам в
onResume(), но это не отразится вItemAdapterкласс, потому что он имеет свою собственную переменную под названием 'элементы'.для обновления
ListViewдобавить обновления() вItemAdapterкласс, который принимает список данных, т. е. элементыclass ItemAdapter { ..... public void refresh(List<Item> items) { this.items = items; notifyDataSetChanged(); } }обновление
onResume()со следующим кодом@Override public void onResume() { super.onResume(); items.clear(); items = dbHelper.getItems(); //reload the items from database **adapter.refresh(items);** }
в onResume () изменить эту строку
items = dbHelper.getItems(); //reload the items from databaseдо
items.addAll(dbHelper.getItems()); //reload the items from databaseпроблема в том, что вы никогда не говорите своему адаптеру о списке новых элементов. Если вы не хотите передавать новый список своему адаптеру (как кажется, вы этого не делаете), то просто используйте
items.addAllпослеclear(). Это гарантирует, что вы изменяете тот же список, на который ссылается адаптер.
если адаптер уже установлен, его повторная установка не обновит listview. Вместо этого сначала проверьте, имеет ли listview адаптер, а затем вызовите соответствующий метод.
Я думаю, что это не очень хорошая идея, чтобы создать новый экземпляр адаптера при настройке представления списка. Вместо этого создайте объект.
BuildingAdapter adapter = new BuildingAdapter(context); if(getListView().getAdapter() == null){ //Adapter not set yet. setListAdapter(adapter); } else{ //Already has an adapter adapter.notifyDataSetChanged(); }Также вы можете попробовать запустить список обновления в потоке пользовательского интерфейса:
activity.runOnUiThread(new Runnable() { public void run() { //do your modifications here // for example adapter.add(new Object()); adapter.notifyDataSetChanged() } });
если вы хотите обновить свой listview не имеет значения, если вы хотите сделать это на
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(); }так что в зависимости от этого ответа вы должны использовать то же самое
List<Item>в своемFragment. При первой инициализации адаптера вы заполняете свой список элементами и установите адаптер для вашего listview. После этого в каждом изменении ваших элементов вы должны очистить значения от основногоList<Item> itemsи чем заполнить его снова с новыми элементами и позвонитьnotifySetDataChanged();.вот как это работает : ).
ответ от AlexGo сделал трюк для меня:
getActivity().runOnUiThread(new Runnable() { @Override public void run() { messages.add(m); adapter.notifyDataSetChanged(); getListView().setSelection(messages.size()-1); } });обновление списка работало для меня раньше, когда обновление было вызвано из события GUI, таким образом, находясь в потоке пользовательского интерфейса.
однако, когда я обновляю список из другого события / потока-т. е. вызова из-за пределов приложения, обновление не будет в потоке пользовательского интерфейса, и он проигнорировал вызов getListView. Вызов обновления с помощью runOnUiThread, как указано выше, сделал трюк для меня. Спасибо!!
попробуй такое
@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(); }
попробуйте так:
this.notifyDataSetChanged();вместо:
adapter.notifyDataSetChanged();вы должны
notifyDataSetChanged()доListViewне к классу адаптера.
если ваш список содержится в самом адаптере, вызов функции, которая обновляет список, также должен вызвать
notifyDataSetChanged().запуск этой функции из потока пользовательского интерфейса, сделал трюк для меня:
The
refresh()функция внутри адаптераpublic void refresh(){ //manipulate list notifyDataSetChanged(); }затем в свою очередь запустите эту функцию из потока пользовательского интерфейса
getActivity().runOnUiThread(new Runnable() { @Override public void run() { adapter.refresh() } });