0

Hello I'm having problems understanding how the ArrayAdapter works. My code is working but I dont know how.(http://amy-mac.com/images/2013/code_meme.jpg)

I have my activity, inside it i have 2 private classes.:

public class MainActivity extends Activity {
    ...
    private void SomePrivateMethod(){
        autoCompleteTextView.setAdapter(new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_dropdown_item, new ArrayList<String>(Arrays.asList(""))));
        autoCompleteTextView.addTextChangedListener(new MyTextWatcher());
    }
    ...

    private class MyTextWatcher implements TextWatcher {
        ...
    }
    private class SearchAddressTask extends AsyncTask<String, Void, String[]> {
        ...
    }
}

Now inside my textwatcher class i call the search address task:

@Override
public void afterTextChanged(Editable s) {
    new SearchAddressTask().execute(s.toString());
}

So far so good.

In my SearchAddressTask I do some stuff on doInBackground() that returns the right array.

On the onPostExecute() method i try to just modify the AutoCompleteTextView adapter to add the values from the array obtained in doInBackground() but the adapter cannot be modified:

NOT WORKING CODE:

protected void onPostExecute(String[] addressArray) {
    ArrayAdapter<String> adapter = (ArrayAdapter<String>) autoCompleteDestination.getAdapter();
    adapter.clear();
    adapter.addAll(new ArrayList<String>(Arrays.asList(addressArray)));
    adapter.notifyDataSetChanged();
    Log.d("SearchAddressTask", "adapter isEmpty : " + adapter.isEmpty()); // Returns true!!??!
}

I dont get why this is not working. Even if i run it on UI Thread...

I kept investigating, if i recreate the arrayAdapter, is working in the UI (Showing the suggestions), but i still need to clear the old adapter:

WORKING CODE:

protected void onPostExecute(String[] addressArray) {
    ArrayAdapter<String> adapter = (ArrayAdapter<String>) autoCompleteDestination.getAdapter();
    adapter.clear();
    autoCompleteDestination.setAdapter(new ArrayAdapter<String>(NewDestinationActivity.this,android.R.layout.simple_spinner_dropdown_item, new ArrayList<String>(Arrays.asList(addressArray))));
    //adapter.notifyDataSetChanged(); // no needed
    Log.d("SearchAddressTask", "adapter isEmpty : " + adapter.isEmpty()); // keeps returning true!!??!
}

So my question is, what is really happening with this ArrayAdapter? why I cannot modify it in my onPostExecute()? Why is working in the UI if i am recreating the adapter? and why i need to clear the old adapter then?

I dont know there are so many questions that I need some help in here!!

Thanks!!

Roi
  • 1,434
  • 2
  • 11
  • 13
  • instead of clearing adapter you need change the list content in `onPostExecute` and call `adapter.notifyDatasetChanged();` – Shayan Pourvatan Jun 07 '14 at 11:54
  • what is SearchAddressTask used for? – pskink Jun 07 '14 at 11:56
  • Shayan, I am changing the list content in the onPostExecuteMethod, i am using adapter.AddAll and then adapter.notifyDataSetChanged(). Read the whole question please. http://developer.android.com/reference/android/widget/ArrayAdapter.html#clear() http://developer.android.com/reference/android/widget/ArrayAdapter.html#addAll(java.util.Collection extends T>) – Roi Jun 07 '14 at 12:06
  • pskink as u can see in the question, the SearchAddressTask is used in the text change, it modifies the autocompletedestination that is an AutoCompleteTextView. The values of the input array for the onPostExecute() is correct. You can see that i have an example of code working. – Roi Jun 07 '14 at 12:09
  • 1
    dont take it personally but your current solution is wrong: you dont use at all the functionality that is already built in the system: a Filter class, when you use it you will not have to use any Watcher, AsyncTask, clearing and setting the Adapter etc, read about Filter and Filterable – pskink Jun 07 '14 at 12:17
  • Well i cannot use the filter class, the content is generated in the doInBackground method with every TextWatcher.afterTextChanged(). So i cannot filter something i dont have. – Roi Jun 07 '14 at 13:26
  • 1
    this a perfect job for a Filter then! – pskink Jun 07 '14 at 13:35
  • I think i didn't explain myself well. The content that the filter would filter is not generated. I have an AutoCompleteTextView. I introduce "n". Now afterTextChanged() will call searchAddressTask (AsyncTask) that will download from internet a list of 5 strings. I need now to populate the array with this 5 values. Next the user input "e" show the textwatcher has "ne". I need to clear the array and populate with the 5 new results from internet for "ne" and so on. – Roi Jun 07 '14 at 13:49
  • 1
    yes you explained it right, see my answer here http://stackoverflow.com/questions/19858843/how-to-dynamically-add-suggestions-to-autocompletetextview-with-preserving-chara how i query wikipedia web search api, it uses a Filter behind the scenes so you don't to implement the Filter by yourself – pskink Jun 07 '14 at 13:52
  • Ok! now i get what you mean!!! Thanks as Lot!!! Your solution is much better than the TextWatcher, i leave all the responsability to the Filter ! Now my code is much cleaner! I will post my solution as an answer. Thanks a lot for your help! – Roi Jun 07 '14 at 14:29
  • you're welcome, nie ma za co – pskink Jun 07 '14 at 14:44

1 Answers1

0

I followed the recomendation of pskink, Thanks a lot!!

I switched all the responsability to the ArrayAdapter and implemented my own as filter.

import java.util.ArrayList;

import android.content.Context;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;

import com.roisoftstudio.travelalarm.gui.utils.StreetListHelper;

public class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
    private ArrayList<String> mData;
    private Context context;

    public AutoCompleteAdapter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
        this.context = context;
        mData = new ArrayList<String>();
    }

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

    @Override
    public String getItem(int index) {
    return mData.get(index);
    }

    @Override
    public Filter getFilter() {
        Filter myFilter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults filterResults = new FilterResults();
                if (constraint != null) {
                    mData = StreetListHelper.getStreetList(context, constraint.toString());
                    filterResults.values = mData;
                    filterResults.count = mData.size();
                }
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
            }

        };
        return myFilter;
    }
}
Roi
  • 1,434
  • 2
  • 11
  • 13
  • 1
    you dont need SearchAddressTask at all (performFiltering is executed in background thread), see the link i posted again – pskink Jun 07 '14 at 14:52
  • also you dont use results in publishResults and you have to, btw why not just to use FilterQueryProvider? its as simple as it should be: you dont need to do anything but just return the data – pskink Jun 07 '14 at 15:04
  • Thanks, i removed the Asynctask. Didnt know that performFiltering was executed in another thread. Regarding FilterQueryProvider, i dont know but does it force me to work with cursors? if so i think i will leave as it is, i prefer to perform this simple task with a string[]. – Roi Jun 07 '14 at 15:23
  • whats wrong with a MatrixCursor? note my whole code including creavting ACTV, creating Adapter, settingContentView, quering web api etc (the complete working code to add to onCreate) takes only 40 lines of code (only your adapter takes more lines) – pskink Jun 07 '14 at 15:29
  • Well i see is oriented for databases, I see that it contains less code, how ever i see more clear the code with the Filterable interface. Thanks a lot for your help and comments to improve my code! – Roi Jun 07 '14 at 15:47