Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android, add() function of ArrayAdapter not working

Tags:

android

I have an ArrayAdapter (myAdapter) attached to an AutoCompleteTextView (textView) component.
Once the user presses a character I would like to populate AutoCompleteTextView's drop down list with items containing this character.
I retrieve the items using AsyncTask (which uses a web service).

I call myAdapter.add(item) but the drop down list is empty.
I added a call myAdapter.getCount() after each addition and it shows zero every time. Calling notifyDataSetChanged() didn't help.
I even tried to add simple String objects instead of my custom objects, to no avail.
What am I doing wrong?

Edit: I changed the code as miette suggested below but still to no avail.
Generally, what I do is after text is changed in my auto complete text view, I call a new AsyncTask and pass it the entered text and a Handler (see afterTextChanged()). The task retrieves objects relevant to the text and once done the Handler's handleMessage() is called. In handleMessage() I attempt to populate the adapter's objects. But still the adapter's drop down list ends up empty.

Here is my code:

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

Thanks a lot, Rob

like image 246
Rob Avatar asked Mar 01 '10 13:03

Rob


People also ask

How do I add items to ArrayAdapter?

Create a global ArrayList and add the contents to it using add() and pass it to ArrayAdapter. It's better to pass the List or String[] to ArrayAdapter and set that adapter to List You should update the List or Array being passed to Adapter, not Adapter itself.

How to update ListView item in Android?

This example demonstrates how do I dynamically update a ListView in android. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml.


1 Answers

I had the exact same problem. After examining the ArrayAdapter and AutoCompleteTextView source code, I found out that the problem was, in short, that:

  • the original object list is stored in ArrayAdapter.mObjects.
  • However, AutoCompleteTextView enables ArrayAdapter's filtering, meaning that new objects are added to ArrayAdapter.mOriginalValues, while mObjects contains the filtered objects.
  • ArrayAdapter.getCount() always returns the size of mObjects.

My solution was to override ArrayAdapter.getFilter() to return a non-filtering filter. This way mOriginalValues is null and mObjects is used instead in all cases.

Sample code:

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
        }
    }
}
like image 131
Giorgos Kylafas Avatar answered Oct 30 '22 06:10

Giorgos Kylafas