How to add a custom adapter to an AutoCompleteTextView

前端 未结 6 976
余生分开走
余生分开走 2021-02-14 08:37

Is there any simple way to set a 2 TextView dropdown to an AutoCompleteTextView.

There is android.R.layout.two_line_list_item Which I couldn\'t find any exa

相关标签:
6条回答
  • 2021-02-14 09:06

    Here's an extension for 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
        }
    }
    
    0 讨论(0)
  • 2021-02-14 09:12

    Always keep in mind that when you are customizing ArrayAdapter for your AutoCompleteTextView, you have to implement your own filtering method.

    0 讨论(0)
  • 2021-02-14 09:18

    According to the documentation, the inferred type of setAdapter in AutoCompleteTextView is :

    <T extends ListAdapter & Filterable> void setAdapter(T adapter)
    

    Your adapter must be a ListAdapter (which BaseAdapter is, so far so good) and a Filterable, which BaseAdapter is not, nor is your Adapter implementation. I would extend an ArrayAdapter, which is Filterable, not to mention is would simplify your implementation (some of your methods duplicate methods of ArrayAdapter for the same result) :

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-14 09:27

    Converted Dwivedi Ji's answer to Kotlin. I had some issue with Android Studio's auto convert. Thus, spent some time to make it work.

    Now it is working. In case anyone needs it (in my case, I am filtering street names):

    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()
                }
            }
        }
    }
    
    

    And set your adapter:

    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)
    
    0 讨论(0)
  • 2021-02-14 09:28

    Here Code is working for me,

    Set This adapter to autocompletetextview

    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 class

    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();
                }
            }
        };
    
    }
    
    0 讨论(0)
  • 2021-02-14 09:29

    I believe that the easiest approach is to extend 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});
        }
    }
    

    The major drawback is that this will bring candidates based on any space-delimited word in either name or description. If you add another field to your auto_complete_layout, it will be involved in matching, too.

    Therefore, I finished with rewriting the SimpleAdapter to better fit my needs, removing a significant amount of the base class overhead which was not relevant for my use case. But the few lines above give you a good start, and provide a solid reference to start customization from.

    0 讨论(0)
提交回复
热议问题