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 106

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

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

adapter.setNotifyDataChanged()

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

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

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