动态更新 AutoCompleteTextView 适配器

Posted

技术标签:

【中文标题】动态更新 AutoCompleteTextView 适配器【英文标题】:Dynamically updating an AutoCompleteTextView adapter 【发布时间】:2011-12-26 22:33:45 【问题描述】:

我想通过从 RESTful Web 服务获取列表来定期更改 AutoCompleteTextview 给出的建议,但无法使其顺利运行。我设置了一个硬编码的建议列表,以确保它正常工作:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, new String[] "Hi", "Ho");
speciesName.setAdapter(adapter);//my autocomplete tv

我在 textview 上有一个 TextWatcher,当文本更改时,它会启动一个非阻塞调用以获取新的建议列表——这部分获取新列表的工作正常。然后我想重置适配器,如下所示:

public void setOptionsAndUpdate(String[] options) 
    Log.d(TAG, "setting options");
    //speciesName.setAdapter((ArrayAdapter<String>)null);
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, options);
    speciesName.setAdapter(adapter);

此方法已调用,但不起作用 - 尽管调用了 setAdapter,但建议列表要么消失,要么显示的建议保持不变。

这甚至是正确的方法吗?我查看了SimpleCursorAdapter,但看不到如何将我的网络服务注册为内容提供者。 (它的形式是http://www.blah.com/query?term=XX,其中 XX 是我的应用程序的输入,响应是一个 JSON 字符串数组。)

【问题讨论】:

我在这里做类似的事情!!! ***.com/questions/12854336/… 【参考方案1】:

这就是我更新 AutoCompleteTextView 的方式:

String[] data = terms.toArray(new String[terms.size()]);  // terms is a List<String>
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data);
keywordField.setAdapter(adapter);  // keywordField is a AutoCompleteTextView
if(terms.size() < 40) keywordField.setThreshold(1); 
else keywordField.setThreshold(2);

当然,这是静态的,不处理无线建议,但我也可以建议您在将更改分配给 AutoCompleteTextView 后通知适配器:

adapter.notifyDataSetChanged();   

希望这会有所帮助。

-serkan

【讨论】:

@serkan:太好了,这正是我所需要的。但是,我认为阈值没有更新,对吧?因此,在您的示例中,如果以 terms.size() &lt; 40 开头,则阈值将永远保持在 1 中。当然,您可以再次运行该行代码进行更新。谢谢! 谢谢路易斯。好吧,我在这里所做的只是说,如果我有一个小清单,即使输入了 1 个字符,我也可以开始建议;但是,如果我的池很大,那么,为了提高效率,我应该等到输入两个字符让我提出任何建议。您当然始终保留 1 或 2。【参考方案2】:

关于这个主题有一个非常好的教程,使用 Google Map API 中的远程数据来填充 AutoCompleteTextView here。

如果您需要缓存版本,我从here 检索到它。 原始教程已被删除,但本质上您需要以类似于下面所示的方式编写一个带有自定义过滤器的 ArrayAdapter,并将其分配给您的 AutoCompleteTextView。

注意:您需要实现一个方法 autocomplete() 来执行同步获取和返回自动完成项所需的任何操作。由于过滤器是在后台线程中调用的,因此不会阻塞主 UI 线程。

private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable 
private ArrayList<String> resultList;

public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) 
    super(context, textViewResourceId);


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


@Override
public String getItem(int index) 
    return resultList.get(index);


@Override
public Filter getFilter() 
    Filter filter = new Filter() 
        @Override
        protected FilterResults performFiltering(CharSequence constraint) 
            FilterResults filterResults = new FilterResults();
            if (constraint != null) 
                // Retrieve the autocomplete results.
                resultList = autocomplete(constraint.toString());

                // Assign the data to the FilterResults
                filterResults.values = resultList;
                filterResults.count = resultList.size();
            
            return filterResults;
        

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


【讨论】:

我对 UI 线程有同样的疑问。 @manyobject 你能澄清一下吗? 我测试过了,没有收到 NetworkOnMainThreadException。所以它可以安全使用。 网络调用不会阻塞 UI,因为 Android 在后台异步线程上执行过滤。因此,您可以/应该编写 autocomplete() 方法,以便它同步获取数据(阻塞)。 这应该是被接受的答案!!!做得好。使用 Retrofit 同步调用验证了这一点。在不阻塞 UI 线程的情况下工作!! 谢谢,它有效! :) 一个建议:它可能会导致 IndexOutOfBoundsException,如果你快速输入(resultList 正在更新)。解决方案:在函数 publishResults() 中更新本地 resultList 而不是 performFiltering()【参考方案3】:

在适配器中动态添加和更改数据时,我没有任何运气使用 adapter.notifyDataSetChanged()。在我的情况下,我正在异步访问外部 api 并定期获取完成数据列表。

此代码清除适配器,并按照您的预期添加新数据。但是,我不得不调用 getFilter().Filter 方法来强制显示数据。另外,我必须根据 AutocompleteTextView 中的当前文本显式过滤,因为我的 api 调用是异步的。

adapter.clear();
for (Map<String, String> map : completions) 
     adapter.add(map.get("name"));


//Force the adapter to filter itself, necessary to show new data.
//Filter based on the current text because api call is asynchronous. 
adapter.getFilter().filter(autocompleteTextView.getText(), null);

【讨论】:

你可以传入autocompleteTextView,而不是传入null,让它的行为方式与AutoCompleteTextView.performFiltering()相同——这是文本更改后通常调用的方法跨度> 花费数小时搜索此内容。我给你一杯啤酒:) 这是解决此类问题的最佳方法!非常感谢! 这是我从doAfterTextChanged 中更新自动完成列表后获得更新的唯一方法,我从数据库查询中异步获取相关列表。它避免了每次都创建一个适配器。谢谢!【参考方案4】:

由于我无法添加评论,我正在给出一个新答案 无需清除适配器或调用 adapter.getFilter().filter(...)... 要动态更新 AutoCompleteTextView 适配器,只需将新项目添加到适配器并再次 setAdapter。对于原始问题中给出的示例,我尝试了以下操作并且它可以工作(下面的代码没有显示适配器的初始设置,因为这里有多个答案涵盖了这一点。这只是显示了动态更新适配器)。适配器更新可以与更新与 ArrayAdapter 关联的列表的代码一起使用。

    adapter.add(String newSuggestion); // this goes inside a loop for adding multiple suggestions
    speciesName.setAdapter(adapter) ; // speciesName is an AutoCompleteTextView as given in the original question.

【讨论】:

【参考方案5】:

我找到的更新适配器的最佳解决方案:

Editable text = autocomplete.getText();
autocomplete.setText(text);
autocomplete.setSelection(text.length());

工作原理:

我们将 autoCompleteTextView 的文本设置为其当前文本,因此适配器会通知数据已更改并更新 listViews 的内容。

但是通过这个技巧,光标移动到了edittext的开头。所以我们使用autocomplete.setSelection(text.length()) 将光标移动到末尾。

像魅力一样工作!

编辑:

您还必须直接在ArrayAdapter 上使用clear()add()remove() 方法,而不是ArrayList

【讨论】:

我尝试了许多不同的解决方案,但这是唯一适合我的解决方案。

以上是关于动态更新 AutoCompleteTextView 适配器的主要内容,如果未能解决你的问题,请参考以下文章

Android动态自动匹配输入框

AutoCompleteTextView 的用法

适配器中的AutoCompleteTextView无法正常工作

与 Uber 一样,在 ListView 中显示带有 Google Places 的 AutoCompleteTextView

AutoCompleteTextView的使用

AutoCompleteTextView