我使用 Firestore 做了类似的事情,创建了一个自定义 ArrayAdapter,它侦听 Firestore 查询并实现 Filterable。
public class ProductAdapter extends ArrayAdapter<Product>
implements Filterable, EventListener<QuerySnapshot> {
private final Object mLock = new Object();
private static final java.lang.String TAG = "Product Adapter";
private ArrayList<Product> mProducts = new ArrayList<>();
private ArrayList<Product> mOriginalValues;
private ArrayFilter mFilter;
private CharSequence mFilterPrefix;
private Query mQuery;
private ListenerRegistration mRegistration;
public ProductAdapter(Context context, int resource, Query query) {
super(context, resource);
mQuery = query;
}
@Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "onEvent:error", e);
return;
}
for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
switch (change.getType()) {
case ADDED:
onDocumentAdded(change);
break;
case MODIFIED:
onDocumentModified(change);
break;
case REMOVED:
onDocumentRemoved(change);
break;
}
if (mFilterPrefix != null) {
getFilter().filter(mFilterPrefix);
}
}
}
public void startListening() {
if (mQuery != null && mRegistration == null) {
mRegistration = mQuery.addSnapshotListener(this);
}
}
public void stopListening() {
if (mRegistration != null) {
mRegistration.remove();
mRegistration = null;
}
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.clear();
} else {
mProducts.clear();
}
}
notifyDataSetChanged();
}
public void setQuery(Query query) {
stopListening();
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.clear();
}
mProducts.clear();
}
notifyDataSetChanged();
mQuery = query;
startListening();
}
protected void onDocumentAdded(DocumentChange change) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(change.getNewIndex(), change.getDocument().toObject(Product.class));
} else {
mProducts.add(change.getNewIndex(), change.getDocument().toObject(Product.class));
}
}
notifyDataSetChanged();
}
protected void onDocumentModified(DocumentChange change) {
synchronized (mLock) {
if (change.getOldIndex() == change.getNewIndex()) {
if (mOriginalValues != null) {
mOriginalValues.set(change.getOldIndex(), change.getDocument().toObject(Product.class));
} else {
mProducts.set(change.getOldIndex(), change.getDocument().toObject(Product.class));
}
} else {
if (mOriginalValues != null) {
mOriginalValues.remove(change.getOldIndex());
mOriginalValues.add(change.getNewIndex(), change.getDocument().toObject(Product.class));
} else {
mProducts.remove(change.getOldIndex());
mProducts.add(change.getNewIndex(), change.getDocument().toObject(Product.class));
}
}
}
notifyDataSetChanged();
}
protected void onDocumentRemoved(DocumentChange change) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.remove(change.getOldIndex());
} else {
mProducts.remove(change.getOldIndex());
}
}
notifyDataSetChanged();
}
@Override
public int getCount() {
return mProducts.size();
}
@Override
public Product getItem(int position){
return mProducts.get(position);
}
@Override
public long getItemId(int position){
return position;
}
@Override
public View getView(int position, final View convertView, final ViewGroup parent) {
TextView label = (TextView) super.getView(position, convertView, parent);
label.setText(mProducts.get(position).getName());
return label;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView label = (TextView) super.getDropDownView(position, convertView, parent);
label.setText(mProducts.get(position).getName());
return label;
}
@Override
public @NonNull Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
final FilterResults results = new FilterResults();
mFilterPrefix = prefix;
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<>(mProducts);
}
}
final ArrayList<Product> values;
synchronized (mLock) {
values = new ArrayList<>(mOriginalValues);
}
if (prefix == null || prefix.length() == 0) {
results.values = values;
results.count = values.size();
} else {
final String prefixString = prefix.toString().toLowerCase();
final int count = values.size();
final ArrayList<Product> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final Product product = values.get(i);
final String valueText = product.getName().toLowerCase();
if (valueText.startsWith(prefixString)) {
newValues.add(product);
} else {
final String[] words = valueText.split(" ");
if (words.length > 1) {
if (words[words.length - 1].startsWith(prefixString)) {
newValues.add(product);
} else {
for (int j = words.length - 2; j > 0; j--) {
words[j] = words[j] + " " + words[j + 1];
if (words[j].startsWith(prefixString)) {
newValues.add(product);
break;
}
}
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mProducts = (ArrayList<Product>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
然后我将此适配器设置为我的AutoCompleteTextView 的适配器。在我的例子中,我正在通过 AutoCompleteTextView 中显示的名称过滤 Product 类的对象列表,但您可以将其更改为字符串。
需要明确的是,此方法侦听整个文档列表(在我的例子中为产品)的查询,然后在本地过滤它们,而不是根据AutoCompleteTextView 中键入的文本进行查询。这样,您无需在每次输入新字母时都进行查询,并且如果 Firestore 数据库发生更改,列表会自动更新。如果您想在每次文本更改时进行新查询,您可以尝试使用 this 之类的内容。