如何使用 AutoCompleteTextView 并使用来自 Web API 的数据填充它?

Posted

技术标签:

【中文标题】如何使用 AutoCompleteTextView 并使用来自 Web API 的数据填充它?【英文标题】:How do I Use AutoCompleteTextView and populate it with data from a web API? 【发布时间】:2011-06-28 18:16:37 【问题描述】:

我想在我的活动中使用AutoCompleteTextView,并通过查询 Web API 在用户键入时填充数据。我该怎么做呢?

我是创建一个新类并覆盖AutoCompleteTextView.performFiltering,还是使用自定义列表适配器并提供一个自定义android.widget.Filter 来覆盖performFiltering?

或者有没有更好的方法来实现我的最终目标?

我做了一些类似的事情,但它是针对快速搜索框的,它涉及实现一个服务,但我相信这不是我想要做的。

【问题讨论】:

未来观众的绝佳链接:) : makovkastar.github.io/blog/2014/04/12/… 请看这里***.com/a/68313126/3904109 【参考方案1】:

我想出了一个解决方案,我不知道它是否是最好的解决方案,但它似乎工作得很好。我所做的是创建了一个扩展 ArrayAdapter 的自定义适配器。在自定义适配器中,我覆盖了 getFilter 并创建了自己的过滤器类来覆盖 performFiltering。这会启动一个新线程,因此它不会中断 UI。下面是一个准系统示例。

MyActivity.java

public class MyActivity extends Activity 
    private AutoCompleteTextView style;

    @Override
    public void onCreate(Bundle savedInstanceState) 
        ...
        style = (AutoCompleteTextView) findViewById(R.id.style);
        adapter = new AutoCompleteAdapter(this, android.R.layout.simple_dropdown_item_1line); 
        style.setAdapter(adapter);
    

AutoCompleteAdapter.java

public class AutoCompleteAdapter extends ArrayAdapter<Style> implements Filterable 
    private ArrayList<Style> mData;

    public AutoCompleteAdapter(Context context, int textViewResourceId) 
        super(context, textViewResourceId);
        mData = new ArrayList<Style>();
    

    @Override
    public int getCount() 
        return mData.size();
    

    @Override
    public Style getItem(int index) 
        return mData.get(index);
    

    @Override
    public Filter getFilter() 
        Filter myFilter = new Filter() 
            @Override
            protected FilterResults performFiltering(CharSequence constraint) 
                FilterResults filterResults = new FilterResults();
                if(constraint != null) 
                    // A class that queries a web API, parses the data and returns an ArrayList<Style>
                    StyleFetcher fetcher = new StyleFetcher();
                    try 
                        mData = fetcher.retrieveResults(constraint.toString());
                    
                    catch(Exception e) 
                        Log.e("myException", e.getMessage());
                    
                    // Now assign the values and count to the FilterResults object
                    filterResults.values = mData;
                    filterResults.count = mData.size();
                
                return filterResults;
            

            @Override
            protected void publishResults(CharSequence contraint, FilterResults results) 
                if(results != null && results.count > 0) 
                notifyDataSetChanged();
                
                else 
                    notifyDataSetInvalidated();
                
            
        ;
        return myFilter;
    

【讨论】:

很好的解决方案——正是我所需要的。如果我可能会问...您将样式返回到您的 simple_dropdown_item_1line 布局。您如何从 Style 类中检索适当的值?对我来说,这是我创建的一个类,我希望文本值 getStyleName 显示在我的列表项中,但它只是显示类的名称。 @bugfixr 在你的类上添加一个公共的 toString 方法。在我的例子中是: public String toString() return name; 注意 notifyDataSetInvalidated() 调用。这意味着随后无法使用适配器。 @AJ。感谢您的回答。由于答案是 6 年,我想知道这是否仍然是最佳做法,或者有一种新的更好的方法来实现自动完成元素。 @Tohid 这仍然是最好的答案,只能添加一件事。这是一个延迟的处理程序,可用于触发 API 调用。如图所示:truiton.com/2018/06/…【参考方案2】:

扩展上面AJ. 的答案,以下自定义适配器还包括服务器请求的处理和 json 解析:

class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable

    private ArrayList<String> data;
    private final String server = "http://myserver/script.php?query=";

    AutoCompleteAdapter (@NonNull Context context, @LayoutRes int resource)
    
        super (context, resource);
        this.data = new ArrayList<>();
    

    @Override
    public int getCount()
    
        return data.size();
    

    @Nullable
    @Override
    public String getItem (int position)
    
        return data.get (position);
    

    @NonNull
    @Override
    public Filter getFilter()
    
        return new Filter()
        
            @Override
            protected FilterResults performFiltering (CharSequence constraint)
            
                FilterResults results = new FilterResults();
                if (constraint != null)
                
                    HttpURLConnection conn = null;
                    InputStream input = null;
                    try
                    
                        URL url = new URL (server + constraint.toString());
                        conn = (HttpURLConnection) url.openConnection();
                        input = conn.getInputStream();
                        InputStreamReader reader = new InputStreamReader (input, "UTF-8");
                        BufferedReader buffer = new BufferedReader (reader, 8192);
                        StringBuilder builder = new StringBuilder();
                        String line;
                        while ((line = buffer.readLine()) != null)
                        
                            builder.append (line);
                        
                        JSONArray terms = new JSONArray (builder.toString());
                        ArrayList<String> suggestions = new ArrayList<>();
                        for (int ind = 0; ind < terms.length(); ind++)
                        
                            String term = terms.getString (ind);
                            suggestions.add (term);
                        
                        results.values = suggestions;
                        results.count = suggestions.size();
                        data = suggestions;
                    
                    catch (Exception ex)
                    
                        ex.printStackTrace();
                    
                    finally
                    
                        if (input != null)
                        
                            try
                            
                                input.close();
                            
                            catch (Exception ex)
                            
                                ex.printStackTrace();
                            
                        
                        if (conn != null) conn.disconnect();
                    
                
                return results;
            

            @Override
            protected void publishResults (CharSequence constraint, FilterResults results)
            
                if (results != null && results.count > 0)
                
                    notifyDataSetChanged();
                
                else notifyDataSetInvalidated();
            
        ;
    

并以同样的方式使用它:

public class MyActivity extends Activity

    @Override
    public void onCreate(Bundle savedInstanceState) 
        ...
        AutoCompleteTextView textView = (AutoCompleteTextView) findViewById (R.id.style);
        int layout = android.R.layout.simple_list_item_1;
        AutoCompleteAdapter adapter = new AutoCompleteAdapter (this, layout); 
        textView.setAdapter (adapter);
    

【讨论】:

【参考方案3】:

Chu:要自定义视图的外观并更好地控制展开对象,请执行以下操作...

    @Override
    public View getView (int position, View convertView, ViewGroup parent) 
        TextView originalView = (TextView) super.getView(position, convertView, parent); // Get the original view

        final LayoutInflater inflater = LayoutInflater.from(getContext());
        final TextView view = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);

        // Start tweaking
        view.setText(originalView.getText());
        view.setTextColor(R.color.black);  // also useful if you have a color scheme that makes the text show up white
        view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); // override the text size
        return view;
    

【讨论】:

【参考方案4】:
private AutoCompleteUserAdapter userAdapter;
private AutoCompleteTextView actvName;
private ArrayList<SearchUserItem> arrayList;

actvName = findViewById(R.id.actvName);

actvName.setOnItemClickListener(new AdapterView.OnItemClickListener() 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
        actvName.setText(userAdapter.getItemNameAtPosition(position));
        actvName.setSelection(actvName.getText().toString().trim().length());
    
);

actvName.addTextChangedListener(new TextWatcher() 
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) 

    

    @Override
    public void onTextChanged(final CharSequence s, int start, int before, int count) 
        if (actvName.isPerformingCompletion()) 
            // An item has been selected from the list. Ignore.
         else 
            if (s.toString().toLowerCase().trim().length() >= 2) 
                getUserList(s.toString().toLowerCase().trim());
            
        
    

    @Override
    public void afterTextChanged(Editable s) 

    
);

private void getUserList(String searchText) 
    //Add data to your list after success of API call
    arrayList = new ArrayList<>();
    arrayList.addAll(YOUR_LIST);
    userAdapter = new AutoCompleteUserAdapter(context, R.layout.row_user, arrayList);
    getActivity().runOnUiThread(new Runnable() 
        @Override
        public void run() 
            actvName.setAdapter(userAdapter);
            userAdapter.notifyDataSetChanged();
            actvName.showDropDown();
        
    );        

自动完成用户适配器

/**
 * Created by Ketan Ramani on 11/07/2019.
 */
public class AutoCompleteUserAdapter extends ArrayAdapter<SearchUserItem> 

    private Context context;
    private int layoutResourceId;
    private ArrayList<SearchUserItem> arrayList;

    public AutoCompleteUserAdapter(Context context, int layoutResourceId, ArrayList<SearchUserItem> arrayList) 
        super(context, layoutResourceId, arrayList);
        this.context = context;
        this.layoutResourceId = layoutResourceId;
        this.arrayList = arrayList;
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        try 
            if (convertView == null) 
                convertView = LayoutInflater.from(parent.getContext()).inflate(layoutResourceId, parent, false);
            

            SearchUserItem model = arrayList.get(position);

            AppCompatTextView tvUserName = convertView.findViewById(R.id.tvUserName);
            tvUserName.setText(model.getFullname());
         catch (NullPointerException e) 
            e.printStackTrace();
         catch (Exception e) 
            e.printStackTrace();
        

        return convertView;
    

    public String getItemNameAtPosition(int position) 
        return arrayList.get(position).getName();
    

    public String getItemIDAtPosition(int position) 
        return arrayList.get(position).getId();
    

【讨论】:

对 actvName.isPerformingCompletion() 投了赞成票,这节省了我的处理时间。【参考方案5】:

这是一个 Kotlin 版本的适配器类,它通过 Room 从本地数据库加载数据:

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.Filterable
import android.widget.TextView
import ...MyFinderDatabase
import ...R
import ...model.SearchResult

class SearchCompleteAdapter(context: Context, val resourceId: Int): ArrayAdapter<SearchResult>(context, resourceId), Filterable 
    private val results = mutableListOf<SearchResult>()

    override fun getCount() = results.size

    override fun getItem(position: Int) = results[position]

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View 
        val view = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false)
        val textView = view.findViewById<TextView>(R.id.autocomplete_name)
        textView.text = getItem(position).fullName
        return view
    

    override fun getFilter() = object : Filter()
        override fun performFiltering(constraint: CharSequence?): FilterResults 
            val filterResults = FilterResults()
            val db = MyRoomDatabase.getDatabase(context.applicationContext)
            val dbResults = db.resultDao().findWithNameLike(String.format("%%%s%%", constraint.toString()))
            filterResults.values = dbResults
            filterResults.count = dbResults.size
            results.clear()
            results.addAll(dbResults)
            return filterResults
        

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) 
            if((results != null) && (results.count > 0))
                notifyDataSetChanged()
            
            else
                notifyDataSetInvalidated()
            
        

        override fun convertResultToString(resultValue: Any?): CharSequence 
            val searchResult = resultValue as SearchResult
            return searchResult.fullName
        
    

DAO 方法定义:

    @Query("select * from SearchResult where full_name like :name and type = 'USER_TYPE'")
fun findWithNameLike(name: String): List<SearchResult>

【讨论】:

谢谢!你刚刚为我节省了几个小时 =)

以上是关于如何使用 AutoCompleteTextView 并使用来自 Web API 的数据填充它?的主要内容,如果未能解决你的问题,请参考以下文章

如何在保留字符状态的情况下动态向 autocompletetextview 添加建议

如何捕获AutoCompleteTextView箭头单击事件

AutoCompleteTextView onItemClick 使用 HashMap 的项目位置或 id

使用 CursorLoader 查询 SQLite DB 并填充 AutoCompleteTextView

如何在android的AutocompleteTextView中添加水平滚动条?

Google 在 Appcompactivity - Android 中对 AutoCompleteTextView 进行 api 调用