I've been having difficulty creating a custom ArrayAdapter for AutoCompleteTextView such errors that would come up despite following code found on the internet would be:
Dropdown would not appear.
Custom Objects and their details would not appear.
So for those who are having or had the same problem as me, I recommend using BaseAdapter for AutoCompleteTextView instead.
The following is my working code using ArrayAdapter.
Let's assume the reponse data from web service looks like the following:
[
{
"id": "1",
"name": "Information Technology"
},
{
"id": "2",
"name": "Human Resources"
},
{
"id": "3",
"name": "Marketing and PR"
},
{
"id": "4",
"name": "Research and Developement"
}
]
Then in your Android client:
Department class:
public class Department {
public int id;
public String name;
}
Custom Adapter class:
public class DepartmentArrayAdapter extends ArrayAdapter<Department> {
private final Context mContext;
private final List<Department> mDepartments;
private final List<Department> mDepartmentsAll;
private final int mLayoutResourceId;
public DepartmentArrayAdapter(Context context, int resource, List<Department> departments) {
super(context, resource, departments);
this.mContext = context;
this.mLayoutResourceId = resource;
this.mDepartments = new ArrayList<>(departments);
this.mDepartmentsAll = new ArrayList<>(departments);
}
public int getCount() {
return mDepartments.size();
}
public Department getItem(int position) {
return mDepartments.get(position);
}
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(mLayoutResourceId, parent, false);
}
Department department = getItem(position);
TextView name = (TextView) convertView.findViewById(R.id.textView);
name.setText(department.name);
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
#Override
public Filter getFilter() {
return new Filter() {
#Override
public String convertResultToString(Object resultValue) {
return ((Department) resultValue).name;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
List<Department> departmentsSuggestion = new ArrayList<>();
if (constraint != null) {
for (Department department : mDepartmentsAll) {
if (department.name.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
departmentsSuggestion.add(department);
}
}
filterResults.values = departmentsSuggestion;
filterResults.count = departmentsSuggestion.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mDepartments.clear();
if (results != null && results.count > 0) {
// avoids unchecked cast warning when using mDepartments.addAll((ArrayList<Department>) results.values);
for (Object object : (List<?>) results.values) {
if (object instanceof Department) {
mDepartments.add((Department) object);
}
}
notifyDataSetChanged();
} else if (constraint == null) {
// no filter, add entire original list back in
mDepartments.addAll(mDepartmentsAll);
notifyDataSetInvalidated();
}
}
};
}
}
Main Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
mAutoCompleteTextView.setThreshold(1);
new DepartmentRequest().execute();
}
private class DepartmentRequest extends AsyncTask<Void, Void, JSONArray> {
#Override
protected JSONArray doInBackground(Void... voids) {
OkHttpJsonArrayRequest request = new OkHttpJsonArrayRequest();
try {
return request.get("http://...");
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(JSONArray jsonArray) {
super.onPostExecute(jsonArray);
if (jsonArray != null && jsonArray.length() > 0) {
Gson gson = new Gson();
Department[] departments = gson.fromJson(jsonArray.toString(), Department[].class);
mDepartmentList = Arrays.asList(departments);
mDepartmentArrayAdapter = new DepartmentArrayAdapter(mContext, R.layout.simple_text_view, mDepartmentList);
mAutoCompleteTextView.setAdapter(mDepartmentArrayAdapter);
}
}
}
private class OkHttpJsonArrayRequest {
OkHttpClient client = new OkHttpClient();
// HTTP GET REQUEST
JSONArray get(String url) throws IOException, JSONException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return new JSONArray(response.body().string());
}
}
Here's the screenshot:
Hope this helps!
Custom BaseAdapter Class
public class ObjectAdapter extends BaseAdapter implements Filterable {
private Context context;
private ArrayList<Object> originalList;
private ArrayList<Object> suggestions = new ArrayList<>();
private Filter filter = new CustomFilter();
/**
* #param context Context
* #param originalList Original list used to compare in constraints.
*/
public ObjectAdapter(Context context, ArrayList<Object> originalList) {
this.context = context;
this.originalList = originalList;
}
#Override
public int getCount() {
return suggestions.size(); // Return the size of the suggestions list.
}
#Override
public Object getItem(int position) {
return suggestions.get(position).getCountryName();
}
#Override
public long getItemId(int position) {
return 0;
}
/**
* This is where you inflate the layout and also where you set what you want to display.
* Here we also implement a View Holder in order to recycle the views.
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.adapter_autotext,
parent,
false);
holder = new ViewHolder();
holder.autoText = (TextView) convertView.findViewById(R.id.autoText);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.autoText.setText(suggestions.get(position).getCountryName());
return convertView;
}
#Override
public Filter getFilter() {
return filter;
}
private static class ViewHolder {
TextView autoText;
}
/**
* Our Custom Filter Class.
*/
private class CustomFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
suggestions.clear();
if (originalList != null && constraint != null) { // Check if the Original List and Constraint aren't null.
for (int i = 0; i < originalList.size(); i++) {
if (originalList.get(i).getCountryName().toLowerCase().contains(constraint)) { // Compare item in original list if it contains constraints.
suggestions.add(originalList.get(i)); // If TRUE add item in Suggestions.
}
}
}
FilterResults results = new FilterResults(); // Create new Filter Results and return this to publishResults;
results.values = suggestions;
results.count = suggestions.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
Main Activity Class
public class MainActivity extends AppCompatActivity{
private SGetCountryListAdapter countryAdapter;
private ArrayList<SGetCountryList> countryList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
country = (AutoCompleteTextView) findViewById(R.id.country);
countryAdapter = new SGetCountryListAdapter(getApplicationContext(),
ConnectionParser.SGetCountryList);
country.setAdapter(countryAdapter);
country.setThreshold(1);
}
}
Drop down layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/autoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:textColor="#color/black" />
</LinearLayout>
My Original List has data taken from web service so let's just assume that it already has data. Of course you can customize the dropdown even more by adding more views, just don't forget to update the adapter in order to incorporate the new views.
Related
I wrote a custom ArrayAdapter with a custom Filter for my AutoCompleteTextView. It shows everything correctly, but when I filter the suggestions and click on an item, it takes the string of the item that was at this position in the suggestion list when ALL items were shown. I made screenshot to clarify what I mean:
And this is my code:
public class AutoCompleteCountryAdapter extends ArrayAdapter<CountryItem> {
private List<CountryItem> countryList;
private List<CountryItem> filteredCountryList = new ArrayList<>();
public AutoCompleteCountryAdapter(#NonNull Context context, #NonNull List<CountryItem> countryList) {
super(context, 0, countryList);
this.countryList = countryList;
}
#Override
public int getCount() {
return filteredCountryList.size();
}
#NonNull
#Override
public Filter getFilter() {
return countryFilter;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
CountryItem countryItem = filteredCountryList.get(position);
LayoutInflater inflater = LayoutInflater.from(getContext());
if (convertView == null) {
convertView = inflater.inflate(
R.layout.country_autocomplete_row, parent, false
);
}
TextView textViewName = convertView.findViewById(R.id.text_view_name);
ImageView imageViewFlag = convertView.findViewById(R.id.image_view_flag);
textViewName.setText(countryItem.getCountryName());
imageViewFlag.setImageResource(countryItem.getFlagImage());
return convertView;
}
private Filter countryFilter = new Filter() {
private List<CountryItem> suggestions = new ArrayList<>();
#Override
protected FilterResults performFiltering(CharSequence constraint) {
suggestions.clear();
FilterResults results = new FilterResults();
if (constraint == null || constraint.length() == 0) {
suggestions.addAll(countryList);
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for (CountryItem item : countryList) {
if (item.getCountryName().toLowerCase().contains(filterPattern)) {
suggestions.add(item);
}
}
}
results.values = suggestions;
results.count = suggestions.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredCountryList.clear();
filteredCountryList.addAll((List) results.values);
notifyDataSetChanged();
}
#Override
public CharSequence convertResultToString(Object resultValue) {
return ((CountryItem) resultValue).getCountryName();
}
};
}
I solved it.
I have no idea why this did not appear in any tutorial I found (including the Materialdoc one), but you also have to override getItem in the adapter, to pick it's item from the filtered List, not the original List:
public CountryItem getItem(int position) {
return filteredCountryList.get(position);
}
i provide one adapter class that used recylerview adapter for user contact adapter that filter user contact. you can make changes your requirement according into code..
public class InviteContactAdapter extends RecyclerView.Adapter<InviteContactAdapter.ItemViewHolder> implements Filterable {
private List<UserContact> mContactList = new ArrayList<>();
private List<UserContact> mContectFilter = new ArrayList<>();
private Context mContext;
private CustomFilter mFilter;
public List<String> mEmailList = new ArrayList<>();
public InviteContactAdapter(Context context, List<UserContact> mContactList) {
mContext = context;
this.mContactList = mContactList;
this.mContectFilter = mContactList;
mFilter = new CustomFilter();
}
public onItemClickListener onItemClickListener;
public void setOnItemClickListener(InviteContactAdapter.onItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
#Override
public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.invite_contact_row_layout, viewGroup, false);
return new ItemViewHolder(view);
}
public interface onItemClickListener {
void onClick(UserContact contact);
}
#Override
public Filter getFilter() {
return mFilter;
}
#Override
public void onBindViewHolder(final ItemViewHolder itemViewHolder, int i) {
final UserContact contact = mContectFilter.get(i);
itemViewHolder.mTvUserNane.setText(contact.getUserName().trim());
itemViewHolder.mTvUserEmail.setText(contact.getUserEmail().trim());
if (contact.isSelect())
itemViewHolder.mIvSelect.setImageResource(R.drawable.check_contect);
else
itemViewHolder.mIvSelect.setImageResource(R.drawable.un_check_contact);
itemViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (contact.isSelect()) {
contact.setSelect(false);
itemViewHolder.mIvSelect.setImageResource(R.drawable.un_check_contact);
} else {
contact.setSelect(true);
itemViewHolder.mIvSelect.setImageResource(R.drawable.check_contect);
}
}
});
}
#Override
public int getItemCount() {
return mContectFilter.size();
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView mTvUserNane, mTvUserEmail;
private ImageView mIvSelect;
public ItemViewHolder(View itemView) {
super(itemView);
mTvUserEmail = itemView.findViewById(R.id.icrlTvUserEmail);
mTvUserNane = itemView.findViewById(R.id.icrlTvUserName);
mIvSelect = itemView.findViewById(R.id.icrlIvSelect);
}
}
public List<String> getEmail() {
mEmailList.clear();
for (UserContact contact : mContectFilter) {
if (contact.isSelect()) {
mEmailList.add(contact.getUserEmail());
}
}
return mEmailList;
}
/**
* this class for filter data.
*/
class CustomFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults();
if (charSequence != null && charSequence.length() > 0) {
ArrayList<UserContact> filters = new ArrayList<>();
charSequence = charSequence.toString().toUpperCase();
for (int i = 0; i < mContactList.size(); i++) {
if (mContactList.get(i).getUserName().toUpperCase().contains(charSequence) || mContactList.get(i).getUserEmail().toUpperCase().contains(charSequence)) {
UserContact contact = new UserContact();
contact.setUserName(mContactList.get(i).getUserName());
contact.setUserEmail(mContactList.get(i).getUserEmail());
filters.add(contact);
}
}
results.count = filters.size();
results.values = filters;
} else {
results.count = mContactList.size();
results.values = mContactList;
}
return results;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
mContectFilter = (ArrayList<UserContact>) filterResults.values;
notifyDataSetChanged();
}
}
}
ArrayList<YourModel> arrayList = new ArrayList<>();
arrayList.addAll(yourList);
AutoCompleteAdapter autoCompleteAdapter = new AutoCompleteAdapter(context, arrayList);
autoCompleteTextView.setAdapter(brandAdapter);
autoCompleteTextView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
YourModel model = (YourModel) autoCompleteAdapter.getItem(position);
autoCompleteTextView.setText(model.getText());
autoCompleteTextView.setSelection(model.getText().length());
}
});
AutoCompleteAdapter.java
public class AutoCompleteAdapter extends BaseAdapter implements Filterable {
private Context context;
private ArrayList<YourModel> originalList;
private ArrayList<YourModel> suggestions = new ArrayList<>();
private Filter filter = new CustomFilter();
public AutoCompleteAdapter(Context context, ArrayList<YourModel> originalList) {
this.context = context;
this.originalList = originalList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_filter, parent, false);
}
YourModel model = suggestions.get(position);
AppCompatTextView tvTitle = convertView.findViewById(R.id.tvTitle);
tvTitle.setText(model.getText());
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
#Override
public Object getItem(int position) {
return suggestions.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public int getCount() {
return suggestions.size();
}
#Override
public Filter getFilter() {
return filter;
}
private class CustomFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
suggestions.clear();
if (originalList != null && constraint != null) {
for (int i = 0; i < originalList.size(); i++) {
if (originalList.get(i).getText().toLowerCase().contains(constraint.toString().toLowerCase().trim())) {
suggestions.add(originalList.get(i));
}
}
}
FilterResults results = new FilterResults();
results.values = suggestions;
results.count = suggestions.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
I have created a Search Icon in my Action Bar. I tried to use Strings Array, it worked well.
But, when I tried to get data from a web server. The search is not working well. Though I have tried to fixed it. But, it still doesn't work.
Here is my class :
public class MainActivity extends AppCompatActivity {
private String URL=
"TTTTTTTTTT.php";
private String Contact_NAME[] ;
private JSONParser jsonParser = new JSONParser();
private ListView mListView;
private my_Adapter ContactViewAdapter;
private ArrayList<Contacts_ListView> ContactView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView)findViewById(R.id.listViewCountry);
new Get_Contacts().execute();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_search, menu);
MenuItem item = menu.findItem(R.id.menuSearch);
SearchView searchView = (SearchView)item.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
ContactViewAdapter.getFilter().filter(newText);
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
Here is my AsyncTask. I used it as a private class inside my MainActivity
private class Get_Contacts extends AsyncTask<Void, Void, Boolean>
{
private ProgressDialog mProgressDialog;
private JSONObject jsonObjectResult = null;
private String error;
private String THE_URL ;
public Get_Contacts(){
THE_URL = Contact_URL ;
WITH_INPUT = false ;
} ;
#Override
protected void onPreExecute()
{
super.onPreExecute();
ContactView = new ArrayList<Contacts_ListView>();
}
#Override
protected Boolean doInBackground(Void... params)
{
List<NameValuePair> pairs = new ArrayList<NameValuePair>();
pairs = null;
jsonObjectResult = jsonParser.makeHttpRequest(THE_URL, pairs);
if (jsonObjectResult == null)
{
error = "ERROR";
return false;
}
try
{
if (jsonObjectResult.getInt("success") == 1)
{
JSONArray jsonArray = jsonObjectResult.getJSONArray("posts");
saving_loop = jsonArray.length() ;
JSONObject news ;
for (int i = 0; i < jsonArray.length(); i++)
{
news = jsonArray.getJSONObject(i);
Contacts_ListView listviewcontacts = new Contacts_ListView
(
news.getString("Contact_Name")
);
ContactView.add(listviewcontacts);
}
return true;
}
else
error = jsonObjectResult.getString("message");
}
catch (Exception ex)
{
}
return false;
}
#Override
protected void onPostExecute(Boolean aBoolean)
{
super.onPostExecute(aBoolean);
if (aBoolean)
{
ContactViewAdapter = new Contacts_ListViewAdapter_No_Checkbox(MainActivity.this,
ContactView);
mListView.setAdapter(ContactViewAdapter);
}
else
Toast.makeText(MainActivity.this, error, Toast.LENGTH_LONG).show();
}
}
}
The application works and it doesn't give me any errors. When I try to search. It doesn't search
Here is my Adapter
public class Contacts_ListViewAdapter_No_Checkbox extends BaseAdapter {
private Context mContext;
private List<Contacts_ListView> mData;
private MyFilter filter ;
private ArrayList<Contacts_ListView> originalList ;
public Contacts_ListViewAdapter_No_Checkbox (Context mContext, ArrayList<Contacts_ListView> mData) {
//super(mContext, R.layout.contacts_shape_nocheckbox, mData);
this.mContext = mContext;
this.mData = mData;
this.originalList = new ArrayList<Contacts_ListView>();
this.originalList.addAll(mData);
}
#Override
public int getCount() {
return mData.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public Object getItem(int position) {
return mData.get(position);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
LayoutInflater mInflater = (LayoutInflater)
mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.contacts_shape_nocheckbox, null);
}
TextView Contact_Name = (TextView) convertView.findViewById(R.id.Contact_Name_1);
Contact_Name.setText(mData.get(position).getContactName());
return convertView;
}
public Filter getFilter() {
if (filter == null){
filter = new MyFilter();
}
return filter;
}
private class MyFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
constraint = constraint.toString();
FilterResults result = new FilterResults();
if(constraint != null && constraint.toString().length() > 0)
{
ArrayList<Contacts_ListView> filteredItems = new ArrayList<Contacts_ListView>();
for(int i = 0, l = originalList.size(); i < l; i++)
{
Contacts_ListView nameList = originalList.get(i);
if(nameList.toString().contains(constraint))
filteredItems.add(nameList);
}
result.count = filteredItems.size();
result.values = filteredItems;
}
else
{
synchronized(this)
{
result.values = originalList;
result.count = originalList.size();
}
}
return result;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence contraint, FilterResults results) {
mData = (ArrayList<Contacts_ListView>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
Filter not work because you are using a custom object. If you pass a String or int value to array adapter its know how to filter it. But if you pass custom object default filter implementation have to no idea how to deal with that. use custom adapter instead of arrayadapter
What you need to do
Extends BaseAdapter insted of ArrayAdater
Make custom Filter in Adapter
3.Return filter result
Filter myFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<Contacts_ListView> tempList=new ArrayList<Contacts_ListView>();
//constraint is the result from text you want to filter against.
//objects is your data set you will filter from
if(constraint != null && objects!=null) {
int length=objects.size();
int i=0;
while(i<length){
Contacts_ListView item=objects.get(i);
//do whatever you wanna do here
//adding result set output array
tempList.add(item);
i++;
}
//following two lines is very important
//as publish result can only take FilterResults objects
filterResults.values = tempList;
filterResults.count = tempList.size();
}
return filterResults;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence contraint, FilterResults results) {
objects = (ArrayList<Contacts_ListView>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
Override method
#Override
public Filter getFilter() {
return myFilter;
}
Updated change your code to below code
Contacts_ListView nameList = originalList.get(i);
if(nameList.getContactName().toString().contains(constraint))
filteredItems.add(nameList);
i really need help debugging this android app i am building. I have a custom adapter. when i dont use it with simpleSectionAdapter it displays list items correctly, but when i use SimpleSectionAdapter from adapter kit, it displays blank list. I want to have sections in my list according to names. Thanks in advance.
Heres's my adapter
public class CustomContriAdapter extends BaseAdapter implements Filterable{
List<Contributions> contributions;
LayoutInflater inflater;
Context context;
public List<Contributions> orig;
public CustomContriAdapter(Context context, List<Contributions> contributions) {
this.contributions = contributions;
this.context = context;
inflater = LayoutInflater.from(this.context);
}
#Override
public int getCount() {
return contributions.size();
}
#Override
public Object getItem(int position) {
return contributions.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
final FilterResults oReturn = new FilterResults();
final List<Contributions> results = new ArrayList<Contributions>();
if (orig == null)
orig = contributions;
if (constraint != null) {
if (orig != null && orig.size() > 0) {
for (final Contributions g : orig) {
if (g.getContributor_name().toLowerCase()
.contains(constraint.toString()))
results.add(g);
}
}
oReturn.values = results;
}
return oReturn;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
Filter.FilterResults results) {
contributions = (ArrayList<Contributions>) results.values;
notifyDataSetChanged();
}
};
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyViewHolder mViewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.contri_list_item_layout, parent, false);
mViewHolder = new MyViewHolder(convertView);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
Contributions currentListData = (Contributions) getItem(position);
mViewHolder.tvTitleName.setText(currentListData.getContributor_name());
//date created at
Date createdAt = currentListData.getCreated();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String createdDate = df.format(createdAt);
mViewHolder.tvDescDate.setText(createdDate);
return convertView;
}
private class MyViewHolder {
TextView tvTitleName, tvDescDate;
public MyViewHolder(View item) {
tvTitleName = (TextView) item.findViewById(R.id.name_view);
tvDescDate = (TextView) item.findViewById(R.id.contri_created_date);
}
}
}
here's how am calling it in the onCreate()
Collections.sort(allContributions, new Comparator<Contributions>() {
#Override
public int compare(Contributions lhs, Contributions rhs) {
return lhs.getContributor_name().compareTo(rhs.getContributor_name());
}
});
InstantAdapter<Contributions> contribu = new InstantAdapter<Contributions>(
this,R.layout.contri_list_item_layout, Contributions.class, allContributions);
//wrap adapter to simple section adapter
SimpleSectionAdapter<Contributions> sectionAdapter = new SimpleSectionAdapter<Contributions>(
this, contribu, R.layout.section_header, R.id.section_text , new ContributionsSectionizer());
listView.setAdapter(sectionAdapter);
here's the sectionizer,
public class ContributionsSectionizer implements Sectionizer<Contributions> {
#Override
public String getSectionTitleForItem(Contributions contributions) {
return contributions.getContributor_name();
}
}
and the items
protected List<Contributions> allContributions = new ArrayList<>();
I am extending the Array Adapter as it follows but I get still the old results can you please tell me what is the problem ?
public class Adaptor extends ArrayAdapter<String> implements Filterable{
private ArrayList<String> items;
public Adaptor(Context context, int textViewResourceId, String[] objects) {
super(context, textViewResourceId, objects);
items = new ArrayList<String>();
for (int i = 0; i < objects.length ; i++)
items.add(objects[i]);
}
#Override
public int getCount() {
return items.size();
}
#Override
public String getItem(int position) {
return items.get(position);
}
#Override
public Filter getFilter() {
Filter myFilter = new Filter(){
#Override
protected FilterResults performFiltering(CharSequence arg0) {
FilterResults rezultate = new FilterResults();
ArrayList<String> chestii = new ArrayList<String>();
for (int i = 0; i < items.size() ; i++)
{
String tmp = items.get(i).toUpperCase();
if (tmp.startsWith(arg0.toString().toUpperCase()))
chestii.add(items.get(i));
}
rezultate.count = chestii.size();
rezultate.values = chestii;
return rezultate;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results != null && results.count > 0)
{
notifyDataSetChanged();
}
else notifyDataSetInvalidated();
}
};
return myFilter;
}
}
Your list contains items from "items" and you don't modify items in it, you have to remove positions from "items" and then call notifyDataSetChange, to restore lately all items you have to save previous items
this is works fine:
/**
* Adapter wrapper to represent list of dialogs
* #author Ryazantsev Dmitry
* #email dilix90#gmail.com 2012
*/
public class FriendsAdapter extends ArrayAdapter<User>
{
private final LayoutInflater inflater;
private final ImageLoader il;
private Context parentContext;
private List<User> mData;
private List<User> mOriginalData;
public SimpleImageLoader sil;
#Override
public void add(User object)
{
if (mOriginalData != null)
mOriginalData.add(object);
else
mData.add(object);
}
#Override
public void remove(User object)
{
if (mOriginalData != null)
mOriginalData.remove(object);
else
mData.remove(object);
}
#Override
public int getCount()
{
return mData.size();
}
#Override
public User getItem(int position)
{
return mData.get(position);
}
#Override
public int getPosition(User item)
{
return mData.indexOf(item);
}
public FriendsAdapter(Context context, int textViewResourceId, List<User> objects)
{
super(context, textViewResourceId, objects);
Log.v("refresh", context + " " + textViewResourceId + " " + objects);
parentContext = context;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
il = new ImageLoader(context);
mData = objects;
sil = new SimpleImageLoader(context, true, 64);
sil.setOnUiThread(false);
}
/**
* We have a custom view and need to organize it
*/
#Override
public View getView(final int position, View convertView, final ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
convertView = inflater.inflate(R.layout.friends_list_row, null);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.friendName);
holder.root = convertView.findViewById(R.id.root);
holder.photo = (ImageView) convertView.findViewById(R.id.friendPhoto);
holder.online = (ImageView) convertView.findViewById(R.id.online);
convertView.setTag(holder);
}
else
holder = (ViewHolder) convertView.getTag();
User user = getItem(position);
holder.online.setVisibility(user.isOnline() > 0 ? View.VISIBLE : View.GONE);
if (user != null)
{
holder.name.setText(user.getFIO());
holder.photo.setTag(user.getPhotoUrl());
if (user.getPhotoBitmap() != null)
holder.photo.setImageBitmap(user.getPhotoBitmap());
else
{
holder.photo.setImageResource(R.drawable.contact_nophoto);
sil.displayImageUserAttach(holder.photo, user.getPhotoUrl(), user, null, false, null);
}
}
// parent.setVisibility(position % 2 == 0?View.GONE:View.VISIBLE);
return convertView;
}
private static class ViewHolder
{
public TextView name;
public ImageView photo;
public View root;
public View online;
}
#Override
public Filter getFilter()
{
return new Filter()
{
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
Log.v("filter", "filter finished");
mData = (List<User>) results.values;
if (results.count > 0)
{
notifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
Log.v("filter", "filter perform");
if (mOriginalData == null)
mOriginalData = new ArrayList<User>(mData);
List<User> result;
FilterResults r = new FilterResults();
if (constraint == null || constraint.length() <= 0)
result = new ArrayList<User>(mOriginalData);
else
{
result = new ArrayList<User>();
for (int i = 0; i < mOriginalData.size(); i++)
if (constraint.length() > 0
&& mOriginalData.get(i).getFIO().toLowerCase()
.contains(constraint.toString().toLowerCase()))
result.add(mOriginalData.get(i));
}
r.values = result;
r.count = result.size();
return r;
}
};
}
}
I've the Custom CustomerAdapter
public class CustomerAdapter extends ArrayAdapter<Customer> {
private final String MY_DEBUG_TAG = "CustomerAdapter";
private ArrayList<Customer> items;
private int viewResourceId;
public CustomerAdapter(Context context, int viewResourceId, ArrayList<Customer> items) {
super(context, viewResourceId, items);
this.items = items;
this.viewResourceId = viewResourceId;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(viewResourceId, null);
}
Customer customer = items.get(position);
if (customer != null) {
TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel);
if (customerNameLabel != null) {
customerNameLabel.setText(String.valueOf(customer.getName()));
}
}
return v;
}
}
and customer_auto layout
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/customerNameLabel"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="10dp" android:textSize="16sp"
android:textColor="#000">
</TextView>
and on my public void onCreate
AutoCompleteTextView customerAutoComplete = (AutoCompleteTextView) findViewById(R.id.autocomplete_customer);
CustomerAdapter customerAdapter = new CustomerAdapter(this, R.layout.customer_auto, customerList);
customerAutoComplete.setAdapter(customerAdapter);
and Customer.java
public class Customer implements Parcelable {
private int id;
private String name = "";
public Customer() {
// TODO Auto-generated constructor stub
}
/**
* This will be used only by the MyCreator
*
* #param source
*/
public Customer(Parcel source) {
/*
* Reconstruct from the Parcel
*/
id = source.readInt();
name = source.readString();
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
#Override
public Customer createFromParcel(Parcel source) {
return new Customer(source);
}
#Override
public Customer[] newArray(int size) {
return new Customer[size];
// TODO Auto-generated method stub
}
};
#Override
public String toString() {
return this.name;
}
}
But the auto suggest box does not filter correctly. for eg; if i type an in the test box customers starting with br are showing up!
I have to over-ride the getFilter() method of the Adapter
Here is the code which worked for me, thanks to sacoskun
public class CustomerAdapter extends ArrayAdapter<Customer> {
private final String MY_DEBUG_TAG = "CustomerAdapter";
private ArrayList<Customer> items;
private ArrayList<Customer> itemsAll;
private ArrayList<Customer> suggestions;
private int viewResourceId;
public CustomerAdapter(Context context, int viewResourceId, ArrayList<Customer> items) {
super(context, viewResourceId, items);
this.items = items;
this.itemsAll = (ArrayList<Customer>) items.clone();
this.suggestions = new ArrayList<Customer>();
this.viewResourceId = viewResourceId;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(viewResourceId, null);
}
Customer customer = items.get(position);
if (customer != null) {
TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel);
if (customerNameLabel != null) {
// Log.i(MY_DEBUG_TAG, "getView Customer Name:"+customer.getName());
customerNameLabel.setText(customer.getName());
}
}
return v;
}
#Override
public Filter getFilter() {
return nameFilter;
}
Filter nameFilter = new Filter() {
#Override
public String convertResultToString(Object resultValue) {
String str = ((Customer)(resultValue)).getName();
return str;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if(constraint != null) {
suggestions.clear();
for (Customer customer : itemsAll) {
if(customer.getName().toLowerCase().startsWith(constraint.toString().toLowerCase())){
suggestions.add(customer);
}
}
FilterResults filterResults = new FilterResults();
filterResults.values = suggestions;
filterResults.count = suggestions.size();
return filterResults;
} else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values;
if(results != null && results.count > 0) {
clear();
for (Customer c : filteredList) {
add(c);
}
notifyDataSetChanged();
}
}
};
}
This is my solution. I feel like it's a bit cleaner (doesn't use 3 separate, confusing ArrayLists) than the accepted one, and has more options. It should work even if the user types backspace, because it doesn't remove the original entries from mCustomers (unlike the accepted answer):
public class CustomerAdapter extends ArrayAdapter<Customer> {
private LayoutInflater layoutInflater;
List<Customer> mCustomers;
private Filter mFilter = new Filter() {
#Override
public String convertResultToString(Object resultValue) {
return ((Customer)resultValue).getName();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null) {
ArrayList<Customer> suggestions = new ArrayList<Customer>();
for (Customer customer : mCustomers) {
// Note: change the "contains" to "startsWith" if you only want starting matches
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) {
// we have filtered results
addAll((ArrayList<Customer>) results.values);
} else {
// no filter, add entire original list back in
addAll(mCustomers);
}
notifyDataSetChanged();
}
};
public CustomerAdapter(Context context, int textViewResourceId, List<Customer> customers) {
super(context, textViewResourceId, customers);
// copy all the customers into a master list
mCustomers = new ArrayList<Customer>(customers.size());
mCustomers.addAll(customers);
layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = layoutInflater.inflate(R.layout.customerNameLabel, null);
}
Customer customer = getItem(position);
TextView name = (TextView) view.findViewById(R.id.customerNameLabel);
name.setText(customer.getName());
return view;
}
#Override
public Filter getFilter() {
return mFilter;
}
}
Instead of overriding getFilter() method in adapter, simply we can override the toString() of the userDefined object (Customer).
In toString() just return the field based on what you need to filter. It worked for me.
In my example I'm filtering based on names:
public class Customer{
private int id;
private String name;
#Override
public String toString() {
return this.name;
}
}
In the above code publisHResults() method gives the concurrent modification exception....
we have to modify the code as:
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values;
ArrayList<Customer> customerList=new ArrayList<Customer>();
if (results != null && results.count > 0) {
clear();
for (Customer c : filteredList) {
customerList.add(c);
}
Iterator<Customer> customerIterator=getResult.iterator();
while (customerIterator.hasNext()) {
Customer customerIterator=customerIterator.next();
add(customerIterator);
}
notifyDataSetChanged();
}
}
Maybe this is too late, you dont need to override all of these functions , the only function to override is :
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(viewResourceId, null);
}
Customer customer = getItem(position);
if (customer != null) {
TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel);
if (customerNameLabel != null) {
customerNameLabel.setText(String.valueOf(customer.getName()));
}
}
return v;
}
consider I change :
Customer customer = items.get(position);
Customer customer = getItem(position);
pay attention, you should not declare new ListItems,
private ArrayList<Customer> items;
because ArrayAdapter works with its own mObjects, and filter this list not your items list,
So you should use getItem function to access items.
then there is no reason to write your ArrayFilter.
I don't know where you retrieving the getResult. I think the solution in this case for don't have the concurrent modification is:
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values;
ArrayList<Customer> customerList=new ArrayList<Customer>();
if (results != null && results.count > 0) {
clear();
try{
for (Customer c : filteredList) {
customerList.add(c);
}
}catch(Exception e){
Log.e("PEEEETAAAAAAAA", "AutoCompletaError: "+e.getMessage()+" "+e.getCause()+" "+e.getLocalizedMessage());
}
Iterator<Customer> customerIterator=customerList.iterator();
while (customerIterator.hasNext()) {
Customer customerIterator=customerIterator.next();
add(customerIterator);
}
notifyDataSetChanged();
}
}
I hope that this post will help people with implementation of a similar custom functionality in the future. I based this on my version of adapter used for displaying tag suggestions in my microblogging app:
public class TagSuggestionsAdapter extends ArrayAdapter<String> implements Filterable
Extending ArrayAdapter to have less boilerplate code. Implementing Filterable to change filter behavior later.
private List<String> allTags;
private List<String> tagSuggestions;
private Context context;
public TagSuggestionsAdapter(List<String> initialTagSuggestions, List<String> allTags,
Context context) {
super(context, R.layout.item_tag_suggestion, initialTagSuggestions);
this.tagSuggestions = initialTagSuggestions;
this.allTags = allTags;
this.context = context;
}
Basically in constructor you need to pass a list that will be displayed initially - it'll later become a list with filtered results (this is also a reference to a list that will be taken into consideration when calling notifyDataSetChanged()) and obviously a list on which you can base your filtering (allTags in my case). I'm also passing Context for layout inflation in getView().
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(context)
.inflate(R.layout.item_tag_suggestion, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.tagSuggestionTextView.setText(tagSuggestions.get(position));
return convertView;
}
static class ViewHolder {
#BindView(R.id.tag_suggestion_text_view)
TextView tagSuggestionTextView;
ViewHolder(View itemView) {
ButterKnife.bind(this, itemView);
}
}
Above you can see a simple view holder pattern with a little help from Butterknife to inflate a custom row layout.
#NonNull
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
List<String> filteredTags = filterTagSuggestions(constraint.toString(), allTags);
FilterResults filterResults = new FilterResults();
filterResults.values = filteredTags;
filterResults.count = filteredTags.size();
return filterResults;
} else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
tagSuggestions.clear();
if (results != null && results.count > 0) {
List<?> filteredTags = (List<?>) results.values;
for (Object filteredTag : filteredTags) {
if (filteredTag instanceof String) {
tagSuggestions.add((String) filteredTag);
}
}
}
notifyDataSetChanged();
}
};
}
This is the least boilerplate code I could write. Your only concern is method filterTagSuggestions that should return a filtered list of tags based on input from user (CharSequence constraint). Hope that summarized and organized necessary info a little bit.
If you get ConcurrentModificationException exception.
Replace ArrayList with the thread safe CopyOnWriteArrayList.
Here you can find detatils answer
I have non-update and modify orginal list issues from above answer. I fixed this problem with this codes.
public class AdapterAutoCompleteTextView extends ArrayAdapter<ItemWord> {
private int LayoutID;
private int TextViewID;
private LayoutInflater Inflater;
private List<ItemWord> ObjectsList;
public AdapterAutoCompleteTextView(Context ActivityContext, int ResourceID, int TextViewResourceID, List<ItemWord> WordList) {
super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList<ItemWord>());
LayoutID = ResourceID;
TextViewID = TextViewResourceID;
ObjectsList = WordList;
Inflater = LayoutInflater.from(ActivityContext);
}
#Override
public View getView(int Position, View ConvertView, ViewGroup Parent) {
ItemWord Word = getItem(Position);
if(ConvertView == null) {
ConvertView = Inflater.inflate(LayoutID, null);
ResultHolder Holder = new ResultHolder();
Holder.ResultLabel= (TextView) ConvertView.findViewById(TextViewID);
ConvertView.setTag(Holder);
}
ResultHolder Holder = (ResultHolder) ConvertView.getTag();
Holder.ResultLabel.setText(Word.getSpelling());
return ConvertView;
}
#Override
public Filter getFilter() {
return CustomFilter;
}
private Filter CustomFilter = new Filter() {
#Override
public CharSequence convertResultToString(Object ResultValue) {
return ((ItemWord) ResultValue).getSpelling();
}
#Override
protected FilterResults performFiltering(CharSequence Constraint) {
FilterResults ResultsFilter = new FilterResults();
ArrayList<ItemWord> OriginalValues = new ArrayList<ItemWord>(ObjectsList);
if(Constraint == null || Constraint.length() == 0){
ResultsFilter.values = OriginalValues;
ResultsFilter.count = OriginalValues.size();
} else {
String PrefixString = Constraint.toString().toLowerCase();
final ArrayList<ItemWord> NewValues = new ArrayList<ItemWord>();
for(ItemWord Word : OriginalValues){
String ValueText = Word.getSpelling().toLowerCase();
if(ValueText.startsWith(PrefixString))
NewValues.add(Word);
}
ResultsFilter.values = NewValues;
ResultsFilter.count = NewValues.size();
}
return ResultsFilter;
}
#Override
protected void publishResults(CharSequence Constraint, FilterResults Results) {
clear();
if(Results.count > 0)
addAll(((ArrayList<ItemWord>) Results.values));
else
notifyDataSetInvalidated();
}
};
private static class ResultHolder {
TextView ResultLabel;
}
}
This is most important line for non-update and modify orginal list issue:
super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList<ItemWord>());
Particularly those
super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList());
I hope this solution will be help you :)