Logan
Logan

Reputation: 2505

AutoCompleteTextView not working as expected

I am working on an application in which I am using AutoCompleteTextView and am facing few issues. Please find the details of the issues below.

The following values are present in the data:

1) Manish Logan Jain

2) M. J. (Logan Fern)

3) Logan

Issues:

1) When user searches for Manish, Manish Logan Jain is shown as a suggestion. But when user enters Logan Jain, then no results are returned.

2) When user enters Logan, I expect second value to be shown as suggestion, but currently, the suggestion lists shows nothing.

3) When user enters ogan, I expect suggestion 3 to be shown. Currently, its not shown.

AutoCompleteView xml:

AutoCompleteTextView

    android:id="@+id/autoCompleteTextView1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:ems="10"
    android:hint="@string/enter_user_name" >

    <requestFocus />
</AutoCompleteTextView>

Java code that populates the data:

    List<String> namesList = new ArrayList<String>(stops);
    namesList.add("Manish Logan Jain");
    namesList.add("Logan");
    namesList.add("M. J. (Logan Fern)");

    ArrayAdapter<String> namesSuggestion = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, namesList);
    AutoCompleteTextView textView = (AutoCompleteTextView)                       findViewById(R.id.autoCompleteTextView1);
     textView.setAdapter(namesSuggestion);
     textView.setThreshold(1);

Has someone faced similar issues? And if yes, then what are the possible solutions for this?

Upvotes: 0

Views: 564

Answers (5)

pskink
pskink

Reputation: 24720

use a CursorAdapter for your ACTV and call setFilterQueryProvider(FilterQueryProvider) for custom filtering (use a MatrixCursor for filtered data)

EDIT: sample FilterQueryProvider

class FQP extends LinkedList<String> implements FilterQueryProvider {
    @Override
    public Cursor runQuery(CharSequence constraint) {
        if (constraint == null) {
            return null;
        }
        Log.d("TAG", "runQuery " + constraint);

        String lowerConstraint = constraint.toString().toLowerCase();
        String[] columns = {
                "_id", "name"
        };
        int id = 0;
        MatrixCursor c = new MatrixCursor(columns);
        for (String name : this) {
            String lowerName = name.toLowerCase();
            if (lowerName.indexOf(lowerConstraint) != -1) {
                c.newRow().add(id++).add(name);
            }
        }
        return c;
    }
};

test it with the following in onCreate:

LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
AutoCompleteTextView actv = new AutoCompleteTextView(this);
String[] from = {"name"};
int[] to = {android.R.id.text1};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_dropdown_item_1line, null, from, to);
FQP fqp = new FQP();
fqp.add("Manish Logan Jain");
fqp.add("Logan");
fqp.add("M. J. (Logan Fern)");
adapter.setFilterQueryProvider(fqp);
actv.setAdapter(adapter);
actv.setThreshold(1);
ll.addView(actv);
setContentView(ll);

Upvotes: 2

user3189178
user3189178

Reputation: 1

The default result string set is returned by treat the imput string as prefix. This is implemented in a ArrayFilter object. Code is like

 /**
 * <p>An array filter constrains the content of the array adapter with
 * a prefix. Each item that does not start with the supplied prefix
 * is removed from the list.</p>
 */
private class ArrayFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence prefix) {
        FilterResults results = new FilterResults();

        if (mOriginalValues == null) {
            synchronized (mLock) {
                mOriginalValues = new ArrayList<T>(mObjects);
            }
        }

        if (prefix == null || prefix.length() == 0) {
            synchronized (mLock) {
                ArrayList<T> list = new ArrayList<T>(mOriginalValues);
                results.values = list;
                results.count = list.size();
            }
        } else {
            String prefixString = prefix.toString().toLowerCase();

            final ArrayList<T> values = mOriginalValues;
            final int count = values.size();

            final ArrayList<T> newValues = new ArrayList<T>(count);

            for (int i = 0; i < count; i++) {
                final T value = values.get(i);
                final String valueText = value.toString().toLowerCase();

                // First match against the whole, non-splitted value
                if (valueText.startsWith(prefixString)) {
                    newValues.add(value);
                } else {
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;

                    for (int k = 0; k < wordCount; k++) {
                        if (words[k].startsWith(prefixString)) {
                            newValues.add(value);
                            break;
                        }
                    }
                }
            }

            results.values = newValues;
            results.count = newValues.size();
        }

        return results;
    }

if you want change the return string to the set that contains the input string, A customed ArrayFilter is needed.

Upvotes: 0

GrIsHu
GrIsHu

Reputation: 23638

Try out the below code:

public class AutoCompleteAdapter extends ArrayAdapter<Address> implements Filterable {

    private LayoutInflater mInflater;
    private Geocoder mGeocoder;
    private StringBuilder mSb = new StringBuilder();

    public AutoCompleteAdapter(final Context context) {
        super(context, -1);
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mGeocoder = new Geocoder(context);
    }

    @Override
    public View getView(final int position, final View convertView, final ViewGroup parent) {
        final TextView tv;
        if (convertView != null) {
            tv = (TextView) convertView;
        } else {
            tv = (TextView) mInflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
        }

        tv.setText((createFormattedAddressFromAddress(getItem(position))));
        return tv;
    }

    private String createFormattedAddressFromAddress(final Address address) {
        mSb.setLength(0);
        final int addressLineSize = address.getMaxAddressLineIndex();
        for (int i = 0; i < addressLineSize; i++) {
            mSb.append(address.getAddressLine(i));
            if (i != addressLineSize - 1) {
                mSb.append(", ");
            }
        }
        return mSb.toString();
    }

    @Override
    public Filter getFilter() {
        Filter myFilter = new Filter() {
            @Override
            protected FilterResults performFiltering(final CharSequence constraint) {
                List<Address> addressList = null;
                if (constraint != null) {
                    try {
                        addressList = mGeocoder.getFromLocationName((String) constraint, 5,23.0,72.0,23.9,72.9);
                    } catch (IOException e) {
                    }
                }
                if (addressList == null) {
                    addressList = new ArrayList<Address>();
                }

                final FilterResults filterResults = new FilterResults();
                filterResults.values = addressList;
                filterResults.count = addressList.size();

                return filterResults;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(final CharSequence contraint, final FilterResults results) {
                clear();
                for (Address address : (List<Address>) results.values) {
                    add(address);
                }
                if (results.count > 0) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }

            @Override
            public CharSequence convertResultToString(final Object resultValue) {
                return resultValue == null ? "" : ((createFormattedAddressFromAddress((Address) resultValue).split(", Ahmedabad")[0].length()<6)?createFormattedAddressFromAddress((Address) resultValue).split(", Gujarat")[0]:createFormattedAddressFromAddress((Address) resultValue).split(", Ahmedabad")[0]);
            }
        };
        return myFilter;
    }
}

Upvotes: 0

chiru
chiru

Reputation: 645

What your looking for Is cannot be done directly. But i suggest you to use MultiCompleteTextView.

refer this link: http://www.c-sharpcorner.com/uploadfile/manish1231/autocomplete-and-multicomplete-textview-in-mono-for-android/

Upvotes: 0

Chirag
Chirag

Reputation: 56925

Use a custom Adapter which implements Filterable. In the getFilter method use String.contains() as per your requirement.

Please check this link.

Upvotes: 2

Related Questions