I found many questions like this, but no answer. Is it possible to change the ArrayList of the adapter while AutoCompleteTextView is being typed?
private void setUpAutocomplete() {
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_dropdown_item_1line, COUNTRIES);
final AutoCompleteTextView textView = autocompleteTV;
textView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.clear();
ArrayList<String> locations=gAPI.autocomplete(s.toString());
adapter.addAll(locations);
}
#Override
public void afterTextChanged(Editable s) {
}
});
textView.setAdapter(adapter);
}
Yes, there is. The following code retrieves places from the Google Places API and populates the an ArrayAdapter, which changes as the text in the AutocompleteTextView changes.
// declare this variable globally in your activity so that it can be
//referenced and updated in the inner class (public void onResult(...))
private ArrayAdapter<String> adapter;
AutoCompleteTextView locationText = (AutoCompleteTextView) findViewById(R.id.location_text);
adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_dropdown_item_1line);
locationText.setAdapter(adapter);
locationText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String query = s.toString();
//mMap is a GoogleMap object. Alternatively, you can initialize a
//LatLngBounds object directly through its constructor
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
AutocompleteFilter filter = new AutocompleteFilter.Builder()
.setTypeFilter(AutocompleteFilter.TYPE_FILTER_NONE)
.build();
PendingResult<AutocompletePredictionBuffer> autocompleteResult =
Places.GeoDataApi.getAutocompletePredictions(mGoogleApiClient, query,
bounds, filter);
autocompleteResult.setResultCallback(new ResultCallback<AutocompletePredictionBuffer>() {
#Override
public void onResult(#NonNull AutocompletePredictionBuffer autocompletePredictions) {
for (int i = 0; i < autocompletePredictions.getCount(); i++) {
adapter.add(autocompletePredictions.get(0).getFullText(null).toString());
}
autocompletePredictions.release();
adapter.notifyDataSetChanged();
}
});
}
Below is the XML code for the AutocompleteTextView:
<AutoCompleteTextView
android:id="#+id/location_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="Search location..." />
Related
I write this code for search in recyclerview with edit text but, when I run the application and input a text that I need to search about it on the edit text in the first letter the recycler content not changed and when I input the second Letter the RecyclerView become empty.
how can I filter the recycler? what is the wrong in my code ?
xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FamilyActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/search"
android:layout_width="294dp"
android:layout_height="70dp"
android:layout_marginTop="-20dp"
android:hint="Search ..."
android:drawableLeft="#drawable/ic_search_black_24dp"
/>
<Button
android:id="#+id/add"
android:layout_width="54dp"
android:layout_height="match_parent"
android:layout_marginLeft="35dp"
android:drawableTop="#drawable/ic_person_add_black_24dp"
android:onClick="add_new_family"
tools:ignore="OnClick" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginStart="0dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="50dp"></android.support.v7.widget.RecyclerView>
</RelativeLayout>
code in main activity:
public class FamilyActivity extends AppCompatActivity {
RecyclerView recyclerView;
FamilyAdapter familyAdapter;
Button add_family;
EditText search;
List<Family> familyList;
String patientID = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_family);
add_family=(Button)findViewById(R.id.add);
search =(EditText)findViewById(R.id.search);
familyList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Bundle extras = getIntent().getExtras();
if (extras != null) {
patientID = extras.getString("ID");
}
loadFamilyList();
//adding some items to our list
//creating recyclerView adapter
FamilyAdapter adapter = new FamilyAdapter(this, familyList);
//setting adapter to recyclerView
recyclerView.setAdapter(adapter);
addTextListener();
}
public void addTextListener(){
search.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence query, int start, int before, int count) {
query = query.toString().toLowerCase();
final List<Family> filteredList = new ArrayList<>();
for (int i = 0; i < familyList.size(); i++) {
final String text = familyList.get(i).toString().toLowerCase();
if (text.contains(query)) {
filteredList.add(familyList.get(i));
}
}
recyclerView.setLayoutManager(new LinearLayoutManager(FamilyActivity.this));
FamilyAdapter fadapter = new FamilyAdapter(FamilyActivity.this,filteredList);
recyclerView.setAdapter(fadapter);
fadapter.notifyDataSetChanged(); // data set changed
}
});
}
code in adapter:
public class FamilyAdapter extends RecyclerView.Adapter<FamilyAdapter.FamilyViewHolder> {
private Context context;
private List<Family> familyList;
private List<Family> familyListFull;
public FamilyAdapter(Context context, List<Family> familyList) {
this.context = context;
this.familyList = familyList;
familyListFull=new ArrayList<>(familyList);
}
#NonNull
#Override
public FamilyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.list_layout, null);
return new FamilyViewHolder(view);
}
#Override
public void onBindViewHolder(FamilyViewHolder familyViewHolder, final int position) {
Family family = familyList.get(position);
familyViewHolder.textViewTitle.setText(family.getName());
familyViewHolder.familyLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick: clicked on : " + familyList.get(position));
String name = familyList.get(position).getName();
String num = familyList.get(position).getNum();
Intent intent = new Intent(context, Chatting.class);
intent.putExtra("num", num);
intent.putExtra("name", name);
context.startActivity(intent);
}
});
familyViewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(context );
builder.setTitle("Delete");
builder.setMessage("Are you sure you want to delete this one ")
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
RemoveFamilyMember(position);
}
}).setNegativeButton("NO", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
builder.setCancelable(true);
}
});
AlertDialog alert=builder.create();
alert.show();
}
});
}
you can try something like this I think this might work, I haven't tested it but I think that making a new adapter everytime that the edittext has something in it is a bad idea. and if you get to the point back to where the query is equal to "" empty string then you should make the fileterd list back into the whole list which I didn't put in there
public class FamilyActivity extends AppCompatActivity {
RecyclerView recyclerView;
FamilyAdapter familyAdapter;
Button add_family;
EditText search;
List<Family> familyList;
List<Family> filteredList;
String patientID = "";
FamilyAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_family);
add_family=(Button)findViewById(R.id.add);
search =(EditText)findViewById(R.id.search);
familyList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Bundle extras = getIntent().getExtras();
if (extras != null) {
patientID = extras.getString("ID");
}
loadFamilyList();
filteredList = new ArrayList<>(familyList);
//adding some items to our list
//creating recyclerView adapter
adapter = new FamilyAdapter(this, filteredList);
//setting adapter to recyclerView
recyclerView.setAdapter(adapter);
addTextListener();
}
public void addTextListener(){
search.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence query, int start, int before, int count) {
query = query.toString().toLowerCase();
filteredList = new ArrayList<>();
for (int i = 0; i < familyList.size(); i++) {
final String text = familyList.get(i).toString().toLowerCase();
if (text.contains(query)) {
filteredList.add(familyList.get(i));
}
}
recyclerView.removeAllViews();;
adapter.notifyDataSetChanged(); // data set changed
}
});
}
editTextSearch.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
private void filter(String text) {
ArrayList<Model> newList = new ArrayList<>();
for (Model item : mModelList) {
if (item.getTitle().contains(text)){
newList.add(item);
}
}
yourAdapter.setFilter(newList);
}
I'm using array adapter to implement a search view to search for hard coded elements
my code as follows
public class MainActivity extends AppCompatActivity {
ListView list;
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.toolbar);
list = (ListView) findViewById(R.id.list_view);
EditText edittext = (EditText) findViewById(R.id.editText);
edittext.addTextChangedListener(textWatcher);
adapter = new ArrayAdapter<String>(getApplicationContext(), R.layout.row_item, COUNTRIES);
}
TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().equals(""))
list.setAdapter(null);
else {
adapter.getFilter().filter(s.toString());
adapter.notifyDataSetChanged();
list.setAdapter(adapter);
// adapter.clear();
Log.v("HEY", adapter.getCount() + "");
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
private static final String[] COUNTRIES = new String[]{
"FAAAFA", "France", "FOO"};
}
When i type F in the search for example, the count is printed 0 instead of 3
then when i apply any other query the count is 3 (ie it prints the count of previous query to current one)
Here is my code:
public class MainActivity extends AppCompatActivity {
ListView listView;
List all;
SharedPreferences sharedPref;
SharedPreferences.Editor editor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sharedPref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
editor = sharedPref.edit();
setListAdapter();
}
private void setListAdapter(){
final String[] data = displayData().toString().split(",");
ListAdapter listAdapter = new CustomAdapter(this, data);
listView = (ListView) findViewById(R.id.listView);
listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
listView.setAdapter(listAdapter);
}
public List displayData(){
all = new ArrayList<>();
try {
Map<String, ?> allMap = sharedPref.getAll();
for (Map.Entry<String, ?> entry : allMap.entrySet()) {
all.add(entry.getKey().toString() + ":" + entry.getValue().toString());
}
} catch(NullPointerException npe){
all.add(" : ");
}
Log.i("understandCode", "displayData: " + all.toString());
return all;
}
public void add(View view){
editor.putString(" ", " ");
editor.apply();
setListAdapter();
}
private class CustomAdapter extends ArrayAdapter<String> {
SharedPreferences.Editor editor;
String type;
String content;
public CustomAdapter(Context context, String[] resource) {
super(context, R.layout.custom_row, resource);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences
(MainActivity.this);
editor = sharedPref.edit();
}
EditText typeET;
EditText contentET;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
View customView = layoutInflater.inflate(R.layout.custom_row, parent, false);
String singleItem = getItem(position);
typeET = (EditText) customView.findViewById(R.id.type);
contentET = (EditText) customView.findViewById(R.id.content);
String[] givenStrings = singleItem.split(":");
try {
typeET.setText(givenStrings[0]);
contentET.setText(givenStrings[1]);
}catch(ArrayIndexOutOfBoundsException aioobe){
typeET.setText(" ");
contentET.setText(" ");
}
typeET.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
type = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
type = s.toString();
save();
}
#Override
public void afterTextChanged(Editable s) {
}
});
contentET.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
content = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
content = s.toString();
save();
}
#Override
public void afterTextChanged(Editable s) {
}
});
return customView;
}
private void save() {
editor.putString(type, content);
editor.apply();
Log.i("understandCode", type + ":" + content);
}
}
}
Here is my log when I am editing my key, value:
05-04 17:52:52.107 11146-11146/com.x.xI/understandCode: [ my mom!a said:y ]
05-04 17:52:52.462 11146-11146/com.valerdaita.myinfo I/understandCode: [ my mom!a said:yo ]
05-04 17:52:52.698 11146-11146/com.valerdaita.myinfo I/understandCode: [ my mom!a said:yol ]
05-04 17:52:52.897 11146-11146/com.x.x I/understandCode: [ my mom!a said:yolo ]
When I restart the Activity:
05-04 17:53:05.085 12056-12056/com.x.x I/understandCode: displayData: [ : ]
So, it is not showing the data that I saved. I really am having trouble identifying the problem because I know that my save functionality is working well because of the Log, but I cannot identify any problem with the displayer.
You will definitely get NPE as you have not saved any values in your preferences yet.
Code flow is setListAdapter() ->displayData() ->sharedPref.getAll().
In between this you are not adding any key-value pair to preferences.
So why do you expect you'll not get NPE ?
Save some values before using getAll() if you don't want to get NPE.
This sounds easy but it has been hard for me. I have an autocomplete textview that shows user location address. I also implemented a places api to get addresses if user enters a different location other than their location. Everything is working like it is supposed to but the places result is still showing even when there is already an address. To reduce cost I would like to get address results only when the user enters an address. I made a global boolean and set it true when the text is changed like so:
autocomplete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
isTextEntered = true; //to get autocomplete get results only after user enters text
}
#Override
public void afterTextChanged(Editable s) {
}
});
Then I check if the boolean is true when I set my adapter as such:
if (isTextEntered) {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
But doing this in oncreate method of my activity simply blocks the autocomplete from showing any places hint results. How can I accomplish my goal here? As always any help is much appreciated.
This is my custom adapter class:
class GooglePlacesAutocompleteAdapter extends ArrayAdapter implements Filterable {
private ArrayList resultList;
//private ArrayList<HashMap<String, Place>> results = new ArrayList<>();
public GooglePlacesAutocompleteAdapter(Context context, int list, int textViewResourceId) {
super(context, list, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index).toString();
}
//#Override
//public HashMap<String, Place> getItem(int index) {return results.get(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.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
}
Here is my idea to achieve this.
complete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 1. init autocomplete
// 2. show autocomplete
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 1. get enter text
// 2. update the datasource of autocomplete adapter
}
#Override
public void afterTextChanged(Editable s) {
// hide the autocomplete
}
});
Hope it help
What I will suggest to you is just refactor it as a method like here
public void setSelectedPlace() {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
And call it inside afterTextChanged method like here, and hide the autocomplete after that
#Override
public void afterTextChanged(Editable s) {
setSelectedPlace();
// hide the autocomplete
}
I have a ListView that contains an EditText to filter list result. Everything is working well during this phase.
My problem is after clicking an item in the list and then go back to the ListView, the item in the list is gone, most probably the because EditText contains an empty space after back button is pressed. Why is this so?
I've put a Log inside onTextChanged to see what character is being put when the back button is pressed. It's just blank. I'm not sure why this listener adds an empty space and filter the result after back button is pressed.
Here is the code in ListFragment:
public class CategoryFragment extends ListFragment {
...
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// filter result
etFilterCategory.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// Log.d(TAG, "ontextchanged= " + charSequence);
va.getFilter().filter(charSequence);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
}
The BaseAdapter implementing Filterable:
public class CategoryAdapter extends ArrayAdapter<CategoryModel> implements
Filterable {
...
#Override
public Filter getFilter() {
return mFilter;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final ArrayList<CategoryModel> list = originalData;
int count = list.size();
final ArrayList<CategoryModel> nlist = new ArrayList<CategoryModel>(count);
String filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i).getName();
if(filterableString.toLowerCase().contains(filterString)) {
// add the whole custom row
nlist.add(list.get(i));
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrCategory = (ArrayList<CategoryModel>) results.values;
notifyDataSetChanged();
}
}
}
How do I NOT make the ListView item filtered after every back button pressed?
EDIT:
After back button is pressed, the only way to make the ListView visible is to add a space into the EditText and remove it. That will show the previous list. What did I missed?
you may be re-creating your listAdapter object, this will nullify the previously created listAdapter.
Your call of textwatcher is pointing to older listadapter where in onResume/OnCreateView event your new instance of listadapter is generated.
I solved my problem by declaring listadapter object as member in my fragment and checking it in onResume method as follow
#Override
public void onResume() {
super.onResume();
if (news == null) {
if(CommonFun.isInternetOn(activity))
new NewsRetrivalTask().execute();
else
ToastMsg.showToast(activity, Msg.alertInternetMsg);
} else {
newsListAdapter = new NewsListAdapter(getActivity(), news);
lv.setAdapter(newsListAdapter);
lv.setOnItemClickListener(MAP_FragmentChatRoom.this);
lv.setVisibility(View.VISIBLE);
tvNoRecord.setVisibility(View.GONE);
}
}
and textwatcher in onCreateView method
etSearch.addTextChangedListener(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) {
newsListAdapter.getFilter().filter(s);
}
});