如何将自定义适配器添加到AutoCompleteTextView

15
有没有简单的方法将两个TextView设置为AutoCompleteTextView下拉框?
android.R.layout.two_line_list_item,但我找不到任何使用示例。
所以我尝试了这个:
public class TwoLineDropdownAdapter extends BaseAdapter {

    private LayoutInflater mInflater = null;
    private Activity activity;
    public ArrayList<TwoLineDropDown> values = new ArrayList<TwoLineDropDown>();

    public TwoLineDropdownAdapter(Activity a, ArrayList<TwoLineDropDown> items) {

        values = items;
        activity = a;
        mInflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public int getCount() {

        return values.size();
    }

    public TwoLineDropDown getItem(int position) {

        return values.get(position);
    }

    public long getItemId(int position) {

        return position;
    }

    public static class ViewHolder {

        public TextView title;
        public TextView description;
    }

    public View getView(final int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if (convertView == null) {

            holder = new ViewHolder();

            convertView = mInflater.inflate(R.layout.dropdown_text_twoline,
                    parent, false);
            holder.title = (TextView) convertView
                    .findViewById(R.id.text1);
            holder.description = (TextView) convertView
                    .findViewById(R.id.text2);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        return convertView;
    }

    public void add(TwoLineDropDown ei) {

        values.add(ei);
    }
}

但是我在这里遇到了一个问题:

TwoLineDropdownAdapter AutoCompleteAdapter = new TwoLineDropdownAdapter(this, items);
myAutoComplete.setAdapter(AutoCompleteAdapter);

在设置适配器时出现以下提示:

边界不匹配:类型为AutoCompleteTextView的泛型方法setAdapter(T)对参数(TwoLineDropdownAdapter)不适用。推断类型TwoLineDropdownAdapter不是边界参数的有效替代品。

如何解决?

谢谢!

6个回答

24

这段代码对我有效。

将此适配器设置为自动完成文本视图

AutoCompleteTextView etProductSearch = (AutoCompleteTextView)getView().findViewById(R.id.edtSearchBoxTakeOrder);
ProductSearchAdapter adapter = new ProductSearchAdapter(getActivity(), android.R.layout.simple_dropdown_item_1line, productList);
etProductSearch.setAdapter(adapter );

ProductSearchAdapter类

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;

public class ProductSearchAdapter extends ArrayAdapter<ProductDataModel> {
    private ArrayList<ProductDataModel> items;
    private ArrayList<ProductDataModel> itemsAll;
    private ArrayList<ProductDataModel> suggestions;
    private int viewResourceId;

    @SuppressWarnings("unchecked")
    public ProductSearchAdapter(Context context, int viewResourceId,
            ArrayList<ProductDataModel> items) {
        super(context, viewResourceId, items);
        this.items = items;
        this.itemsAll = (ArrayList<ProductDataModel>) items.clone();
        this.suggestions = new ArrayList<ProductDataModel>();
        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);
        }
        ProductDataModel product = items.get(position);
        if (product != null) {
              TextView productLabel = (TextView)  v.findViewById(android.R.id.text1);
            if (productLabel != null) {
                productLabel.setText(product.getProductName());
            }
        }
        return v;
    }

    @Override
    public Filter getFilter() {
        return nameFilter;
    }

    Filter nameFilter = new Filter() {
        public String convertResultToString(Object resultValue) {
            String str = ((ProductDataModel) (resultValue)).getProductName();
            return str;
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            if (constraint != null) {
                suggestions.clear();
                for (ProductDataModel product : itemsAll) {
                    if (product.getProductName().toLowerCase()
                            .startsWith(constraint.toString().toLowerCase())) {
                        suggestions.add(product);
                    }
                }
                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) {
            @SuppressWarnings("unchecked")
            ArrayList<ProductDataModel> filteredList = (ArrayList<ProductDataModel>) results.values;
            if (results != null && results.count > 0) {
                clear();
                for (ProductDataModel c : filteredList) {
                    add(c);
                }
                notifyDataSetChanged();
            }
        }
    };

}

1
谢谢!那真的很有帮助!我遇到了一个问题——我的AutoCompleteTextView中的下拉列表没有显示,而我的自定义适配器的getView方法也没有被调用。我所需要做的就是像这个例子一样添加getFilter方法。 - Victor Lyan
也许我来晚了,但对于未来的开发者们:getView应该被重写。 - Dagnogo Jean-François

9
根据文档,AutoCompleteTextView中setAdapter方法的推断类型是:
<T extends ListAdapter & Filterable> void setAdapter(T adapter)

您的适配器必须是一个ListAdapter(BaseAdapter是这样的,到目前为止还不错),并且必须是Filterable,而BaseAdapter不是,您的适配器实现也不是。我建议扩展ArrayAdapter,它是Filterable,而且可以简化您的实现(一些方法会重复ArrayAdapter的方法来达到相同的结果):

public class TwoLineDropdownAdapter extends ArrayAdapter<TwoLineDropDown> {

    private LayoutInflater mInflater = null;
    private Activity activity;

    public TwoLineDropdownAdapter(Activity a, ArrayList<TwoLineDropDown> items) {
        super(a, 0, items);
        activity = a;
        mInflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public static class ViewHolder {

        public TextView title;
        public TextView description;
    }

    public View getView(final int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if (convertView == null) {

            holder = new ViewHolder();

            convertView = mInflater.inflate(R.layout.dropdown_text_twoline,
                    parent, false);
            holder.title = (TextView) convertView
                    .findViewById(R.id.text1);
            holder.description = (TextView) convertView
                    .findViewById(R.id.text2);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        return convertView;
    }
}

11
我尝试使用这种方法,但是没有出现自动完成。 - tony9099
你能否将 "extends ListAdapter & Filterable" 覆盖为 BaseAdapter?我是说:adapter 变成 adapter? - user3402040
@delive,您可以将“ListAdapter”替换为您想要的任何子类。 - njzk2

4

Dwivedi Ji 的答案转换为 Kotlin。我在 Android Studio 的自动转换中遇到了一些问题,因此花了一些时间来解决。

现在它可以工作了。如果有人需要它(在我的情况下,我正在过滤街道名称):

class StreetsAdapter( private val mContext: Context,
                      private val viewResourceId: Int,
                      private val items: ArrayList<Street>) : ArrayAdapter<Street?>(mContext, viewResourceId, items.toList()) {

    private val itemsAll = items.clone() as ArrayList<Street>
    private var suggestions = ArrayList<Street>()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var v: View? = convertView
        if (v == null) {
            val vi = mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            v = vi.inflate(viewResourceId, null)
        }
        val street: Street? = items[position]
        if (street != null) {
            val streetTitle = v?.findViewById(R.id.tvStreetTitle) as TextView?
            streetTitle?.text = street.title
        }
        return v!!
    }

    override fun getFilter(): Filter {
        return nameFilter
    }

    private var nameFilter: Filter = object : Filter() {
        override fun convertResultToString(resultValue: Any): String {
            return (resultValue as Street).title
        }

        override fun performFiltering(constraint: CharSequence?): FilterResults {
            return if (constraint != null) {
                suggestions.clear()
                for (street in itemsAll) {
                    if (street.title.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
                        suggestions.add(street)
                    }
                }
                val filterResults = FilterResults()
                filterResults.values = suggestions
                filterResults.count = suggestions.size
                filterResults
            } else {
                FilterResults()
            }
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            val filteredList =  results?.values as ArrayList<Street>?

            if (results != null && results.count > 0) {
                clear()
                for (c: Street in filteredList ?: listOf<Street>()) {
                    add(c)
                }
                notifyDataSetChanged()
            }
        }
    }
}

设置您的适配器:

val adapter = StreetsAdapter(this,
       R.layout.item_street, //Your layout. Make sure it has [TextView] with id "tvStreetTitle" 
       arrayListOf() //Your list goes here
)
autoTextView.threshold = 1 //will start working from first character
autoTextView.setAdapter(adapter)

这个运作得很好,唯一发现的问题是当你进行结果筛选并点击单个项目时,输入一个空格并删除它时会导致崩溃。 - K P

0

我相信最简单的方法是扩展SimpleAdapter

public class MyAdapter extends android.widget.SimpleAdapter {

    static ArrayList<Map<String, String>> toMapList(Collection<MyObject> objectsCollection) {
        ArrayList<Map<String, String>> objectsList = new ArrayList<Map<String, String>>(objectsCollection.size());
        for (MyObject obj : objectsCollection) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("name", obj.getName());
            map.put("details", obj.getDetails());
            objectsList.add(map);
        };
        return objectsList;
    }

    public MyAdapter(Context context, Collection<MyObject> objects) {

        super(context, toMapList(objects),
              R.layout.auto_complete_layout, new String[] {"name", "description"}, new int[] {R.id.name, R.id.description});
    }
}

主要缺点是这将基于名称或描述中的任何空格分隔单词带来候选人。如果您向auto_complete_layout添加另一个字段,则它也将参与匹配。

因此,我重写了SimpleAdapter以更好地满足我的需求,删除了大量不适用于我的用例的基类开销。但上面的几行为您提供了一个良好的起点,并提供了一个坚实的参考,可供开始自定义。


0
这是一个针对 AutoCompleteTextView 的扩展,使用 Kotlin 编写。
fun AutoCompleteTextView.showListDropDown(list: List<Any?>, action:(item: Any) -> Unit){

    val adapter = ArrayAdapter<Any?>(
        this.context,
        R.layout.custom_dropdown_item,
        ArrayList<Any?>(list)
    )

    this.setAdapter(adapter)

    this.threshold = 1

    this.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
        val item = adapter.getItem(position)!!
        action(item)
    }

    this.setOnTouchListener { _: View?, _: MotionEvent? ->
        if (list.isNotEmpty()) {
            if (this.text.toString() != "") adapter.filter
                .filter(null)
            this.showDropDown()
        }
        return@setOnTouchListener true
    }
}

-1

永远记住,当你为你的AutoCompleteTextView定制ArrayAdapter时,你必须实现自己的筛选方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接