Android ListView не обновляется после 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>

Но это не обновляет 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;
    }
}

Может кто-нибудь помочь?

Ответ 1

Посмотрите на свой метод 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 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) измените свой onResume на:

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

Ответ 2

Вы назначаете перезагруженные элементы глобальным переменным в onResume(), но это не будет отображаться в классе ItemAdapter, потому что у него есть своя переменная экземпляра, называемая "элементы".

Для обновления ListView добавьте класс refresh() в ItemAdapter, который принимает данные списка i.e items

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

Ответ 3

В onResume() измените эту строку

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

к

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

Проблема в том, что вы никогда не рассказываете вашему адаптеру о списке новых элементов. Если вы не хотите передавать новый список вашему адаптеру (как вам кажется, нет), просто используйте items.addAll после вашего clear(). Это гарантирует, что вы измените тот же список, на который ссылается адаптер.

Ответ 4

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

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

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

Ответ 5

Если вы хотите обновить свой список, не имеет значения, хотите ли вы сделать это на 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. В вашей первой инициализации адаптера вы заполняете свой список элементами и устанавливаете адаптер в свой список. После этого при каждом изменении ваших элементов вам нужно очистить значения от основного List<Item> items и снова заполнить его новыми элементами и вызвать notifySetDataChanged();.

Как это работает:).

Ответ 6

Ответ от AlexGo сделал для меня трюк:

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

Обновление списка работало для меня раньше, когда обновление было вызвано из события GUI, таким образом, в потоке пользовательского интерфейса.

Однако, когда я обновляю список из другого события/потока - то есть вызова извне приложения, обновление не будет в потоке пользовательского интерфейса, и оно игнорирует вызов getListView. Вызов обновления с помощью runOnUiThread, как и выше, помогло. Спасибо!!

Ответ 7

Попробуйте это

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

Ответ 8

adpter.notifyDataSetInvalidated();

Попробуйте это в методе onPause() класса Activity.

Ответ 9

adapter.setNotifyDataChanged()

должен сделать трюк.

Ответ 10

Попробуйте вот так:

this.notifyDataSetChanged();

вместо:

adapter.notifyDataSetChanged();

Вы должны notifyDataSetChanged() к ListView не к классу адаптера.

Ответ 11

Если ваш список содержится в самом адаптере, вызов функции, которая обновляет список, также вызывает notifyDataSetChanged().

Запуск этой функции из потока пользовательского интерфейса сделал для меня трюк:

Функция refresh() внутри адаптера

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

Затем, в свою очередь, запустите эту функцию из потока пользовательского интерфейса

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