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 class, потому что он имеет свою собственную переменную экземпляра, называемую 'items'.

для обновления 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);**
}

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

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


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

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

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

однако, когда я обновляю список из другого события / потока-т. е. вызова извне приложения, обновление не будет в потоке пользовательского интерфейса, и он проигнорировал вызов 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();
}

adpter.notifyDataSetInvalidated();

попробуем onPause() метод класса действия.


adapter.setNotifyDataChanged()

следует сделать трюк.


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

this.notifyDataSetChanged();

вместо:

adapter.notifyDataSetChanged();

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


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

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

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

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

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

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