Dinuka Jayasuriya Dinuka Jayasuriya - 11 days ago 7
Android Question

Android: Adapter Changed outside ListView Issue

I am trying to build an Address Searcher using Geocoder API where the user types in names of locations and all the related addresses appear in a listview.

I get this error when running the search:


java.lang.IllegalStateException: The content of the adapter has
changed but ListView did not receive a notification. Make sure the
content of your adapter is not modified from a background thread, but
only from the UI thread.

Make sure your adapter calls
notifyDataSetChanged() when its content changes.


This is my implementation:

SimpleStringAdapter adapter;
ArrayList<String> items = new ArrayList<>();
ListView addressList;

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

addressList = (ListView) findViewById(R.id.addressList);
addressBox = (EditText) findViewById(R.id.addressBox);

//Search for locations when the user types in
addressBox.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(final CharSequence s, int start, int before, int count) {
//Search the typed location
SearchAddresses(searchText);
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {

}

@Override
public void afterTextChanged(Editable s) {

}
});
}

private void SearchAddresses(String searchText) {
Geocoder geoCoder = new Geocoder(this, Locale.getDefault());
try {
List<Address> addresses = geoCoder.getFromLocationName(searchText, 5);
addressList.setAdapter(null);

if (addresses.size() != 0) {
//Add the Address line to the list
items.add(addresses.get(0).getAddressLine(0));
//Populate the listview
adapter = new SimpleStringAdapter(this, items);
addressList.setAdapter(adapter);
}
} catch (Exception e) {
e.printStackTrace();
}
}


And the ListView Adapter simply just sets the text to a TextView. If you need to see it, let me know.

What am I doing wrong?

Answer

Don't initialize adapter everytime in onTextChanged() method. use notifyDataSetChanged(). here is example with your code.

Just Replace your code with this. That's it.

SimpleStringAdapter adapter;
ArrayList<String> items = new ArrayList<>();
ListView addressList;

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

    addressList = (ListView) findViewById(R.id.addressList);
    addressBox = (EditText) findViewById(R.id.addressBox);

    adapter = new SimpleStringAdapter(this, items);
    addressList.setAdapter(adapter);

    //Search for locations when the user types in
    addressBox.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(final CharSequence s, int start, int before, int count) {
                //Search the typed location
                SearchAddresses(searchText);
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                                      int after) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
    });
}

private void SearchAddresses(String searchText) {
    Geocoder geoCoder = new Geocoder(this, Locale.getDefault());
    try {
        List<Address> addresses =     geoCoder.getFromLocationName(searchText, 5);
        addressList.setAdapter(null);

        if (addresses.size() != 0) {
            //Add the Address line to the list
            items.add(addresses.get(0).getAddressLine(0));
            //Populate the listview
            adapter.notifyDataSetChanged();

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Comments