Android, функция add() от ArrayAdapter не работает
У меня есть ArrayAdapter (myAdapter), прикрепленный к компоненту AutoCompleteTextView (textView).
Когда пользователь нажимает символ, я хотел бы заполнить выпадающий список AutoCompleteTextView с элементами, содержащими этот символ.
Я извлекаю элементы, используя AsyncTask (который использует веб-службу).
Я вызываю myAdapter.add(item), но выпадающий список пуст.
Я добавил вызов myAdapter.getCount() после каждого добавления и каждый раз показывает нуль.
Вызов notifyDataSetChanged() не помогло.
Я даже пытался добавить простые объекты String вместо моих пользовательских объектов, но безрезультатно.
Что я делаю не так?
Изменить: Я изменил код, как предложено ниже, но все еще безрезультатно.
Как правило, то, что я делаю, это после того, как текст изменен в моем автоматическом полном текстовом представлении, я вызываю новый AsyncTask и передаю ему введенный текст и обработчик (см. AfterTextChanged()). Задача извлекает объекты, относящиеся к тексту, и после того, как вызывается обработчик handleMessage(). В handleMessage() я пытаюсь заполнить объекты адаптера. Но все же выпадающий список адаптера заканчивается пустым.
Вот мой код:
public class AddStockView extends Activity
implements OnClickListener, OnItemClickListener, TextWatcher {
ArrayAdapter<Stock> adapter;
AutoCompleteTextView textView;
Vector<Stock> stocks;
public AddStockView() {
// TODO Auto-generated constructor stub
stocks = new Vector<Stock>();
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.add_stock_view);
findViewById(R.id.abort_button).setOnClickListener(this);
adapter = new ArrayAdapter<Stock>(this,
android.R.layout.simple_dropdown_item_1line, stocks);
//adapter.setNotifyOnChange(true);
textView = (AutoCompleteTextView)
findViewById(R.id.search_edit_text);
textView.setAdapter(adapter);
textView.setOnItemClickListener(this);
textView.addTextChangedListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId())
{
case R.id.abort_button:
finish();
break;
case R.id.search_edit_text:
break;
}
}
@Override
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// TODO Auto-generated method stub
Stock stockToAdd = (Stock)parent.getAdapter().getItem(position);
//TODO: Add the above stock to user stocks and close this screen
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.layout.menu, menu);
CategoryMenu.getInstance().populateMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
CategoryMenu.getInstance().menuItemSelected(item, this);
return false;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
return true;
}
@Override
public void afterTextChanged(Editable text) {
// TODO Auto-generated method stub
if (text.toString().equals(""))
return;
new AppTask().execute(new AppTask.Payload(Consts.taskType.SEARCH_STOCK,
new Object[] {text, handler}, this));
}
@Override
public void beforeTextChanged(CharSequence a0, int a1, int a2, int a3) {
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence a0, int a1, int a2, int a3) {
// TODO Auto-generated method stub
}
private void addStockItemsToAdapter(Vector<Object> dataItems)
{
for (int i = 0; i <dataItems.size(); i++)
{
Stock stk = (Stock)dataItems.elementAt(i);
stocks.add(stk);
}
}
public void populateAdapter()
{
addStockItemsToAdapter(ContentReader.getInstance.getDataItems());
adapter.notifyDataSetChanged();
int size = adapter.getCount(); // size == 0 STILL!!!!
textView.showDropDown();
}
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
populateAdapter();
}
};
}
Большое спасибо, Rob
Ответы
Ответ 1
Где вы называете addItemsToAdapter()?
Можете ли вы показать нам, как вы пытались добавить простые строки в свой адаптер?
Изменить: из комментариев полезный пример кода:
adapter = new ArrayAdapter<Stock>(this, android.R.layout.simple_dropdown_item_1line, stocks);
adapter.notifyDataSetChanged();
textView.setAdapter(adapter);
Ответ 2
У меня была такая же проблема. Изучив исходный код ArrayAdapter и AutoCompleteTextView, я обнаружил, что проблема была, короче говоря, в том, что:
- исходный список объектов хранится в
ArrayAdapter.mObjects
.
- Однако
AutoCompleteTextView
включает фильтрацию ArrayAdapter
, что означает, что новые объекты добавляются в ArrayAdapter.mOriginalValues
, а mObjects
содержит отфильтрованные объекты.
-
ArrayAdapter.getCount()
всегда возвращает размер mObjects
.
Моим решением было переопределить ArrayAdapter.getFilter()
, чтобы вернуть фильтр без фильтрации. Этот способ mOriginalValues
равен null
, а во всех случаях используется mObjects
.
Пример кода:
public class MyAdapter extends ArrayAdapter<String> {
NoFilter noFilter;
/*
...
*/
/**
* Override ArrayAdapter.getFilter() to return our own filtering.
*/
public Filter getFilter() {
if (noFilter == null) {
noFilter = new NoFilter();
}
return noFilter;
}
/**
* Class which does not perform any filtering.
* Filtering is already done by the web service when asking for the list,
* so there is no need to do any more as well.
* This way, ArrayAdapter.mOriginalValues is not used when calling e.g.
* ArrayAdapter.add(), but instead ArrayAdapter.mObjects is updated directly
* and methods like getCount() return the expected result.
*/
private class NoFilter extends Filter {
protected FilterResults performFiltering(CharSequence prefix) {
return new FilterResults();
}
protected void publishResults(CharSequence constraint,
FilterResults results) {
// Do nothing
}
}
}
Ответ 3
Создайте адаптер массива с вектором или массивом вроде:
ArrayAdapter(Context context, int textViewResourceId, T[] objects)
Инициализируя ваш массив, вы заставите его прослушивать массив объектов. Не добавляйте элемент в адаптер или не очищайте адаптер, не добавляйте его в массив объектов и не очищайте его. После того, как изменения в этом массиве вызовут
adapter.notifyDataSetChanged();
Более конкретно
ArrayAdapter<YourContentType> yourAdapter = new ArrayAdapter<YourContentType> (this,R.id.OneOfYourTextViews,YourDataList);
yourAdapter.notifyDataSetChanged();
aTextView.setText(yourAdapter.isEmpty() ? "List is empty" : "I have too many objects:)");
Это должно быть сделано после загрузки YourDataList, я проверил ваш код, уверены ли вы, что обработчик вызывает addStockItemsToAdapter()
, прежде чем смотреть, что ваш адаптер пуст или нет?
Вы также должны проверить, есть ли у вектора запасов какие-либо элементы в нем.