Как обновить список рассылки Android? - PullRequest
415 голосов
/ 12 февраля 2010

Как обновить Android ListView после добавления / удаления динамических данных?

Ответы [ 25 ]

513 голосов
/ 12 февраля 2010

Вызовите notifyDataSetChanged() на вашем Adapter объекте, как только вы изменили данные в этом адаптере.

Некоторые дополнительные сведения о том, как / когда звонить notifyDataSetChanged(), можно посмотреть в этом видео ввода-вывода Google .

198 голосов
/ 29 ноября 2011

Также вы можете использовать это:

myListView.invalidateViews();
148 голосов
/ 23 мая 2013

Пожалуйста, игнорируйте все ответы invalidate(), invalidateViews(), requestLayout(), ... на этот вопрос.

Что нужно сделать (и, к счастью, также отмечено как правильный ответ), это для вызова notifyDataSetChanged() на вашем адаптере .

Устранение неполадок

Если вызов notifyDataSetChanged() не работает, все методы макета тоже не помогут. Поверьте мне, ListView был должным образом обновлен. Если вы не можете найти разницу, вам нужно проверить, откуда поступают данные в вашем адаптере.

Если это просто коллекция, которую вы храните в памяти, проверьте, действительно ли вы удалили или добавили элемент (ы) в коллекцию перед вызовом notifyDataSetChanged().

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

Дело в том, что notifyDataSetChanged работает, только если набор данных изменился. Так что это место, чтобы посмотреть, если вы не обнаружите, что изменения происходят через. Отладка при необходимости.

ArrayAdapter против BaseAdapter

Я обнаружил, что работа с адаптером, который позволяет вам управлять коллекцией, например, BaseAdapter, работает лучше. Некоторые адаптеры, такие как ArrayAdapter, уже управляют собственной коллекцией, что затрудняет доступ к соответствующей коллекции для обновлений. Это действительно просто ненужный дополнительный уровень сложности в большинстве случаев.

Пользовательский интерфейс

Это правда, что это должно вызываться из потока пользовательского интерфейса. В других ответах есть примеры того, как этого добиться. Однако это требуется, только если вы работаете с этой информацией вне потока пользовательского интерфейса. Это из службы или потока не пользовательского интерфейса. В простых случаях вы будете обновлять свои данные с помощью нажатия кнопки или другого действия / фрагмента. Так что все еще в потоке пользовательского интерфейса. Не нужно всегда вставлять этот runOnUiTrhead.

Быстрый пример проекта

Можно найти на https://github.com/hanscappelle/so-2250770.git. Просто клонируйте и откройте проект в Android Studio (gradle). Этот проект имеет здание MainAcitivity ListView со всеми случайными данными. Этот список можно обновить с помощью меню действий.

Реализация адаптера, которую я создал для этого примера ModelObject, предоставляет сбор данных

public class MyListAdapter extends BaseAdapter {

    /**
     * this is our own collection of data, can be anything we 
     * want it to be as long as we get the abstract methods 
     * implemented using this data and work on this data 
     * (see getter) you should be fine
     */
    private List<ModelObject> mData;

    /**
     * our ctor for this adapter, we'll accept all the things 
     * we need here
     *
     * @param mData
     */
    public MyListAdapter(final Context context, final List<ModelObject> mData) {
        this.mData = mData;
        this.mContext = context;
    }

    public List<ModelObject> getData() {
        return mData;
    }

    // implement all abstract methods here
}

Код от MainActivity

public class MainActivity extends Activity {

    private MyListAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView list = (ListView) findViewById(R.id.list);

        // create some dummy data here
        List<ModelObject> objects = getRandomData();
        // and put it into an adapter for the list
        mAdapter = new MyListAdapter(this, objects);
        list.setAdapter(mAdapter);

        // mAdapter is available in the helper methods below and the 
        // data will be updated based on action menu interactions

        // you could also keep the reference to the android ListView 
        // object instead and use the {@link ListView#getAdapter()} 
        // method instead. However you would have to cast that adapter 
        // to your own instance every time
    }

    /**
     * helper to show what happens when all data is new
     */
    private void reloadAllData(){
        // get new modified random data
        List<ModelObject> objects = getRandomData();
        // update data in our adapter
        mAdapter.getData().clear();
        mAdapter.getData().addAll(objects);
        // fire the event
        mAdapter.notifyDataSetChanged();
    }

    /**
     * helper to show how only changing properties of data 
     * elements also works
     */
    private void scrambleChecked(){
        Random random = new Random();
        // update data in our adapter, iterate all objects and 
        // resetting the checked option
        for( ModelObject mo : mAdapter.getData()) {
            mo.setChecked(random.nextBoolean());
        }
        // fire the event
        mAdapter.notifyDataSetChanged();
    }
}

Дополнительная информация

Еще один приятный пост о силе listViews находится здесь: http://www.vogella.com/articles/AndroidListView/article.html

42 голосов
/ 18 февраля 2014

Вызов может выполняться, когда вы хотите:

runOnUiThread(run);

OnCreate(), вы устанавливаете свою работоспособную нить:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};
11 голосов
/ 13 сентября 2011

У меня возникли проблемы с динамическим обновлением списка.

Вызовите notifyDataSetChanged () на вашем адаптере.

Некоторые дополнительные сведения о том, как и когда вызывать notifyDataSetChanged (), можно посмотреть в этом видео Google I / O.

notifyDataSetChanged () не работал должным образом в моем случае [я вызвал notifyDataSetChanged из другого класса]. На всякий случай я отредактировал ListView в запущенном Activity (Thread). Это видео благодаря Кристоферу дало последний намек.

В моем втором классе я использовал

Runnable run = new Runnable(){
     public void run(){
         contactsActivity.update();
     }
};
contactsActivity.runOnUiThread(run);

для доступа к обновлению () из моей Активности. Это обновление включает

myAdapter.notifyDataSetChanged();

, чтобы сообщить адаптеру обновить представление. Работал нормально, насколько я могу сказать.

9 голосов
/ 25 июня 2011

Если вы используете SimpleCursorAdapter , попробуйте вызвать Requery () для объекта Cursor.

5 голосов
/ 25 августа 2010

Если вы все еще не удовлетворены обновлением ListView, вы можете посмотреть на этот фрагмент, он предназначен для загрузки listView из БД. Фактически, вам нужно просто перезагрузить ListView после выполнения любой операции CRUD. Это не лучший способ написания кода, но он обновит ListView, как вы пожелаете.

Это работает для меня .... если вы найдете лучшее решение, пожалуйста, поделитесь ...

.......
......
do your CRUD Operations..
......
.....
DBAdapter.open();
DBAdapter.insert_into_SingleList();
// Bring that DB_results and add it to list as its contents....
                                            ls2.setAdapter(new ArrayAdapter(DynTABSample.this,
    android.R.layout.simple_list_item_1,                        DBAdapter.DB_ListView));
                                            DBAdapter.close();
2 голосов
/ 19 июня 2014

Решения, предложенные людьми в этом посте, работают или не работают в основном в зависимости от версии Android вашего устройства. Например, чтобы использовать метод AddAll, вы должны поместить android: minSdkVersion = "10" в ваше устройство Android.

Чтобы решить эти вопросы для всех устройств, я создал свой собственный метод в своем адаптере и использовал внутри метода add и remove наследование от ArrayAdapter, который без проблем обновляет ваши данные.

Мой код: используя мой собственный класс данных RaceResult, вы используете свою собственную модель данных.

ResultGpRowAdapter.java

public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {

    Context context;
    int resource;
    List<RaceResult> data=null;

        public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
        super(context, resource, objects);

        this.context = context;
        this.resource = resource;
        this.data = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ........
        }

        //my own method to populate data           
        public void myAddAll(List<RaceResult> items) {

        for (RaceResult item:items){
            super.add(item);
        }
    }

ResultsGp.java

public class ResultsGp extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...........
    ...........
    ListView list = (ListView)findViewById(R.id.resultsGpList); 

    ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data

   list.setAdapter(adapter);

   .... 
   ....
   ....
   //LOAD a ArrayList<RaceResult> with data

   ArrayList<RaceResult> data = new ArrayList<RaceResult>();
   data.add(new RaceResult(....));
   data.add(new RaceResult(....));
   .......

   adapter.myAddAll(data); //Your list will be udpdated!!!
1 голос
/ 14 февраля 2018

Просто используйте myArrayList.remove(position); внутри слушателя:

  myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
           myArrayList.remove(position);
           myArrayAdapter.notifyDataSetChanged();
        }
    });
1 голос
/ 25 августа 2015

Вам нужно использовать один объект из этого списка, данные которого вы надуваете ListView. Если ссылка изменилась, то notifyDataSetChanged() не работает. Когда вы удаляете элементы из списка, также удаляйте их из списка, который вы используете, будь то ArrayList <> или что-то еще, затем Call notifyDataSetChanged() на объекте Вашего класса адаптера.

Итак, посмотрите, как мне это удалось в моем адаптере, см. Ниже

public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{

private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;

public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
    this.context = context;
    this.dObj=dObj;
    completeList=new  ArrayList<CountryDataObject>();
    completeList.addAll(dObj);
    itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}

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

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

@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
    if(view==null){
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.states_inflator_layout, null);
        holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
        holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
        view.setTag(holder);
    }else{
        holder = (ViewHolder) view.getTag();
    }
    holder.textView.setText(dObj.get(position).getCountryName());
    holder.textView.setTypeface(itemFont);

    if(position==selectedPosition)
     {
         holder.checkImg.setImageResource(R.drawable.check);
     }
     else
     {
         holder.checkImg.setImageResource(R.drawable.uncheck);
     }
    return view;
}
private class ViewHolder{
    private TextView textView;
    private ImageView checkImg;
}

public void getFilter(String name) {
    dObj.clear();
    if(!name.equals("")){
    for (CountryDataObject item : completeList) {
        if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
            dObj.add(item);
        }
    }
    }
    else {
        dObj.addAll(completeList);
    }
    selectedPosition=-1;
    notifyDataSetChanged();
    notifyDataSetInvalidated(); 
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Registration reg=(Registration)context;
    selectedPosition=position;
    reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
    notifyDataSetChanged();
}
}
...