Header and footer for AutoCompleteTextView in android - android

I have AutoCompleteTextView attached to my view, and used ArrayAdapter to populate for list of items. But I am unaware of how to add header and footer view for AutocompleteTextView drop down's item.
I know we can add header and footer in listview.
Any suggestions ?

On an AutoCompleteTextView, you don't have direct access to the DropDownListView, that's why you cannot add header and footer views there.
A solution to your problem will be to use 2 types of views in your list, and set the first/last row to have the header's/footer's layout. This can be done on the adapter, which you create yourself.
Here's some info about how to provide different layouts for different rows in a list view:
Android ListView with different layouts for each row

Android provided methods like addHeaderView(View v) and addFooterView(View v) to define headers and footers for the ListViews.
To find an answer on your question, I can refer you to Android: Adding static header to the top of a ListActivity.
Good luck!

private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> resultList;
public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.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;
}
}
where autocomplete function should return the arraylist of string

Related

Filter in listview did not filter from the full list, but from already filtered list

I have an listview with filter. When I input some words in an edittext that I used as a filter for example "david", it works well, items in the list are filtered and it will show all item that contains "david". But when I delete some words, for example "dav", the list is still filtered, but it filtered from the last filtered by "david".
Let's say I had 40 items, filtered by "david", it becomes 24 items. Then I filtered it again with "dav", it filtered from the "24 items" one, not the "40 items" one.
Here is my custom adapter:
public class WRegistrantListAdapter extends ArrayAdapter<Registrant> {
private Context mContext;
private int mResource;
private List<Registrant> mOriginalList;
private List<Registrant> mFilteredList;
public WRegistrantListAdapter(Context context, int resource, ArrayList<Registrant> oobjects, int workshopItemId) {
super(context, resource, oobjects);
mContext = context;
mResource = resource;
mFilteredList = oobjects;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
//contains code for displaying item.
}
#NonNull
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults result = new FilterResults();
String constraint = charSequence.toString().toLowerCase();
if (mOriginalList == null) {
mOriginalList = mFilteredList;
Toast.makeText(mContext, String.valueOf(mOriginalList.size()), Toast.LENGTH_SHORT).show();
}
if (constraint == null || constraint.isEmpty() || constraint.equals("")) {
result.values = mOriginalList;
result.count = mOriginalList.size();
} else {
List<Registrant> list = new ArrayList<>();
int max = mOriginalList.size();
for (int cont = 0; cont < max; cont++) {
Registrant item = mOriginalList.get(cont);
boolean contains =
item.getRegistrantName().toLowerCase().contains(constraint) ||
item.getRegistrantNumber().toLowerCase().contains(constraint);
if (contains) {
list.add(mOriginalList.get(cont));
}
}
result.values = list;
result.count = list.size();
}
return result;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((ArrayList<Registrant>) results.values);
notifyDataSetChanged();
}
};
}
}
Which part in the filtering is wrong? Any help would be much appreciated. Hope my explanation is not confusing because English is not my mother language.
You need two different lists for filtering, so try change mOriginalList = mFilteredList; to mOriginalList = new ArrayList<>(mFilteredList); may solve the issue.
Explanations:
mOriginalList = mFilteredList; is same list with two different names. It is helpful in modular program, just like mFilteredList = oobjects; in your adapter constructor.
mOriginalList = new ArrayList<>(mFilteredList); is to make a shallow copy of mFilteredList and store it as mOriginalList, so the lists are different.
Shallow and Deep Copy:
Example: If your custom class, Registrant, contains a public field (List, Map or custom object etc., that requires new for creation) named sample. Under shallow copy, mOriginalList = new ArrayList<>(mFilteredList);, mOriginalList.get(i) is a copy of mFilteredList.get(i) and they are 2 different Registrant objects. But mOriginalList.get(i).sample and mFilteredList.get(i).sample is the same object.
If you need mOriginalList.get(i).sample and mFilteredList.get(i).sample to be different objects, then it is called deep copy. There is no ready method to make deep copy, you have to make your own method according to your custom class. But up to now, I never have a case that needs deep copy.
Hope that helps!
You should keep two separate lists in your adapter such as,
private List<Registrant> mOriginalList = new ArrayList();
private List<Registrant> mFilteredList = new ArrayList();
public WRegistrantListAdapter(Context context, int resource, ArrayList<Registrant> oobjects, int workshopItemId) {
super(context, resource, oobjects);
mContext = context;
mResource = resource;
mFilteredList.addAll(oobjects);
mOriginalList.addAll(oobjects);
}
Initially, both of them should have the same value and you will use filteredList for showing your data. Later in the filter, you should publish your data like
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredList.clear();
filteredList.addAll((ArrayList<Registrant>) results.values);
notifyDataSetChanged();
}
A complete example is can be found in Filter ListView with arrayadapter

How to pass the List from the Recylerview to the Class

I want to pass the arraylist from the Recylerview to the class , how i can achieve it ?
Here is my custom filter class
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults=new FilterResults();
if(constraint!=null && constraint.length()>0){
constraint=constraint.toString().toUpperCase();
filterArrayList=new ArrayList<>();
for(int i=0;i<arrayList.size();i++){
if(arrayList.get(i).getId().toUpperCase().contains(constraint)){
filterArrayList.add(arrayList.get(i));
}
}
filterResults.count=filterArrayList.size();
filterResults.values=filterArrayList;
}else{
filterResults.count= arrayList.size();
filterResults.values=arrayList;
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//Here i want the arraylist from the recylerview without making the arraylist static
// adapter.items= (List<OrderPogo>) results.values;
adapter.notifyDataSetChanged();
}
You should actually get the arrayList on performFiltering method. After filtering the list, at the end you can write
filterResults.values = yourList;
filterResults.count = yourList.size();
and then
return filterResults
You will get the filtered results list on publishResults method via
result.values
The best way to access your arrayList inside the Filter class is to create your Filter class inside RecycleView.

notifyDataSetChanged method is not refreshing listview using updated data?

I am using an ArrayAdapter with filterable interface to implement search functionality in a listview.My code for adapter is following:
public class FlightsAdapter extends ArrayAdapter<Flight> implements Filterable {
List<Flight> flightLists = new ArrayList<Flight>();
private List<Flight> origFlightList;
private Context context;
Flight flight = null;
public FlightsAdapter(Context context, int textViewResourceId,
List<Flight> fLists) {
super(context, textViewResourceId, fLists);
this.context = context;
this.flightLists = fLists;
}
public void updateFlightList(List<Flight> newData){
this.flightLists.clear();
this.flightLists = newData;
}
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
final FilterResults oReturn = new FilterResults();
final List<Flight> results = new ArrayList<Flight>();
if (origFlightList == null)
origFlightList = flightLists;
if (constraint != null) {
if (origFlightList != null && origFlightList.size() > 0) {
for (final Flight f : origFlightList) {
if (f.getPlace().toLowerCase()
.contains(constraint.toString()))
results.add(f);
}
}
oReturn.values = results;
oReturn.count = results.size();
}
return oReturn;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
flightLists = (ArrayList<Flight>) results.values;
notifyDataSetChanged();
}
};
}
}
getFilter() and publishResults() methods get triggered properly on searching and flightLists get populated with new data but the listview remain same with no items change, I don't know what I am doing wrong in above code, Please help me figure out this problem
Here is the problem
flightLists = (ArrayList<Flight>) results.values;
Replace this with
flightLists.clear();
flightLists.addAll((ArrayList<Flight>) results.values);
See if that fixes your issue.
Later edit (explanation):
When you created your adapter, you gave it a List object. The adapter thus has a reference, a pointer, to that List, from where it takes the data to display.
When you have replaced that reference with a whole other block of memory
flightLists = (ArrayList<Flight>) results.values;
the adapter didn't updated the orignial data set (from where it actually was taking data to display). Thus, notifyDataSetChanged() didn't work.
When updating the data in an adapter, you have to keep the original data structure reference intact. Clearing and populating with another data set is an option. Reinstantiating it (in any way) isn't.
This is basic java.

fill AutoCompleteTextView using BaseAdapter android

can someone please give me an link of filling AutoCompleteTextView using BaseAdapter in android for phone contacts.
Thanks
user750716 is wrong.
You can fill AutoCompleteTextView with BaseAdapter. You just need to remember that BaseAdapter has to implement Filterable.
In Adapter create your ArrayList of Objects.
Implement getView with whatever View you want and fill it with objects.get(position) info.
Implement getItem(int position) returning a string (name of clicked object)
In the same adapter add stuff for filtering:
public Filter getFilter() {
return new MyFilter();
}
private class MyFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence filterString) {
// this will be done in different thread
// so you could even download this data from internet
FilterResults results = new FilterResults();
ArrayList<myObject> allMatching = new ArrayList<myObject>()
// find all matching objects here and add
// them to allMatching, use filterString.
results.values = allMatching;
results.count = allMatching.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
objects.clear();
ArrayList<myObject> allMatching = (ArrayList<myObject>) results.values;
if (allMatching != null && !allMatching.isEmpty()) {
objects = allMatching;
}
notifyDataSetChanged();
}
}
This is not possible with an BaseAdapter but you can use a CursorAdapter with no SQLite DB in the background. In the function runQueryOnBackgroundThread you can create an MatrixCursor.
Something like this:
String[] tableCols = new String[] { "_id", "keyword" };
MatrixCursor cursor = new MatrixCursor(tableCols);
cursor.addRow(new Object[] { 1, "went" });
cursor.addRow(new Object[] { 2, "gone" });
I have done this in my morphological analyzer.

AutoCompleteTextView onItemClick item position or id using HashMap

I am new to Android development and I ran into a problem which I find difficult to solve. I am trying to figure out how to use an AutoCompleteTextView widget properly. I want to create a AutoCompleteTextView, using XML data from a web service. I managed to get it to work, but I am defenitely not pleased with the output.
I would like to put a HashMap with id => name pairs into the AutoCompleteTextView and get the id of the clicked item. When I click on the autocomplete filtered set output, I want to populate a list underneath the autocompletion box, which I also managed to get to work.
Done so far:
autocomplete works well for simple ArrayList, all data filtered correct
onItemClick event fires properly after click
parent.getItemAtPosition(position) returns correct String representation of the clicked item
The event onItemClick(AdapterView parent, View v, int position, long id) does not behave as I would like. How can I figure out the unfiltered array position of the clicked item? The position of the filtered one is the one I am not interested in.
Further questions:
How to handle HashMaps or Collections in AutoCompleteTextView
How to get the right itemId in the onItemClick event
I did very extensive research on this issue, but did not find any valuable information which would answer my questions.
How to handle HashMaps or Collections in AutoCompleteTextView
You can set your own custom adapter. In your adapter it's up to you where you get your data to a given position.
How to get the right itemId in the onItemClick event
In your custom adapter, you define a filter, and that filter sets the suggested items. You have two different list, one with the original values, and another one with the filtered items. I mean something like this.
private class AutoCompleteItemAdapter extends ArrayAdapter<YourItemClass> implements Filterable {
private NameFilter mFilter;
List<YourItemClass> suggestions;
List<YourItemClass> mOriginalValues;
public AutoCompleteItemAdapter(Context context, int resource, List<YourItemClass> suggestions) {
super(context, resource, suggestions);
this.suggestions = suggestions;
this.mOriginalValues = suggestions;
}
public void updateData(List<YourItemClass> suggestions) {
mLock.lock();
try{
this.suggestions = suggestions;
this.mOriginalValues = suggestions;
finally{
mLock.unlock();
}
}
#Override
public int getCount() {
mLock.lock();
try {
return suggestions.size();
} finally {
mLock.unlock();
}
}
#Override
public YourItemClass getItem(int position) {
return mOriginalValues.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// draw your item here...
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new NameFilter();
}
return mFilter;
}
private class NameFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
mLock.lock();
try {
mOriginalValues = new ArrayList<YourItemClass>(suggestions);
} finally {
mLock.unlock();
}
}
if (prefix == null || prefix.length() == 0) {
mLock.lock();
try {
ArrayList<YourItemClass> list = new ArrayList<YourItemClass>(mOriginalValues);
results.values = list;
results.count = list.size();
} finally {
mLock.unlock();
}
} else {
String prefixString = prefix.toString().toLowerCase();
final List<YourItemClass> values = mOriginalValues;
final int count = values.size();
final ArrayList<YourItemClass> newValues = new ArrayList<YourItemClass>(count);
// FILTERING
//
// add your hits to the newValues collection
//
//
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mLock.lock();
try {
if (results == null || results.values == null) return;
suggestions = new ArrayList<YourItemClass>();
suggestions = (List<YourItemClass>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
} finally {
mLock.unlock();
}
}
}
}
Now this might raise some concurrency issue, as we the reference, the adapter might ask for the size of the list, and a bigger value goes out, which might cause problem in the getView function. (fi.: Attempting to draw 5 element with the underlying data has only 4, because we did another filtering) This the way we used our AutoCompleteTextView, and so far it works great, no problem. I just mentioned that I'm still concerned, and I have this vague feeling that there is a better solution for this.
In your onClick listener, you use the returned value(from the filtered list) as the key in your map, and get the associated value. You can think your list you use an index for the HashMap. After that, you can use your Map to draw your item, or to get your own data.

Categories

Resources