I am loading the phone contacts ina list and implementing TextChangedListener on edittext as below
editTxt.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
final TextView noDataFound = (TextView) findViewById(R.id.norecords);
inputName = s.toString();
if(inputName!=null&&!inputName.trim().equals("")){
Log.d(TAG, "LoadMoreEntries --> Constants.loadEntries : "
+ Constants.loadEntries);
if (Constants.loadEntries != null) {
Constants.loadEntries.cancel(true);
}
Constants.loadEntries = new LoadEntries();
Constants.loadEntries.execute();
}
Button closesearch = (Button) findViewById(R.id.closesearch);
if (inputName != null && !inputName.trim().equals("")) {
closesearch.setVisibility(View.VISIBLE);
} else {
closesearch.setVisibility(View.GONE);
}
closesearch.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (Constants.loadEntries != null) {
Constants.loadEntries.cancel(true);
Constants.loadEntries = new LoadEntries();
Constants.loadEntries.execute();
}else {
}
return false;
}
});
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
here when the user types the correct name it is giving the names and when he types wrong name it shows no data.
my issue is when i typing the correct name and erasing, the whole list is loaded but when i type wrong name and displaying
no data and when erasing the name, list is not updating. also i have " x " button after typing name and clicking on that
should get all my list back. Any help is appreciated
Use Google Places AutoComplete API rather than implementing Textwatcher. Google Places AutoComplete API is really effective when you start type and take pause then it will show dropdown and dropdown list is updated at every character.
Using this you can easily update your dropdown list of your autocomplete.
Here is explanation of this.
editTxt.setAdapter(new PlacesAutoCompleteAdapter(this,R.layout.yourlayout));
here is PlacesAutoCompleteAdapter class which is filter result and return filtered result.
private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> resultList;
private String[] myArray;
public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public int getCount() {
return myArray.length;
}
#Override
public String getItem(int index) {
return myArray[index];
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
myArray = autocomplete(constraint.toString()); // here we are calling myAutocomplete method.
// Assign the data to the FilterResults
filterResults.values = myArray;
filterResults.count = myArray.length;
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}};
return filter;
}
}
private String[] autocomplete(String dropdownString) {
ArrayList<String> resultList = null;
StringBuilder jsonResults = new StringBuilder();
String term;
try {
term=URLEncoder.encode(dropdownString, "utf8");
} catch (Exception e) {
e.printStackTrace();
term = dropdownString;
}
StringBuilder sb = new StringBuilder(PLACES_API_BASE + TYPE_AUTOCOMPLETE);
sb.append("?param="+param1+"); // this is parameter if your getting data from server.
sb.append("&term="+term); // this term which you typing in edittext.
String url = sb.toString();
// you can do here anything with your list. get it and populate it.
return myArray;
}
PLACES_API_BASE:- here is url if you are getting data from Web(in my example www.myurl/myapp).
TYPE_AUTOCOMPLETE:- file name or exact location from where are you getting data(in my example abc.php).
If you have any query ask me. don't be hesitate.
Related
I have written a customer filter inside my adapter to allow a user to search by a customer's name. I followed the answers provided on this question, specifically the answer with 35 up-votes, as the selected answer modifies the original list and causes errors. The filtering works correctly, but if you press backspace after searching for a customer name, the results do not update. Here is my filter method, thanks for any help beforehand.
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<CustomerView> suggestions = new ArrayList<>();
if (constraint != null) {
suggestions.clear();
for (CustomerView customer : customers) {
if (customer.getName().toLowerCase().contains(constraint.toString().toLowerCase())) {
suggestions.add(customer);
}
}
results.values = suggestions;
results.count = suggestions.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
if (results != null && results.count > 0) {
addAll((ArrayList<CustomerView>) results.values);
} else {
addAll(customers);
}
notifyDataSetChanged();
}
};
}
Why don't you create your custom search mechanism to filter the results in your Activity or Fragment instead of Adapter.
If you are using an EditText then add "TextWatcher" to your edit text. like the link given below:
How to use the TextWatcher class in Android?
And if you are using Searchview then simply add "OnqueryTextChangeListener" to your serchview. like the link iven below:
how to get the events of searchview in android
And Simply add some mechanism for filtration according to your requirements like the sample code given below:
#Override
public boolean onQueryTextChange(String s) {
textlength = s.length();
arr_sort.clear();
for (int i = 0; i < tunesList.size(); i++) {
String heading = (String) tunesList.get(i).
getTuneName();
String[] words = heading.split("\\s+");
for (String item : words) {
Log.e("Words" , item);
if (s.length() <= (int) item.length()) {
if (s.equalsIgnoreCase((String) item.subSequence(0, s.length())))
{
RowItemMain cObj = tunesList.get(i);
if(arr_sort.contains(cObj))
{
}else{
arr_sort.add(cObj);
}
}
}
}
}
try
{
AdapterMain ivAdp = new AdapterMain(MainActivity.this, arr_sort);
lvSounds.setAdapter(ivAdp);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
I have created a list view in android and I already implement search filter by using searchview and charsequence as parameter like this
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String query) {
adapter.getFilter().filter(query);
return false;
}
});
now how can I get the filter result by more than one value? adapter.getFilter().filter("example" || "example" || example);
is it possible to do that? or maybe can I using adapter.getFilter().filter(array here)?
EDIT:
this is my filter
public class CustomFilter extends Filter {
List<Event> filterList;
EventAdapter adapter;
public CustomFilter(List<Event> filterList, EventAdapter adapter) {
this.filterList = filterList;
this.adapter = adapter;
}
//FILTERING
#Override
protected FilterResults performFiltering(CharSequence constraint) {
//RESULTS
FilterResults results=new FilterResults();
//VALIDATION
if(constraint != null && constraint.length()>0)
{
//CHANGE TO UPPER FOR CONSISTENCY
constraint=constraint.toString().toUpperCase();
ArrayList<Event> filteredEvent=new ArrayList<>();
//LOOP THRU FILTER LIST
for(int i=0;i<filterList.size();i++)
{
//FILTER (tinggal mainin logic aja mau filter berdasarkan apa nya )
if(filterList.get(i).getJudul().toUpperCase().contains(constraint)|| filterList.get(i).getDeskripsi().toUpperCase().contains(constraint))
{
filteredEvent.add(filterList.get(i));
}
}
results.count=filteredEvent.size();
results.values=filteredEvent;
}else
{
results.count=filterList.size();
results.values=filterList;
}
return results;
}
//PUBLISH RESULTS
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
adapter.eventes= (List<Event>) results.values;
adapter.notifyDataSetChanged();
}
}
You can check my reference for your question.
i implement this with one String that include the values for two different filters , and add a split mark (in this case : "-") between the two values in the String.
MainActivity.class
String lat = latitude.getText().toString();
String lon = longitude.getText().toString();
//join the two strings and add a split mark '-'
String join = lat + "-" + lon;
mca.getFilter().filter(join); //mca is my cursorAdapter
mca.notifyDataSetChanged();
mca.setFilterQueryProvider(new FilterQueryProvider() {
#Override
public Cursor runQuery(CharSequence constraint) {
String value = constraint.toString();
return getFilterResults(value);
}
});
getFilterResult(String filterValue)
public Cursor getFilterResults(String filterValue) {
Cursor c = getLoactionTimeEntry(); //some default init
String lat = "";
String lon = "";
String[] splitFilterValue = filterValue.split("-");
if(filterValue.charAt(0) == '-') {
lon = splitFilterValue[1];
c = getLongitudeFilter(lon);
}
else if(splitFilterValue.length == 2) {
lat = splitFilterValue[0];
lon = splitFilterValue[1];
c = getLongtitudeLatitudeFilter(lat,lon);
}
else {
lat = splitFilterValue[0];
c = getLatitudeFilter(lat);
}
return c;
}
in my filters method i split the String with the function split()
and store the values back to separate string's variables.
the functions: getLongitudeFilter(lon) , getLongtitudeLatitudeFilter(lat,lon), getLatitudeFilter(lat)
is my functions that return some Cursor to get what i need from the DataBase in this case.
You cannot change API of the framework class. There is no method on the Filter class that takes multiple constraint arguments. If you want to keep using Filter, you can only combine your arguments in a way that you can separate them again inside of the filter() method, e.g. by joining them into a comma-separated string and then splitting the string.
Alternatively, you don't need to use the Filter class, you can make your own class that can take (for instance) a List<String> and does the filtering you want, and then set the filtered results in your adapter. The only difficult part is now you must handle the logic for doing that work in the background, canceling the work if another filter call happens while it's in progress, etc.
I integrate AutoCompleteTextView in my project, data for that is come from web service but the thing happens, when i input character first time it shows proper result but at second time it shows older data which comes first time instead of new data.
My code is according to below.
private AutocompleteTextview act_search;
List<SearchedItem> resultList = new ArrayList<>();
private SearchItemAdapter searchAdapter;
Initialize in Oncreate Method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manager);
searchAdapter = new SearchItemAdapter(this, R.layout.row_search_item);
act_search.setAdapter(searchAdapter);
act_search.setDropDownBackgroundResource(R.drawable.auto_bg);
act_search.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
Log.d(TAG, "-- on item click position " + position);
}
});
}
Adapter Class
public class SearchItemAdapter extends ArrayAdapter<String> implements Filterable {
private List<SearchedItem> mData;
private boolean error = false;
public SearchItemAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
mData = new ArrayList<SearchedItem>();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int index) {
return mData.get(index).name;
}
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
mData = autocomplete(constraint.toString().trim());
// Assign the data to the FilterResults
filterResults.values = mData;
filterResults.count = mData.size();
Log.d("", "----- inside filter ---- ");
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
Log.d("Map adapter ", " -- result count " + results.count);
if (error) {
// showToast(getString(R.string.errServerNotResponding));
} else {
if (results != null && results.count > 0) {
Log.d("Map adapter ", " -- result count " + results.count);
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
error = false;
}
};
return filter;
}
private List<SearchedItem> autocomplete(String input) {
try {
String userId = PrefSingleton.getInstance().getUserId();
RestClient.getApiClient().searchItem(ConstantsCode.API_KEY, userId, input, new Callback<ResponceSearch>() {
#Override
public void success(ResponceSearch responceSearch, Response response) {
int status = responceSearch.status;
Log.d(TAG, "status -- " + status);
if (status == ConstantsCode.STATUS_OK) {
resultList = responceSearch.data
} else {
String errMessage = responceSearch.message;
showToast(errMessage);
}
}
#Override
public void failure(RetrofitError error) {
}
});
} catch (Exception e) {
Log.e("Map adapter ", "Cannot process JSON results", e);
error = true;
}
return resultList;
}
}
Any help are appreciable, can any one have idea for this? Please help.
Your REST client is running asynchronously, using a callback to get data. The problem is that performFiltering() already runs asynchronously. It is expecting that you will come up with the FilterResults by the end of that method.
Think of it this way: Filter is like AsyncTask. performFiltering() runs on a worker thread like doInBackground() and publishResults() runs on the UI thread like onPostExecute().
So if you have a way to get data with your REST client that waits for the data and doesn't use a callback, you should use that in performFiltering().
As an user is typing in an AutoCompleteTextView, I want to get some results from an webservice and display them in the box.
For this I declared globally
ArrayAdapter<String> adapter;
autoCompleteText;
ArrayList<String> searchList;
I put this in my onCreate(). searchList is an ArrayList where I will get the results from the web service. Search() is my webservice search. I want it to search after the user typed at least 3 chars so that I used a TextWatcher on the field.
adapter = new ArrayAdapter<String>(MyActivity.this
, android.R.layout.simple_list_item_1, searchList);
autoCompleteText.setAdapter(adapter);
autoCompleteText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
if (s.length() >= 3) {
new Search().execute(null, null, null);
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
Method from Search() - GET request in AsyncTask where I update my searchList
#Override
protected void onPostExecute(final Void unused) {
dlg.dismiss();
if (result != null) {
try {
JSONObject myJson = new JSONObject(result.substring(4));
JSONObject resp = myJson.getJSONObject("response");
for (Iterator<String> iterator = resp.keys(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key + " = " + resp.getString(key));
if(! searchList.contains(resp.getString(key)))
searchList.add(resp.getString(key));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
I would prefer to use ArrayAdapter and not a CustomAdapter. Any ideas?
Try calling notifyDataSetChanged() in onPostExecute() after changing the list.
This is how I update my AutoCompleteTextView:
String[] data = terms.toArray(new String[terms.size()]);
// terms is a List<String>
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data);
keywordField.setAdapter(adapter); // keywordField is a AutoCompleteTextView
if(terms.size() < 40) keywordField.setThreshold(1);
else keywordField.setThreshold(2);
Now of course, this is static and doesn't deal with an over-the-air suggestions but, I can also suggest you to notify adapter for the changes after you assign it to the AutoCompleteTextView:
adapter.notifyDataSetChanged();
I'am listing differnt data from the server. Consider i have 5 different type of data in my list. I want to filter the list based on one data. For that I have written the following code.
eventFilter = (EditText) rootView.findViewById(R.id.eventFilter);
eventFilter.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
search_data = s.toString();
}
});
Button searchbtn = (Button) rootView.findViewById(R.id.searchbtn);
searchbtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
search = new SearchFilter(context, cts);
search.getFilter().filter(search_data);
}
});
In the above code just am passing the text value to SearchFilter.
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
if(cFilter==null)
{
cFilter=new CustomFilter();
}
return cFilter;
}
public class CustomFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<CalEvent>(event);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<CalEvent> list;
synchronized (mLock) {
list = new ArrayList<CalEvent>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<CalEvent> values;
synchronized (mLock) {
values = new ArrayList<CalEvent>(mOriginalValues);
}
final int count = values.size();
final ArrayList<CalEvent> newValues = new ArrayList<CalEvent>();
for (int i = 0; i < count; i++) {
CalEvent value=values.get(i);
EventType type = EventType.getEventType(value.event_type);
String valueText = type.name.toLowerCase();
if (valueText.indexOf(prefixString)!=-1) {
newValues.add(value);
Toast.makeText(ctx, value.toString(), Toast.LENGTH_SHORT).show();
Log.i("value.toString",value.toString());
} else {
ProjectEventFragment.cts.clear();
ProjectEventFragment.adapter.notifyDataSetChanged();
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
}
Here if am typing some data which is not available in the list then the list should be empty or if available then it should list based on the available data. If the data is not available then i am doing clear and setting to notifyDataSetChanged. The list is removing all the data when i type non available data. But again when i type the available data then it is not showing all the data.
I don't know where am doing wrong. Please do correct me.
It is very difficult for me to get whole idea unless you post complete code here. Possible mistake you might be doing is querying on original dataset, which may be getting cleared when you enter null in search bar. try to store original dataset as temporary dataset and then query temporary dataset instead of original. And when user enter null show original dataset and when something is typed then query on temporary dataset. Have a look at the sample provided below, this should help.
protected FilterResults performFiltering(CharSequence query) {
FilterResults results = new FilterResults();
ArrayList<Notification> resultList = new ArrayList<Notification>();
ArrayList<TagUser> receivingUsers = new ArrayList<TagUser>();
if (query == null || query.length() == 0) {
results.values = tempNotifcationList;
results.count = tempNotifcationList.size();
} else {
for (int i = 0; i < tempNotifcationList.size(); i++) {
String sendingUser = tempNotifcationList.get(i).getTag().getSendingUser().getDisplayName();
String message = tempNotifcationList.get(i).getTag().getMessage();
if (sendingUser != null) {
if (sendingUser.toLowerCase().startsWith(query.toString().toLowerCase())) {
resultList.add(tempNotifcationList.get(i));
continue;
}
}
for (String key : tempNotifcationList.get(i).getTag().getReceivingUsers().keySet()) {
receivingUsers.add(tempNotifcationList.get(i).getTag().getReceivingUsers().get(key));
}
if (receivingUsers != null && receivingUsers.size() > 0) {
for (TagUser taguser : receivingUsers) {
Log.d(TAG, "Receiving User:" + taguser.getDisplayName());
if (taguser.getDisplayName().toLowerCase().startsWith(query.toString().toLowerCase())) {
Log.d(TAG, "Receiving User:" + taguser.getDisplayName());
resultList.add(tempNotifcationList.get(i));
}
break;
}
continue;
}
if (message != null) {
if (message.toLowerCase().startsWith(query.toString().toLowerCase())) {
resultList.add(tempNotifcationList.get(i));
continue;
}
}
}
Log.d(TAG, "Done Searching");
results.values = resultList;
results.count = resultList.size();
}
Log.d(TAG, "result set count:" + results.count);
return results;
}