I've been googling my ass of trying to find someone that are having the same problem as me, without luck. So here's my problem:
I'm trying to implement a autocomplete suggestion of addresses as the user types the name of a place using the geocoder in Android. I want this to behave much the same as the javascript version using a combbox.
I am using a layout with an AutoCompleteTextView, and an arrayadapter to dynamically update the suggestionlist as the user types. I have added a 500ms delay from when the onTextChanged() event is received before a call to the geocoder.getFromLocationName is called using a Handler. If a user types more letter within 500ms, the last event will be cancelled. The problem I am encountering is that the suggestions almost never show up in the UI as selectables in the dropdown. I get the address suggestions, but when I add them to the adapter attached to the autocomplatetextview they simple wont show.
I'm running this on an emulator using API level 7, with google apis included.
Now some source code to aid you:
The layout:
<LinearLayout android:id="#+id/searchInputLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="6dip"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/searchMessage" />
<EditText android:id="#+id/freetextInput"
android:hint="#string/searchFreetextLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#android:drawable/editbox_background" />
<CheckBox android:id="#+id/includeVincinityCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/includeVincinityLabel"
android:checked="true"
android:onClick="includeVincinityClick" />
<AutoCompleteTextView android:id="#+id/locationInput"
android:hint="#string/locationInputHint"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="#+id/searchButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/searchBtnLabel"
android:onClick="searchBtnClicked" />
</LinearLayout>
The source code of my activity (I've omitted code not relevant):
public class SearchLocationTabActivity extends Activity implements TextWatcher, OnItemSelectedListener {
private static final int MESSAGE_TEXT_CHANGED = 0;
private static final int AUTOCOMPLETE_DELAY = 500;
private static final int THRESHOLD = 3;
private String latitude, longitude;
private List<Address> autoCompleteSuggestionAddresses;
private ArrayAdapter<String> autoCompleteAdapter;
private Handler messageHandler;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.search);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
messageHandler = new MyMessageHandler(this, this);
autoCompleteAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, new ArrayList<String>());
autoCompleteAdapter.setNotifyOnChange(false);
AutoCompleteTextView locationinput = (AutoCompleteTextView) findViewById(R.id.locationInput);
locationinput.addTextChangedListener(this);
locationinput.setOnItemSelectedListener(this);
locationinput.setThreshold(THRESHOLD);
locationinput.setAdapter(autoCompleteAdapter);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
messageHandler.removeMessages(MESSAGE_TEXT_CHANGED);
}
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
String value = arg0.toString();
if (!"".equals(value) && value.length() >= THRESHOLD) {
Message msg = Message.obtain(messageHandler, MESSAGE_TEXT_CHANGED, arg0.toString());
messageHandler.sendMessageDelayed(msg, AUTOCOMPLETE_DELAY);
} else {
autoCompleteAdapter.clear();
}
}
#Override
public void afterTextChanged(Editable arg0) {
}
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if (arg2 < autoCompleteSuggestionAddresses.size()) {
Address selected = autoCompleteSuggestionAddresses.get(arg2);
latitude = Double.toString(selected.getLatitude());
longitude = Double.toString(selected.getLongitude());
}
}
private void notifyResult(List<Address> suggestions) {
latitude = longitude = null;
autoCompleteAdapter.clear();
for (Address a : autoCompleteSuggestionAddresses) {
autoCompleteAdapter.add(a.toString());//TODO: figure out a nice way to display this address in list
}
autoCompleteAdapter.notifyDataSetChanged();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
latitude = longitude = null;
}
private class MyMessageHandler extends Handler {
private Context context;
private AsyncTaskSubscriber subscriber;
public MyMessageHandler(Context context, AsyncTaskSubscriber subscriber) {
this.context = context;
this.subscriber = subscriber;
}
#Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_TEXT_CHANGED) {
String enteredText = (String) msg.obj;
try {
autoCompleteSuggestionAddresses = new Geocoder(context).getFromLocationName(enteredText, 10);
notifyResult(response);
} catch (IOException ex) {
Log.e(GeoCoderAsyncTask.class.getName(), "Failed to get autocomplete suggestions", ex);
}
}
}
}
}
Any help is much appreciated!
For those who did not managed to remove the filtering, here is what I've done (among other small modifications but I don't think they have an impact on the filtering part). Note also that for a click on one of the item to be detected, you need to add an OnItemClickListener.
autoCompleteAdapter = new ArrayAdapterNoFilter(this, android.R.layout.simple_dropdown_item_1line);
Where ArrayAdapterNoFilter is inspired from this other answer:
public class ArrayAdapterNoFilter extends ArrayAdapter<String> {
public ArrayAdapterNoFilter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
private static final NoFilter NO_FILTER = new NoFilter();
/**
* Override ArrayAdapter.getFilter() to return our own filtering.
*/
#Override
public Filter getFilter() {
return NO_FILTER;
}
/**
* 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 static class NoFilter extends Filter {
protected FilterResults performFiltering(CharSequence prefix) {
return new FilterResults();
}
protected void publishResults(CharSequence constraint, FilterResults results) {
// Do nothing
}
}
}
ok, this one has a really simple solution. The results did not show up all the time because of the filtering mechanism in the AutoCompleteTExtView component. Because all results from the geocoder did not necessarily contain the string typed in, it did not show those results.
This actually works for my geocoding code. I can add the textwatcher to a autocomplete text view, and then get the text and run an async task that will get the list of addresses from googles GeoCode class. Using this no filter array adapter above makes it display the addresses as I type the letters correctly.
Thanks!
Related
I made simple Client server to android.
I have problem when I send an object from server to the client.
The object is received ok and when I check the log, it shows me the the object was sent successfully.
The problem occurs when I'm trying to get this object and put it in my ListView adapter.
The adapter works, I checked it with a random ArrayList I created.
My issue is when I'm trying to to put the values of AsyncTask in my adapter.
public class RestaurantListFragment extends Fragment {
private ArrayList<Resturant> res = new ArrayList<>();
private ListAdapter adapter;
private Firebase restRef = new Firebase("https://restmeup.firebaseio.com/restaurants");
private Client mClient;
// private connectTask t = (connectTask)new connectTask().execute();
public RestaurantListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new connectTask().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// new connectTask(getView()).execute();
final View rootView = inflater.inflate(R.layout.fragment_two, container, false);
ListView restaurantList = (ListView) rootView.findViewById(R.id.list);
adapter = new ListAdapter(getContext(), res, getActivity());
restaurantList.setAdapter(adapter);
// connectTask t = (connectTask)new connectTask().execute();
if (mClient != null) {
mClient.sendMessage("bar");
}
SqlQueriesConverter sql = new SqlQueriesConverter();
sql.getResurantsListQuery("bar");
// sql.getUserFavoritesResturants(accessToken.getUserId());
mClient.sendMessage(sql.getQuery());
// t.setArray(res);
mClient.sendMessage("/quit");
mClient.stopClient();
final EditText searchText = (EditText)rootView.findViewById(R.id.searchListView);
searchText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("Before---------");
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text = searchText.getText().toString().toLowerCase(Locale.getDefault());
adapter.filter(text);
adapter.notifyDataSetChanged();
System.out.println("array: " + res.toString());
}
#Override
public void afterTextChanged(Editable s) {
System.out.println("After---------");
}
});
// Inflate the layout for this fragment
return rootView;
}
public class connectTask extends AsyncTask<ArrayList<?>,ArrayList<?>,Client> {
// private Client mClient;
private ArrayList<?> arrayList = new ArrayList<>();
#Override
protected Client doInBackground(ArrayList<?>... message) {
//we create a Client object and
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> message) {
//this method calls the onProgressUpdate
// publishProgress(message);
onProgressUpdate(message);
}
});
mClient.run();
return null;
}
// #Override
protected void onProgressUpdate(ArrayList<?>... values) {
super.onProgressUpdate(values);
ArrayList<?> arr2;
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
// arr2 = (ArrayList<Resturant>) values[0];
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
if (values[0].get(0)instanceof Review){
arr2 = (ArrayList<Review>) values[0];
}
if (values[0].get(0)instanceof UserFavorites){
arr2 = (ArrayList<Review>) values[0];
Log.d("step 2", "2");
}
}
}
}
There are two things you need to change to use the AsyncTask as you intend. The first change you need is to return the ArrayList you get from your mClient in the doInBackground method. This is a bit troublesome because it looks like the Client is already running asynchronously since you pass a callback to get the result (this is the Client.OnMessageReceived anonymous class you have there). The second change would be to implement onPostExecute on your AsyncTask since that is where the results from doInBackground are sent. You'd add the result sent from doInBackground to your adapter in there.
In any case, since it looks like Client is already performing the work asynchronously, you shouldn't need to use an AsyncTask at all. Just implement the logic to add the results to your adapter in the Client.OnMessageReceived callback.
Just get the code you already have in onProgressUpdate and throw it inside messageReceived. Something like this:
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> values) {
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
}
});
I am trying to update my listview to make an "endless scroll". What happens is that the first 40 results load fine, when i get to the bottom of the scroll, next 40 results replace the first 40...
What I want is for second set of 40 results to add to the first 40 so I have an endless list and ability to scroll back to the beginning of the list.
I am posting my code below. Thank you!
public class SearchResults extends Activity implements BannerAdListener, OnScrollListener{
private LinearLayout bottomNav;
private ListView ringtoneList;
private int start = 0, num = 40, curPage = 1;
private Handler handler = new Handler();
private ProgressDialog progressDialog = null;
private ArrayList<Ringtone> ringtones;
private MoPubView moPubView;
private String searchString;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
if (extras == null) {
// no search string defined
finish();
} else {
searchString = extras.getString("search_string");
}
setContentView(R.layout.search_results);
ringtoneList = (ListView)findViewById(R.id.ringtone_list);
ringtoneList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> av, View view, int position, long id) {
Intent i = new Intent(SearchResults.this, RingtoneView.class);
i.putExtra("ringtone", ringtones.get(position));
startActivity(i);
}
});
performSearch();
moPubView = (MoPubView) findViewById(R.id.adview);
moPubView.setAdUnitId(Utils.MoPubBannerId);
moPubView.loadAd();
moPubView.setBannerAdListener(this);
ringtoneList.setOnScrollListener(this);
}
public void onScroll(AbsListView view, int firstVisible, final int visibleCount, int totalCount) {
Log.i("List", "firstVisible="+firstVisible+" visibleCount="+visibleCount+" totalCount="+totalCount);
boolean loadMore = firstVisible + visibleCount >= totalCount;
if(loadMore) {
Log.i("List", "Loading More Results");
curPage++;
start = num * (curPage-1);
new Thread() {
public void run() {
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
handler.post(new Runnable() {
public void run() {
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
} else {
handler.post(new Runnable() {
public void run() {
new AlertDialog.Builder(SearchResults.this)
.setTitle(getResources().getString(R.string.context_info)).setMessage(getResources().getString(R.string.context_noresult))
.setPositiveButton(getResources().getString(R.string.context_ok), null).show();
}
});
}
}
}
.start();
}
}
public void onScrollStateChanged(AbsListView v, int s) { }
private void performSearch() {
progressDialog = ProgressDialog.show(SearchResults.this, getResources().getString(R.string.loading_message), getResources().getString(R.string.loading_search), true);
new Thread() {
public void run() {
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
updateList();
} else {
handler.post(new Runnable() {
public void run() {
new AlertDialog.Builder(SearchResults.this)
.setTitle(getResources().getString(R.string.context_info)).setMessage(getResources().getString(R.string.context_noresult))
.setPositiveButton(getResources().getString(R.string.context_ok), null).show();
ringtoneList.setVisibility(View.INVISIBLE);
bottomNav.setVisibility(View.INVISIBLE);
}
});
}
progressDialog.dismiss();
}
}.start();
}
private void updateList() {
handler.post(new Runnable() {
public void run() {
//Log.d("search", "ringtones.size() " + ringtones.size());
ringtoneList.setVisibility(View.VISIBLE);
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
}
}
Please help! Thank you!
While I can't be 100% sure, I think your problem has to do with the fact that you're setting a new adapter that only has the section of ringtones that in loads. It probably has to do with this snippet:
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
handler.post(new Runnable() {
public void run() {
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
}
Instead of putting an entirely new array of ringtones, you should add on to the one you already have. Your ringtones variable is already an instance variable, so I'm sure if you changed this line:
ringtones = Utils.search(start, num, searchString);
to the following:
ringtones.addAll(Utils.search(start, num, searchString));
It might fix your problem.
Your code is a little bit messy, but your updateList method is creating a NEW RingtoneRowAdapter. You should ADD items to the list and call
mRingtoneRowAdapter.notifyDatasetChanged();
That will tell the adapter to get new views (if needed) along with a lot of internal stuff happening at Adapter's level. So it's kinda:
private void updateList() {
handler.post(new Runnable() {
public void run() {
if ( mAdapter == null ) {
mAdapter = new RingtoneRowAdapter(SearchResults.this, ringtones);
ringtoneList.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
ringtoneList.setVisibility(View.VISIBLE);
}
});
}
And of course, as already suggested, don't init your ringtones array all the time. Just add data to it.
You don't need (most of the times) to hide the list, you can add in your layout, any View (including a full Layout!) with an id of:
android:id="#android:id/empty"
And if your ListView has an id of android:id="#id/android:list", and you're in a ListFragment or ListActivity, then when the list is empty, the "empty" layout will be shown (which can be a dummy view if you don't want to see it).
Just suggestions :)
UPDATE: For your null, I see that the logic is a little bit weird and since we don't know the requirements for your app (i.e. what to do if there are no results, what to do if the results are invalid, etc.) I'll assume you just want to make sure it works and deal with that later.
So with that in mind, I see two problems.
I assume your Utils.search method can return null, because you're checking for it. To me that feels strange, I'd rather return an empty array indicating that the search produced no results; that little remark aside, you are not checking (or we don't know because we haven't seen the source for your search method), if the searchString is null or not.
You haven't provided a stack trace, so we can't tell where the null is happening (either inside search or ?)
A quick solution would be to check for null before searching… I would personally do this INSIDE the search function.
Something like:
public ArrayList<Ringtone> search(final int start, final int num, final String searchString) {
if ( searchString == null ) {
//DECIDE WHAT YOU WANT TO DO, EITHER:
return null;
// OR YOU CAN RETURN AN EMPTY ARRAY
return new ArrayList<Ringtone>();
}
// You should check for these (change according to your rules)
if (start < 0 ) {
start = 0; // protect yourself from bad data.
}
if ( num < 0 ) {
num = 0;
}
/// THE REST OF YOUR search FUNCTION
return <your array>
}
Now another thing is that you may want the search to return incremental results (so you can ADD them to your array (instead of returning a new array every time). For that, as already suggested, use the addAll trick, but then, DON'T return null, return an new empty array, so there's no harm done if there's nothing else to add.
I manage an AutoCompleteTextView that should give all the towns (ville) found in my DB, according the 4 first letters I put in.
The Async task works well and gets the right data.
My problem is that the DropDownList display NOT all the items. Often only 1, 2, 3 or 4 out of the 20 returned by the DB.
So I figured out, there should be some auto filtering within the ACTV itself!
I check many topics here on SO, to update my code but I didn't succeed.... :-(
I keep getting errors without knowing exactly what the trouble is! :-(
AutoCompleteTextView force to show all items
AutoCompleteTextView - disable filtering
So here is my code:
class MyActivity extends Activity implements AdapterView.OnItemClickListener
{
static class Ville
{
String id;
String name;
#Override
public String toString() { return this.name; }
};
ArrayAdapter<Ville> villeAdapter;
String villeAdapterFilter;
VilleUpdateTask villeAdapterUpdateTask;
AutoCompleteTextView villeText;
Ville selectedVille;
final TextWatcher textChecker = new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
MyActivity.this.setAdapterFilter(s.toString());
}
};
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
setContentView(R.layout.main);
this.villeAdapter = new ArrayAdapter<Ville>(this,android.R.layout.simple_dropdown_item_1line, new Ville[0]);
this.villeText = (AutoCompleteTextView ) findViewById(R.id.villeSelector);
this.villeText.setAdapter(this.villeAdapter);
this.villeText.setThreshold(THRESHOLD_DROPDOWN);
this.villeText.setOnItemClickListener(this);
this.villeText.addTextChangedListener(textChecker);
}
public void onDestroy() { stopVilleAdapterUpdate();
public void setAdapterFilter(String filter)
{
if (filter == null) {
// clearing the adapter
this.villeAdapterFilter = null;
this.villeAdapter.clear();
this.villeAdapter.notifyDataSetChanged();
Log.d("MyActivity","Clearing ville filter !");
} else if (filter.length() > THRESHOLD_QUERY) {
if (this.villeAdapterFilter == null) {
Log.d("MyActivity","Ville Adapter Filter defined to:"+filter);
this.villeAdapterFilter = filter;
startVilleAdapterUpdate();
} else {
Log.d("MyActivity","Already filtered with:"+this.villeAdapterFilter);
}
} else {
Log.d("MyActivity","Resetting filter (not enough data)");
this.villeAdapterFilter = null;
this.villeAdapter.clear();
this.villeAdapter.notifyDataSetChanged();
}
}
public synchronized void onItemClick(ViewAdapter<?> ad, View v, int position, long id)
{
this.selectedVille = this.villeAdapter.getItemAtPosition(position);
Log.d("MyActivity","Ville selected: "+this.selectedVille);
}
public synchronized void startVilleAdapterUpdate()
{
stopVilleAdapterUpdate();
Log.d("MyActivity","Starting Update of Villes with "+this.villeAdapterFilter);
this.villeAdapterUpdateTask = new VilleUpdateTask();
this.villeAdapterUpdateTask.execute(this.villeAdapterFilter);
}
public synchronized void stopVilleAdapterUpdate()
{
if (this.villeAdapterUpdateTask != null) {
Log.d("MyActivity","Stopping current update of villes");
this.villeAdapterUpdateTask.cancel(true);
this.villeAdapterUpdateTask = null;
}
}
public synchronized void onVilleAdapterUpdateResult(Ville[] data)
{
this.villeAdapterUpdateTask = null;
if (data != null) {
Log.d("MyActivity","Received "+data.length+" villes from update task");
this.villeAdapter.clear();
this.villeAdapter.addAll(data);
this.villeAdapter.notifyDataSetChanged(); // mise à jour du drop down...
}
}
class VilleUpdateTask extends AsyncTask<String,Void,Ville[]>
{
public Ville[] doInBackground(String ... filters)
{
ArrayList<Ville> values = new ArrayList<Ville>();
try {
HttpClient httpclient = new DefaultHttpClient();
....
....
for(int i=0;i<json_array.length();i++) {
JSONObject json_ligne = json_array.getJSONObject(i);
try {
Ville v = new Ville();
v.name = json_ligne.getString("NAME_VILLE");
v.id = json_ligne.getString("ID_VILLE");
values.add(v);
} catch (Exception ex) {
Log.w("VilleUpdateTask","Invalid value for Ville at index #"+i,ex);
}
}
} catch (Exception ex) {
Log.e("VilleUpdateTask","Failed to retrieve list of Ville !",ex);
}
return values.toArray(new Ville[values.size()]);
}
public void onPostExecute(Ville[] data)
{
MyActivity.this.onVilleAdapterUpdateResult(data);
}
}
}
EDIT 1: yep sorry, my ACTV is a basic TextView, it is not a scrolling problem because on better case I can see 10 items in the list, and last the position is random
EDIT 2: could you just help me to adapt my existing code to the given solutions from the 2 URLs above?
(1) according to that solution AutoCompleteTextView - disable filtering
I have to:
create my class ClassMyACArrayAdapter which is the same as the given one, only its name changes
change my declaration from
ArrayAdapter villeAdapter;
to
List<ClassMyACArrayAdapter> villeAdapter;
but in the onCreate what should replace the initial
this.villeAdapter = new ArrayAdapter
(this,android.R.layout.simple_dropdown_item_1line, new Ville[0]);
Just call autoCompleteTextView.showDropDown() Whenever you need it.....cheers :)
Is your AutoCompleteTextView a TextView, LinearLayout o ListView? The code in your activity looks fine, so I'm guessing that the problem could be in the layout (maybe you're not using a scroll so you can only see the first values).
Also, the values that you see are always the first ones in the returned list or they're at random positions?
Some users are reporting this error:
java.lang.NullPointerException
at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:291)
at android.widget.AutoCompleteTextView$PopupDataSetObserver$1.run(AutoCompleteTextView.java:1670)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
Here is my code snippet:
private List<City> autoCompleteCities = new ArrayList<City>();
private List<City> autoCompleteCitiesOld = new ArrayList<City>();
private ArrayAdapter<String> autoCompleteAdapter;
private AutoCompleteTextView cityView;
...
autoCompleteAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line);
autoCompleteAdapter.setNotifyOnChange(true);
cityView = (AutoCompleteTextView) findViewById(R.id.city);
cityView.setAdapter(autoCompleteAdapter);
cityView.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
LogUtil.i("!!!!!!!!!!!!! ontextchanged", s.toString());
autoCompleteAdapter.clear();
autoCompleteCitiesOld = autoCompleteCities;
if (s.toString().length() > 2) {
autoCompleteCities = search(s.toString());
for (City city : autoCompleteCities) {
autoCompleteAdapter.add(city.getDisplayName());
}
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
It doesnt happen for me, but I have enough error reports to know it happens for several users. Any idea what is wrong? Why does this happen to only a handful of users?
I found this post but putting the fetching of new autocomplete values in an AsyncTask meant that the results always were one character behind what the user had entered.
Where are you passing the ArrayList to the ArrayAdapter. The NullPointerException throws while counting the list lenght in ArrayAdapter. But the list is not passed to Adapter, so the list object in default Adapter contains null.
My hypothesis is that you are somewhere using static variables which are nulled after activity recreation.
See my answer here: Public static variables and Android activity life cycle management
create android v.4 emulator, go to developer's settings, disable background tasks, disable multiple activities and then try to use your application - chances are it will fail.
Where did u have added the array of String ,the reason of error may be the empty array list
as:
autoCompleteAdapter = new ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line);
here u hav'nt assign any string array
And enter your full code it will help lot to the people to understand the problem
Better do null check before using object like below and do it in search function also..
if (s.toString().length() > 2) {
autoCompleteCities = search(s.toString());
if(autoCompeleteCities!=null){
for (City city : autoCompleteCities) {
autoCompleteAdapter.add(city.getDisplayName());
}
}
I had exactly the same problem with the same issue. In my case I have thousands of streets I dynamically add and remove from the list.
In my case it was always reproducable when I enter some value into the AutoComplete field, let's say 3 characters (while threshold is set to 0), so the dropdown list appears, and then I delete those characters by delete button very quickly. That means I hit the delete button a lot of times. It always crashes then. I figured out that clear causes the crashes. I put the clear in beforeTextChanged and the crashes were gone:
public class StreetTextWatcher implements TextWatcher {
private final StreetArrayAdapter adapter;
private boolean alreadyAdded = false;
public StreetTextWatcher(StreetArrayAdapter adapter) {
this.adapter = adapter;
}
#Override
public void afterTextChanged(Editable s) {
//not used
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (s.length() < 1) {
adapter.clear();
alreadyAdded = false;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() == 1) {
if (s.toString().toLowerCase(Locale.GERMAN).startsWith("a") && !alreadyAdded) {
adapter.addAll(StreetNames.STREETS_A);
alreadyAdded = true;
}
if (s.toString().toLowerCase(Locale.GERMAN).startsWith("b") && !alreadyAdded) {
adapter.addAll(StreetNames.STREETS_B);
alreadyAdded = true;
}
//more streets...
}
}
}
StreetArrayAdapter:
public class StreetArrayAdapter extends ArrayAdapter<String> {
public StreetArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public void addAll(String[] streets) {
for (String street : streets) {
add(street);
}
}
}
I'm developing a browser application for Android 2.2. Of course, this application needs an UI element to enter a URL.
Is there a way to reuse UI components like those used in the default Android browser, Dolphin HD or Fennec? I do not want to rebuild all the features from scratch like
- auto-completion / Suggestions from google
- auto-selecting the domain name (without "www.")
I don't know the exact behaviour of each input type, but maybe this will be a help: https://developer.android.com/reference/android/widget/TextView.html#attr_android:inputType
if you find a good type, please give a short feedback.
Now I've found an interesting thing: There is a widget called AutoCompleteTextView.
You have to set an Adapter, which holds the available auto complete entries. If you catch the text change event, you only have to get the current input from the text view and implement a lookup for available URLs and add the result to the array adapater. These new entries gets displayed as the available auto completes of your current input.
The code can be something like this:
public class AutoCompleteTest
extends Activity
implements TextWatcher
{
private AutoCompleteTextView autoCompleteView;
/** Called when the activity is first created. */
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(layoutRes);
this.autoCompleteView = (AutoCompleteTextView) findViewById(viewId);
this.autoCompleteView.addTextChangedListener(this);
this.autoCompleteView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
public void onTextChanged(final CharSequence currentSubmission, final int start, final int before, final int count)
{
new Thread() {
#Override
public void run()
{
// your code for retrieving url for the current input
// maybe a remote service request
final String[] urls;
final Bundle data = new Bundle();
data.putStringArray("urls", urls);
final Message msg = new Message();
msg.setData(data);
MultiTouchTest.this.messageHandler.sendMessage(new Message());
}
}.start();
}
public void afterTextChanged(final Editable s)
{
// do nothing
}
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after)
{
// do nothing
}
private final Handler messageHandler = new Handler() {
#Override
#SuppressWarnings("unchecked")
public void handleMessage(final Message msg)
{
final ArrayAdapter<String> adapter = (ArrayAdapter<String>) MultiTouchTest.this.autoCompleteView.getAdapter();
final String[] urls = msg.getData().getStringArray("urls");
for (final String url : urls)
{
adapter.add(url);
}
adapter.notifyDataSetChanged();
}
};
}
The code is not tested and I don't know, how to retrieve the URL suggest.