Android-ArrayAdapter дублирует элементы ListView


У меня есть виджет ListView (расположенный ниже других элементов пользовательского интерфейса), который является частью Activity, который не расширяет ListActivity. Я пытаюсь написать базовую функцию, которая добавляет дополнительные элементы в ListView при нажатии кнопки Button. Эти данные получены из EditText. Я определяю Adapter, ArrayAdapter в моем случае для ListView в onCreate() и устанавливаю адаптер для ListView с помощью setListAdapter(). Однако, когда я пытаюсь добавить дополнительный элемент к ListView, два элемента добавляются вместо одного (элемент и его дубликат). Больше подробно об этом ниже.

Функция, которая отвечает на щелчок Button и пытается добавить дополнительный элемент перед вызовом notifyDataSetChanged(), выглядит следующим образом:

public void saveButton(View view){

     EditText editText = (EditText) findViewById(R.id.edit_text);
     String data = editText.getText().toString();
     // duplication caused by this line
        b.add(data);
     ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
     adapter.add((String)data);
     adapter.notifyDataSetChanged();
}

Когда я не включаю эту строку:

B. добавить (данные);

В список добавляется один элемент, который является ожидаемым поведением, поскольку adapter.add(Object) добавляет дополнительный элемент в список. Однако еще более странно, что когда я не включаю adapter.add(Object) элемент списка все еще отображается в списке.

 public void saveButton(View view){

     EditText editText = (EditText) findViewById(R.id.edit_text);
     String data = editText.getText().toString();
     b.add(data);
     ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
     // not calling adapter.add() still includes an item in the list.
     // adapter.add((String)data);
     adapter.notifyDataSetChanged();
}

После небольшой отладки я обнаружил, что это происходит из-за добавления элемента данных в ArrayList, т. е. b.add(data), где b-ArrayList строк, что приводит к включению элемента в ListView, несмотря на то, что он не вызывает адаптер.добавить (объект)`.

Q1. Почему это происходит ?

Q2. Как расширение, означает ли это, что ArrayList "связан" с моим ArrayAdapter (поскольку он был построен с использованием ArrayList в качестве аргумента), подразумевая, что мне не нужно вызывать адаптер методы, такие как add () или clear (), чтобы манипулировать моим ListView ?

Вот полный код моей деятельности:

public class MainActivity extends Activity {

ListView listView;
ArrayList<String> b = new ArrayList<String>();

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

    b.add("Liquorice");
    b.add("Dairy Milk");
    b.add("Rice Crispies");
    b.add("Orange-Mint");
    b.add("After-Tens");


    listView = (ListView) findViewById(R.id.list_view);
    ArrayAdapter adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.layout_file,b);
    listView.setAdapter(adapter);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

public void saveButton(View view){

     EditText editText = (EditText) findViewById(R.id.edit_text);
     String data = editText.getText().toString();
     b.add(data);
     ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
     adapter.add((String)data);
     adapter.notifyDataSetChanged();
}

}
Я также осмотрел другие посты SO, но они не дали мне ответов, которые я искал.

ArrayAdapter в android для создания простого listview

Почему нельзя добавлять/удалять элементы из ArrayAdapter?

Дублированные записи в ListView

Любое направление или объяснение по этому поводу буду очень признателен. Также большое спасибо за ваше время.

2 3

2 ответа:

Похоже, вы уже отыскали ответ. Предоставляя ArrayList ArrayAdapter, вы, по сути, связали их. Чтобы обновить данные, все, что вам нужно сделать, это добавить новый элемент в ArrayList.

Метод .add() - это просто короткий путь к этому. Все, что он делает, это добавляет предоставленный элемент в ArrayList. Вы можете добавить элемент в любом случае, просто не делайте этого в обоих направлениях.

В чем же тогда смысл notifyDataSetChanged()? Просто потому, что вы обновили ArrayList ,это не значит, что ListView знает, что есть новые данные. Таким образом, новые данные могут существовать, но представление может не обновляться. Вызов notifyDataSetChanged() говорит ListView искать новые данные и перерисовывать себя так, чтобы пользователь видел изменение.


Из документации Android для ArrayAdapter.notifyDataSetChanged():

Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.

Http://developer.android.com/reference/android/widget/ArrayAdapter.html#notifyDataSetChanged()

На адаптере проверьте, является ли представление нулевым, если это так, назначьте правильные представления из вашего макета, после if, установите значения.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
     ViewHolder viewHolder;
     if (convertView == null) {
         viewHolder = new ViewHolder();
         LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         convertView = vi.inflate(R.layout.customswipelayout, parent, false);
         viewHolder.name = (TextView) convertView.findViewById(R.id.rowTextView1);
         viewHolder.price = (TextView) convertView.findViewById(R.id.rowTextView2);
         viewHolder.stockCount = (TextView) convertView.findViewById(R.id.rowTextView3);
         convertView.setTag(viewHolder);
         mItemManger.bindView(view, position);
     }

     /* rest of your adapter code */
}

Это пример из одного моего, после того, как я закрываю оператор if, я присваиваю свои значения и возвращаю представление:

viewHolder = (ViewHolder) view.getTag();
final Object object = this.list.get(position);
ImageView cmdDelete = (ImageView) view.findViewById(R.id.cmdDelete);
cmdDelete.setOnClickListener
//more personal code follows

return view;

Теперь я прокручиваю свой список, и он отлично работает!