Android: обновить ListView в первом действии при возврате из второго действия

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

Я переопределил метод onResume:

@Override
public void onResume() {
    super.onResume();
    populateList();
}

populateList() — это метод, в котором я заполняю listView списком строк:

arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item,list);
listView.setAdapter(arrayAdapter);

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

Если я помещу notifyDataSetChanged() в onResume(), он выдаст мне исключение nullPointerException, потому что при первом запуске активности адаптер не инициализируется при первом запуске активности.

Я не знаю, как с этим справиться.

public class testActivity extends Activity {


    private int id=1;
    private ListView listView;
    private CustomArrayAdapter arrayAdapter;
    private ArrayList<String> list = new ArrayList<String>();
    ArrayList<Item> objectList = new ArrayList<Item>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

    }

    @Override
    public void onResume() {
        super.onResume();
        populateList();
    }

    private void populateList() {
        try {
            objectList = new GetAsyncTask(id).execute();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TimeoutException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        int size = objectList.size();
        String name;

        for (int i = 0; i < size; i++) {
            name = objectList.get(i).getName();
            list.add(name);
        }

        arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item,
                list);
        listView.setAdapter(arrayAdapter);
    }
}

person Cristiano    schedule 09.01.2013    source источник
comment
Сохраните переменную-член, которая контролирует, содержит ли уже ListView строки. Если да, не звоните populateList().   -  person Wroclai    schedule 10.01.2013
comment
Проблема с list, как вы это заполняете?   -  person Sam    schedule 10.01.2013
comment
@Shelly - Но мне нужно вызвать populateList(), даже если ListView содержит строки. Я пытаюсь освежить его. Я не уверен, что хорошо вас понял.   -  person Cristiano    schedule 10.01.2013
comment
@ZdravkoVajudin: А, извините, я неправильно понял. Я бы очистил адаптер, а затем снова добавил строки.   -  person Wroclai    schedule 10.01.2013
comment
@Sam - я получаю list_of_files с устройства и помещаю его в список в populateList(). Затем я применяю адаптер. По возвращении этот список на устройстве изменяется, поэтому он снова делает то же самое и помещает его в список. Проблема в том, что в ListView все еще есть старые элементы, а новые элементы просто добавляются в конце. Значит дублируется.   -  person Cristiano    schedule 10.01.2013


Ответы (4)


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

    if (adapter != null) {
        adapter.notifyDataSetChanged();
    }

Но мне кажется, что на более глубоком уровне ваш код можно несколько реорганизовать, чтобы сделать его более эффективным, хотя и не обязательно более функциональным.


сделай это так:

private int id = 1;
private ListView listView;
private CustomArrayAdapter arrayAdapter;
private ArrayList<String> list = new ArrayList<String>();

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
}

@Override
public void onResume() {
    super.onResume();
    populateList();
}

private void populateList() {

    ArrayList<Item> objectList;
    try {
        objectList = new GetAsyncTask(id).execute();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (TimeoutException e) {
        e.printStackTrace();
    }

    list.clear();
    for (int i = 0; i <objectList.size(); i++) {
        String name = objectList.get(i).getName();
        list.add(name);
    }
    if (arrayAdapter == null) {
        arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item, list);
        listView.setAdapter(arrayAdapter);
    } else {
        arrayAdapter.notifyDataSetChanged();            
    }
}
person mango    schedule 09.01.2013
comment
Поставил так и теперь опять работает но проблема с дублированием элементов осталась. У меня, например, 5 элементов, измените один во втором действии, а по возвращении в списке снова будут те старые пять, 1 новый (один изменен во втором действии) и снова 4 старых, которые дублируются. Вот так, если редактируется последний: a, b, c, d, e, a, b, c, d, z - person Cristiano; 10.01.2013
comment
вы написали недостаточно кода, чтобы мы могли вам в достаточной мере помочь. но вам нужно проверить, что любой блок кода, инициализирующий коллекцию list, не вызывается снова, предположительно onResume Тот факт, что вам даже нужно то, что я предложил, заставляет меня беспокоиться о том, что все не так, как могло бы быть. - person mango; 10.01.2013
comment
Оно работает! Всего одна поправка. В этом последнем else ниже arrayAdapter.notifyDataSetChanged(); также должно быть: arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item, list); listView.setAdapter(arrayAdapter); Если это не так, ListView содержит только старые данные. - person Cristiano; 10.01.2013

Я получаю list_of_files с устройства и помещаю его в список в populateList(). Затем я применяю адаптер. По возвращении этот список на устройстве изменяется, поэтому он снова делает то же самое и помещает его в список. Проблема в том, что в ListView все еще есть старые элементы, а новые элементы просто добавляются в конце. Значит дублируется.

Один из основных подходов — вызвать list.clear() перед добавлением новых данных. Это должно стереть старые данные и предотвратить дублирование. (Но трудно дать точный ответ, не видя рассматриваемого кода...)


Дополнение
Вы должны добавить этот код в свой метод onPostExecute() внутри GetAsyncTask:

int size = objectList.size();
String name;

list.clear(); // or list = new ArrayList<String>();
for (int i = 0; i < size; i++) {
    name = objectList.get(i).getName();
    list.add(name);
}

arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item, list);
listView.setAdapter(arrayAdapter);

Если GetAsyncTask не вложено в вашу активность, вам нужно будет переместить несколько переменных, чтобы GetAsyncTask использовать это. Но этот подход лучше, потому что он не заставляет Activity ждать результатов (что может вызвать ошибку «Приложение не отвечает».)

person Sam    schedule 09.01.2013
comment
Спасибо за совет для AsyncTask! Я дал тебе один :) - person Cristiano; 10.01.2013

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

Если вы хотите, чтобы данные были «свежими», почему бы не использовать CursorLoader с LoaderManager? У LoaderManager есть несколько преимуществ (например, удаление запроса из потока пользовательского интерфейса), и LoaderManager делает за вас немного тяжелой работы, когда дело доходит до мониторинга БД на предмет изменений.

В блоге есть запись, которую я нашел весьма полезной: http://www.androiddesignpatterns.com/2012/07/understanding-loadermanager.html

Это все предполагает, что вы извлекаете данные из базы данных...

person user1757442    schedule 09.01.2013

Проблема в том, что вы вызываете функцию populateList() без предварительной очистки списка. Таким образом, список заполнения просто добавляется к предыдущему содержимому.

Это будет правильный путь:

public void onResume() {
super.onResume();
list = new ArrayList<String>();
populateList();  }

Я также не уверен, почему вы установили адаптер внутри функции populateList()

Вы должны попробовать это.

    private ArrayList<String> list = new ArrayList<String>();

    private ArrayList<String> populateList() {
            try {
                objectList = new GetAsyncTask(id).execute();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TimeoutException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            int size = objectList.size();
            String name;

            for (int i = 0; i < size; i++) {
                name = objectList.get(i).getName();
                list.add(name);
            }
 return list;
        }
     arrayAdapter = new CustomArrayAdapter(this, R.layout.symbol_item,
                    populateList());
            listView.setAdapter(arrayAdapter);

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

public void onResume() {
    super.onResume();
    list = new ArrayList<String>();
    }
person Kennedy Nyaga    schedule 10.04.2014