I've have a Vector like this in my app:
Vector<Firm> firmVector= new Vector<Firm>();
As you may see, this is a vector of objects from my own class Firm
So to my question, is it possible to add a AutoComplete to this `Vector?
Like this one, from developer.android.com:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AutoCompleteTextView textView = (AutoCompleteTextView)findViewById(R.id.autocomplete_country);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, COUNTRIES);
textView.setAdapter(adapter);
}
Yes, you can do it by implementing an ArrayAdapter, but you'll need to implement the getFilter() which is used by the AutoCompleteTextView. Something similar to this might work:
private class FirmAdapter extends ArrayAdapter<Firm> {
private Filter filter;
#Override
public Filter getFilter()
{
if(filter == null){
filter = new Filter(){
int lastlen = 0;
boolean initialized = false;
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
filteredItems = (List<Firm>)results.values;
ArrayAdapter.this.notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if(constraint == null || constraint.length() == 0){
results.values = null;
}else{
String val;
Firm obj;
constraint = constraint.toString().toUpperCase();
int newlen = constraint.length();
LinkedList<Firm> filteredResults = new LinkedList<Firm>();
if(newlen < lastlen){
int i = 0, size = ArrayAdapter.super.getCount();
for(i=0;i<size;i++){
obj = ArrayAdapter.super.getItem(i);
val = obj.toString(); // CUSTOMIZE THIS
if(val.contains(constraint)) filteredResults.add(obj);
}
}else{
int i = 0, size = getCount();
for(i=0;i<size;i++){
obj = getItem(i);
val = obj.toString(); // CUSTOMIZE THIS
if(val.contains(constraint)) filteredResults.add(obj);
}
}
lastlen = newlen;
results.values = filteredResults;
}
return results;
}
};
}
return filter;
}
}
}
I ripped this out of a project I have, so it will need some testing and cleanup to get working (in my case I have a Filter on a set of JSONObject instances, where you have a Firm) but try something like that. The comments with CUSTOMIZE THIS are where you want to actually perform the test based on the input into the AutoCompleteTextView.
Related
Image 1 : before any search performed
Image 2 : after search performed
public class classBidAdapter extends ArrayAdapter<classBidAttributes>
implements Filterable {
ArrayList<classBidAttributes> data;
Context context;
CustemFilrer filter;
//------this is a snippit of my listview custom adapter related to the search/filter function----------------------------------------------------------
#NonNull
#Override
public Filter getFilter() {
if(filter == null){
filter = new CustemFilrer();
}
return filter;
}
class CustemFilrer extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint != null && constraint.length()>0){
constraint = constraint.toString();
ArrayList<classBidAttributes> filter = new ArrayList<classBidAttributes>();
for(int i =0; i<data.size();i++){
if(data.get(i).getTitle().contains(constraint)){
classBidAttributes filts = new classBidAttributes(data.get(i).getTitle());
filter.add(filts);
Toast.makeText(Context, "111"+filts, Toast.LENGTH_SHORT).show();
}
}
results.count = filter.size();
results.values=filter;
Toast.makeText(Context, "222"+filter.size() +" / "+filter, Toast.LENGTH_SHORT).show();
}else{
results.count = data.size();
results.values=data;
Toast.makeText(Context, ""+data.size() +" / "+data, Toast.LENGTH_SHORT).show();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data.clear();
data.addAll((ArrayList<classBidAttributes>) results.values);
notifyDataSetChanged();
}
}
I'm guessing the problem is in "data.clear() , notifyDataSetChanged()" but im not sure whats wrong in here.
I've added images explaining what is happening when the search is performed.
hopfully this is every thing needed in this problem if u need to check any thing ask me.
Your guess is right. you are making mistake in data.clear(). By doing this you are clearing complete data of your adapter and adding the last search result. So next time you can't get original data to start filter again.
You should keep original data in another list to update it with your adapter data later.
Your code should be like this:
public class classBidAdapter extends ArrayAdapter<classBidAttributes>
implements Filterable {
ArrayList<classBidAttributes> data;
ArrayList<classBidAttributes> original= new ArrayList<>();
Context context;
CustemFilrer filter;
public classBidAdapter(Context context, ArrayList<classBidAttributes> data){
this.context = context;
this.data = data;
original.addAll(data);
}
}
//------this is a snippit of my listview custom adapter related to the search/filter function----------------------------------------------------------
#NonNull
#Override
public Filter getFilter() {
if(filter == null){
filter = new CustemFilrer();
}
return filter;
}
class CustemFilrer extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint != null && constraint.length()>0){
constraint = constraint.toString();
ArrayList<classBidAttributes> filter = new ArrayList<classBidAttributes>();
for(int i =0; i<data.size();i++) {
if (data.get(i).getTitle().contains(constraint)) {
classBidAttributes filts = new classBidAttributes(data.get(i).getTitle());
filter.add(filts);
Toast.makeText(Context, "111" + filts, Toast.LENGTH_SHORT).show();
}
}
results.count = filter.size();
results.values=filter;
Toast.makeText(Context, "222"+filter.size() +" / "+filter, Toast.LENGTH_SHORT).show();
}else{
results.count = original.size();
results.values=original;
Toast.makeText(Context, ""+original.size() +" / "+original, Toast.LENGTH_SHORT).show();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data.clear();
data.addAll((ArrayList<classBidAttributes>) results.values);
notifyDataSetChanged();
}
}
Hope it helps:)
I have finally managed to make it work correctly with no bugs what so ever and it turned out to be the loop was the source of the problem, its best to avoid the normal for loop and instead use the foreach loop, i had to rewirte the whole code from scratch for the search adapter but as follows:
public void filter(CharSequence constraint) {
constraint = constraint.toString();
data.clear();
if(constraint.length() == 0){
data.addAll(original);
}else{
for(classBidAttributes filts : original){
if(filts.getTitle().contains(constraint)
|| filts.getbidid().contains(constraint)
|| filts.getbid_offer().contains(constraint)
|| filts.getCategory().contains(constraint)
|| filts.getDescription().contains(constraint)
|| filts.getQuantity().contains(constraint)
|| filts.getRepetitive_requests_yes().contains(constraint)
|| filts.getRepetitive_requests_no().contains(constraint)
|| filts.getExpiry_date().contains(constraint)){
data.add(filts);
}
}
}
notifyDataSetChanged();
};
nevertheless this snippet is based on your answer :)
My problem is the following: i want to implement a search mechanism into my JSON-data generated ListView, but i don't know what to do in my case.
I've found a lot of examples but i want to implement the simplest solution possible and, especially, an understandable one.
Here's my Pastebin with the Adapter and the MainActivity class.
http://pastebin.com/SSXHXK7m
Can you give me any suggestion? I'm stuck.
The search functionality would be something like this
public void searchList(String query){
List<Records> matchedActors = new ArrayList<Records>();
for(int i = 0; i < adapter.getCount(); i++){
if(adapter.getItem(i).getAuthor().equals(query)
matchedActors.add(adapter.getItem(i));
}
// See below for what to put here
}
You can then either modify the adapter by creating/calling a method in your adapter class that is something like
public void modifyList(List<Records> actorList){
this.actorList = actorList;
notifyDataSetChanged();
}
and call adapter.modifyList(matchedActors) after your search, or by instantiating a new instance of your adapter after your serach via,
adapter = new RecordsAdapter(MainActivity.this, R.layout."your_layout_resource", actorList);
listview.setAdapter(adapter);
EDIT
I didn't notice you were using a Filterable ArrayAdapter :P You can implement the following funcitionality in your adapter.
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
actorList = (List<Records>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Records> matchedActors = new ArrayList<Records>();
//NOTE mOriginalValues will be a class variable we use to keep track of our values
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Records>(actorList);
}
if (constraint == null || constraint.length() == 0) {
results.count = mOriginalValues.size();
results.values = mOriginalValues;
}
else {
constraint = constraint.toString().toLowerCase();
List<Records> matchedActors = new ArrayList<Records>();
for(int i = 0; i < getCount(); i++){
if(mOriginalValues.getAuthor().toLowerCase().startsWith(constraint)
matchedActors.add(getItem(i);
}
// set the Filtered result to return
results.count = matchedActors.size();
results.values = matchedActors;
}
return results;
}
return filter;
}
I have a custom ArrayAdapter like in this code:
public class UtenteAdapter extends ArrayAdapter<Utente> implements Filterable {
private Context context;
private ArrayList<Utente> utenti;
private ArrayList<Utente> utentiFiltrati;
private FiltroPersonalizzato filtro;
public UtenteAdapter(Context context, ArrayList<Utente> utenti) {
super(context, R.layout.riga_utente, utenti);
this.context=context;
this.utenti=utenti;
utentiFiltrati = new ArrayList<Utente>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//omitted..
return riga;
}
#Override
public void notifyDataSetChanged(){
super.notifyDataSetChanged();
}
#Override
public Filter getFilter() {
if (filtro == null) {
filtro = new FiltroPersonalizzato();
}
return filtro;
}
private class FiltroPersonalizzato extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults risultato = new FilterResults();
ArrayList<Utente> i = new ArrayList<Utente>();
if (prefix!= null && prefix.toString().length() > 0) {
// use the initial values !!!
for (int index = 0; index < utenti.size(); index++) {
Utente si = utenti.get(index);
final int length = prefix.length();
// if you compare the Strings like you did it will never work as you compare the full item string(you'll have a match only when you write the EXACT word)
// keep in mind that you take in consideration capital letters!
if(si.getNome().toLowerCase().substring(0, length).compareTo(prefix.toString().toLowerCase()) == 0){
i.add(si);
}
}
risultato.values = i;
risultato.count = i.size();
}
else{
// revert to the old values
synchronized (utentiFiltrati){
risultato.values = utenti;
risultato.count = utenti.size();
}
}
return risultato;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
utentiFiltrati = (ArrayList<Utente>)results.values;
notifyDataSetChanged();
}
}
}
I have a SearchView and I want to filter the rows by name but I have no results after filtering. I am sure that I have results(from debugging) but if I call filter from my ListFragment I can't see my result:
#Override
public boolean onQueryTextSubmit(String query) {
adapter.getFilter().filter(query);
return true;
}
I update my ArrayAdapter from the ListFragment like this:
private void setListaUtenti(){
if(getListAdapter()==null){
// creo l'adapter
adapter=new UtenteAdapter(
getActivity(),
utenti);
setListAdapter(adapter);
} else{
adapter.notifyDataSetChanged();
}
}
Why don't I see the results of the filtering operation?
i have a searchview and i want filter row by name but i have no
result.
It's normal that you don't get a result as in the publishResults() callback of the Filter you assign the results to the utentiFiltrati list and call notifyDataSetChanged(). This will do nothing as your adapter is based on the utenti list, the one you pass to the super class constructor. Make the following changes:
public UtenteAdapter(Context context, ArrayList<Utente> utentiValues) {
super(context, R.layout.riga_utente, utentiValues);
this.context=context;
this.utentiFiltrati = utentiValues;
utenti = new ArrayList<Utente>(utentiValues);
}
// ...
FilterResults risultato = new FilterResults();
ArrayList<Utente> i;
if (prefix == null || prefix.toString().length() == 0) {
// the contract of a Filter says that you must return all values if the
// challenge string is null or 0 length
i = new ArrayList<Utente>(utenti);
} else {
i = new ArrayList<Utente>();
// use the list that contains the full set of data
for (int index = 0; index < utenti.size(); index++) {
Utente si = utenti.get(index);
final int length = prefix.length();
if(si.getNome().toLowerCase().substring(0, length).compareTo(prefix.toString().toLowerCase()) == 0){
i.add(si);
}
}
//...
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
utentiFiltrati = (ArrayList<Utente>)results.values;
notifyDataSetChanged();
}
I have created a list view in android and I want to add edit text above the list and when the user enter text the list will be filtered according to user input
can anyone tell me please if there is a way to filter the list adapter in android ?
Add an EditText on top of your listview in its .xml layout file.
And in your activity/fragment..
lv = (ListView) findViewById(R.id.list_view);
inputSearch = (EditText) findViewById(R.id.inputSearch);
// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name, products);
lv.setAdapter(adapter);
inputSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
MainActivity.this.adapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { }
#Override
public void afterTextChanged(Editable arg0) {}
});
The basic here is to add an OnTextChangeListener to your edit text and inside its callback method apply filter to your listview's adapter.
EDIT
To get filter to your custom BaseAdapter you"ll need to implement Filterable interface.
class CustomAdapter extends BaseAdapter implements Filterable {
public View getView(){
...
}
public Integer getCount()
{
...
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrayListNames = (List<String>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<String> FilteredArrayNames = new ArrayList<String>();
// perform your search here using the searchConstraint String.
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mDatabaseOfNames.size(); i++) {
String dataNames = mDatabaseOfNames.get(i);
if (dataNames.toLowerCase().startsWith(constraint.toString())) {
FilteredArrayNames.add(dataNames);
}
}
results.count = FilteredArrayNames.size();
results.values = FilteredArrayNames;
Log.e("VALUES", results.values.toString());
return results;
}
};
return filter;
}
}
Inside performFiltering() you need to do actual comparison of the search query to values in your database. It will pass its result to publishResults() method.
Implement your adapter Filterable:
public class vJournalAdapter extends ArrayAdapter<JournalModel> implements Filterable{
private ArrayList<JournalModel> items;
private Context mContext;
....
then create your Filter class:
private class JournalFilter extends Filter{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults result = new FilterResults();
List<JournalModel> allJournals = getAllJournals();
if(constraint == null || constraint.length() == 0){
result.values = allJournals;
result.count = allJournals.size();
}else{
ArrayList<JournalModel> filteredList = new ArrayList<JournalModel>();
for(JournalModel j: allJournals){
if(j.source.title.contains(constraint))
filteredList.add(j);
}
result.values = filteredList;
result.count = filteredList.size();
}
return result;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
items = (ArrayList<JournalModel>) results.values;
notifyDataSetChanged();
}
}
}
this way, your adapter is Filterable, you can pass filter item to adapter's filter and do the work.
I hope this will be helpful.
In case anyone are still interested in this subject, I find that the best approach for filtering lists is to create a generic Filter class and use it with some base reflection/generics techniques contained in the Java old school SDK package. Here's what I did:
public class GenericListFilter<T> extends Filter {
/**
* Copycat constructor
* #param list the original list to be used
*/
public GenericListFilter (List<T> list, String reflectMethodName, ArrayAdapter<T> adapter) {
super ();
mInternalList = new ArrayList<>(list);
mAdapterUsed = adapter;
try {
ParameterizedType stringListType = (ParameterizedType)
getClass().getField("mInternalList").getGenericType();
mCompairMethod =
stringListType.getActualTypeArguments()[0].getClass().getMethod(reflectMethodName);
}
catch (Exception ex) {
Log.w("GenericListFilter", ex.getMessage(), ex);
try {
if (mInternalList.size() > 0) {
T type = mInternalList.get(0);
mCompairMethod = type.getClass().getMethod(reflectMethodName);
}
}
catch (Exception e) {
Log.e("GenericListFilter", e.getMessage(), e);
}
}
}
/**
* Let's filter the data with the given constraint
* #param constraint
* #return
*/
#Override protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<T> filteredContents = new ArrayList<>();
if ( constraint.length() > 0 ) {
try {
for (T obj : mInternalList) {
String result = (String) mCompairMethod.invoke(obj);
if (result.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
filteredContents.add(obj);
}
}
}
catch (Exception ex) {
Log.e("GenericListFilter", ex.getMessage(), ex);
}
}
else {
filteredContents.addAll(mInternalList);
}
results.values = filteredContents;
results.count = filteredContents.size();
return results;
}
/**
* Publish the filtering adapter list
* #param constraint
* #param results
*/
#Override protected void publishResults(CharSequence constraint, FilterResults results) {
mAdapterUsed.clear();
mAdapterUsed.addAll((List<T>) results.values);
if ( results.count == 0 ) {
mAdapterUsed.notifyDataSetInvalidated();
}
else {
mAdapterUsed.notifyDataSetChanged();
}
}
// class properties
private ArrayAdapter<T> mAdapterUsed;
private List<T> mInternalList;
private Method mCompairMethod;
}
And afterwards, the only thing you need to do is to create the filter as a member class (possibly within the View's "onCreate") passing your adapter reference, your list, and the method to be called for filtering:
this.mFilter = new GenericFilter<MyObjectBean> (list, "getName", adapter);
The only thing missing now, is to override the "getFilter" method in the adapter class:
#Override public Filter getFilter () {
return MyViewClass.this.mFilter;
}
All done! You should successfully filter your list - Of course, you should also implement your filter algorithm the best way that describes your need, the code bellow is just an example.. Hope it helped, take care.
All I want to do is displaying a CheckBox at each result of the AutoCompleteTextView's results (which are strings).
I wrote an custom Array Adapter which implements Filterable. I added a simple Filter wich gets called (I checked that) and returns the expected results. However the displayed results are completely different ones.
Here is my Filter-Code:
private class MyFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if ((constraint == null) || (constraint.length() == 0))
{
synchronized (mLock)
{
ArrayList<String> list = new ArrayList<String>();
results.values = list;
results.count = list.size();
}
}
else
{
String constr = constraint.toString().toLowerCase();
final ArrayList<String> newItems = new ArrayList<String>();
for (String temp : items)
{
if (temp.toLowerCase().startsWith((constr)))
{
newItems.add(temp);
}
}
results.values = newItems;
results.count = newItems.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results)
{
if (results.count > 0)
{
notifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
}
Do I miss something?
Thank you!
I forgot to set my results as the new items of the adapter.