从单独的线程中的服务获取AutoCompleteTextView建议

时间:2011-01-05 20:50:48

标签: android multithreading autocompletetextview

对于我的AutoCompleteTextView,我需要从网络服务中获取数据。因为它可能需要一点时间我不希望UI线程没有响应,所以我需要以某种方式在单独的线程中获取数据。例如,在从SQLite DB获取数据时,使用CursorAdapter方法 - runQueryOnBackgroundThread非常容易。我正在寻找其他适配器,例如ArrayAdapterBaseAdapter,但找不到类似的东西......

有一种简单的方法可以实现这一目标吗?我不能直接使用ArrayAdapter,因为建议列表是动态的 - 我总是根据用户输入获取建议列表,因此无法预取和缓存以供进一步使用...

如果有人可以就这个主题提供一些提示或示例 - 会很棒!

3 个答案:

答案 0 :(得分:10)

通过上述方法,我在输入速度非常快时也遇到了这些问题。我想这是因为结果的过滤是由过滤器类异步完成的,所以在完成过滤时在ui线程中修改Adapter的ArrayList时会出现问题。

http://developer.android.com/reference/android/widget/Filter.html

然而,采用以下方法,一切正常。

public class MyActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        MyAdapter myAdapter = new MyAdapter(this, android.R.layout.simple_dropdown_item_1line);

        AutoCompleteTextView acTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
        acTextView.setAdapter(myAdapter);
    }
}

public class MyAdapter extends ArrayAdapter<MyObject> {
    private Filter mFilter;

    private List<MyObject> mSubData = new ArrayList<MyObject>();
    static int counter=0;

    public MyAdapter(Context context, int textViewResourceId) {
      super(context, textViewResourceId);
      setNotifyOnChange(false);

      mFilter = new Filter() {
        private int c = ++counter;
        private List<MyObject> mData = new ArrayList<MyObject>();

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
          // This method is called in a worker thread
          mData.clear();

          FilterResults filterResults = new FilterResults();
          if(constraint != null) {
            try {
              // Here is the method (synchronous) that fetches the data
              // from the server      
              URL url = new URL("...");
              URLConnection conn = url.openConnection();
              BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
              String line = "";

              while ((line = rd.readLine()) != null) {
                      mData.add(new MyObject(line));
              }
            }
            catch(Exception e) {
            }

            filterResults.values = mData;
            filterResults.count = mData.size();
          }
          return filterResults;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence contraint, FilterResults results) {
          if(c == counter) {
            mSubData.clear();
              if(results != null && results.count > 0) {
                ArrayList<MyObject> objects = (ArrayList<MyObject>)results.values;
                for (MyObject v : objects)
                  mSubData.add(v);

                notifyDataSetChanged();
              }
              else {
                notifyDataSetInvalidated();
              }
          }
        }
    };
  }

  @Override
  public int getCount() {
    return mSubData.size();
  }

  @Override
  public MyObject getItem(int index) {
    return mSubData.get(index);
  }

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

答案 1 :(得分:9)

已编辑:添加天真的方法以避免在您点击建议时显示下拉列表。

我在我的应用中做了类似的事情:

private AutoCompleteTextView mSearchbar;
private ArrayAdapter<String> mAutoCompleteAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mAutoCompleteAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line);
    mSearchbar = (AutoCompleteTextView) findViewById(R.id.searchbar);
    mSearchbar.setThreshold(3);
    mSearchbar.setAdapter(mAutoCompleteAdapter);
    mSearchbar.addTextChangedListener(new TextWatcher() {

        private boolean shouldAutoComplete = true;

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            shouldAutoComplete = true;
            for (int position = 0; position < mAutoCompleteAdapter.getCount(); position++) {
                if (mAutoCompleteAdapter.getItem(position).equalsIgnoreCase(s.toString())) {
                    shouldAutoComplete = false;
                    break;
                }
            }

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            if (shouldAutoComplete) {
                new DoAutoCompleteSearch().execute(s.toString());
            }
        }
    }
}

private class DoAutoCompleteSearch extends AsyncTask<String, Void, ArrayList<String>> {
    @Override
    protected ArrayList<String> doInBackground(String... params) {
        ArrayList<String> autoComplete = new ArrayList<String>();
        //do autocomplete search and stuff.
        return autoComplete;
    }

    @Override
    protected void onPostExecute(ArrayList<String> result) {
        mAutoCompleteAdapter.clear();
        for (String s : result)
            mAutoCompleteAdapter.add(s);
    }
}

答案 2 :(得分:1)

有相同的解决方案,除了问题是一切都很好(变量在我调试时更新)但自动完成填充奇怪,如

当我键入sco时,它有结果但不显示在列表中 但是当我退格时它会显示sco的结果。在调试中,所有变量都会更新,这只会告诉我没有为AutoCompleteTextView更新UI。当我退格时它被触发更新,然后它显示早期的计算机列表然后它(同时它用新的搜索字符串的新列表项更新它。 有人遇到过这个问题吗?