Question

In Xamarin, I am having trouble with a custom adapter for a AutoCompleteTextView.

Here is my code:

private void SetupAutoCompleteTextViewWithCustomAdapter()
{
    List<CustomItem> customItems = new List<CustomItem>();

    CustomItem customItem = new CustomItem();
    customItem.Heading = "TestHeading";
    customItem.SubHeading = "TestSubHeading";
    customItems.Add(customItem);

    customItem = new CustomItem();
    customItem.Heading = "TestHeading 2";
    customItem.SubHeading = "TestSubHeading 2";
    customItems.Add(customItem);

    customItem = new CustomItem();
    customItem.Heading = "TestHeading 3";
    customItem.SubHeading = "TestSubHeading 3";
    customItems.Add(customItem);

    AutoCompleteTextView AutoCompleteTextViewWithCustomAdapter = (AutoCompleteTextView) FindViewById(Resource.Id.AutoCompleteTextView);
    AutoCompleteTextViewWithCustomAdapter.Adapter = new CustomAdapter (this, customItems);

}

Here is my custom adapter:

public class CustomAdapter : BaseAdapter<CustomItem> {
    List<CustomItem> items;
    Activity context;
    public CustomAdapter(Activity context, List<CustomItem> items)
        : base()
    {
        this.context = context;
        this.items = items;
    }
    public override long GetItemId(int position)
    {
        return position;
    }
    public override CustomItem this[int position]
    {
        get { return items[position]; }
    }
    public override int Count
    {
        get { return items.Count; }
    }
    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        var item = items[position];

        View view = convertView;
        if (view == null) // no view to re-use, create new
            view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
        view.FindViewById<TextView>(Resource.Id.TextViewMapHeading).Text = item.Heading;
        view.FindViewById<TextView> (Resource.Id.TextViewMapHeading).TextSize = 20;
        view.FindViewById<TextView>(Resource.Id.TextViewItem).Text = item.SubHeading;
        view.FindViewById<ImageView>(Resource.Id.Image).SetImageResource(item.ImageResourceId);

        return view;
    }
}

Here is my custom item class:

public class CustomItem
{
    public string Heading { get; set; }
    public string SubHeading { get; set; }
    public int ImageResourceId { get; set; }
}

Here is my CustomView layout file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="8dp">
    <LinearLayout
        android:id="@+id/Text"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip">
        <TextView
            android:id="@+id/TextViewMapHeading"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="28sp" />
        <TextView
            android:id="@+id/TextViewItem"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:autoLink="all" />
    </LinearLayout>
    <ImageView
        android:id="@+id/Image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:padding="5dp"
        android:src="@drawable/icon"
        android:visibility="invisible"
        android:layout_alignParentRight="true" />
</RelativeLayout>

Here is the error that I am getting:

java.lang.ClassCastException: simplemapdemo.CustomAdapter cannot be cast to android.widget.Filterable

Can I please have some help to get this working?

Thanks in advance

Was it helpful?

Solution

Because the Adapter used for AutoCompleteTextView must be implements Filterable interface, so modify your CustomAdapter like following:

public class CustomAdapter extends BaseAdapter<CustomItem> implements Filterable {

    ArrayFilter mFilter;
    ...

    // please override other method in BaseAdatper as your need
    ...

    @Override
    public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new ArrayFilter();
        }
        return mFilter;
    }

    // you can modify the custom Filter used for AutoCompleteTextView as your need
    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) {
                ArrayList<T> list;
                synchronized (mLock) {
                    list = new ArrayList<T>(mOriginalValues);
                }
                results.values = list;
                results.count = list.size();
            } else {
                String prefixString = prefix.toString().toLowerCase();

                ArrayList<T> values;
                synchronized (mLock) {
                    values = new ArrayList<T>(mOriginalValues);
                }

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

                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;

                        // Start at index 0, in case valueText starts with space(s)
                        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;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            //noinspection unchecked
            mObjects = (List<T>) results.values;
            if (results.count > 0) {
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    }
}

You can refer to the ArrayAdapter's source code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top