使用 RxAndroid 过滤数据库项目的正确方法
Proper way to filter database items using RxAndroid
我正在使用 the sample 提供的代码,并根据我的要求调整了代码。我正在学习 Rx,但并不完全熟悉它的工作原理。所以,在我的片段中我有
private static String LIST_QUERY = "SELECT * FROM " + TodoItem.TABLE;
我的 onResume 看起来像:
@Override
public void onResume() {
super.onResume();
subscription = db.createQuery(TodoItem.TABLE, LIST_QUERY)
.mapToList(TodoItem.MAPPER)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter);
}
适配器在列表视图中显示数据并且工作正常,但现在我在列表顶部添加了一个 TextView 并希望在用户输入文本时过滤结果。我相信 RxAndroid 提供了一个去抖动运算符来提高效率(而不是立即查询数据库),但我不确定如何组合这个订阅代码来监听 textView 的变化并根据该文本进行过滤。
我已经尝试将 LIST_QUERY 更改为 "SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \"" + query + "\" LIMIT 5”;
然后尝试:
etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence searchQuery, int i, int i1, int i2) {
query = searchQuery.toString();
adapter.notifyDataSetChanged();
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { //ignore }
@Override
public void afterTextChanged(Editable editable) { //ignore }
});
这导致列表为空(我认为这没问题,因为初始查询是空字符串)但是当我输入一些文本时,查询根本没有 update/filter 列表。
我尝试了另一种方法并在 mapToList 之后添加了 take(5)(因为我想要 LIMIT 5)并且它起作用并且只显示了 5 个项目。然后我添加了 .filter(,键入 new 并让 Android Studio 生成 Func1 代码,它看起来像:
subscription = db.createQuery(ListItem.TABLE, LIST_QUERY)
.mapToList(Item.MAPPER)
.filter(new Func1<List<ListItem>, Boolean>() {
@Override
public Boolean call(List<ListItem> items) {
return null;
}
})
所以问题是它要求我过滤整个列表。我试过这个:
public Boolean call(List<ListItem> items) {
for(ListItem i: items)
if(!i.description().startsWith(query))
items.remove(i);
return true;
}
但仍然显示了整个列表,并且文本的更改根本没有使列表发生变化。我相信我错过了一些东西,我必须订阅 EditText 但我不确定我将如何做到这一点并将它与现有的数据库订阅代码结合起来。寻找解决方案。
有几种不同的方法可以做到这一点。如果您希望过滤器存在于 java 中,那么您可以在 QueryObservable
:
上使用 asRows
运算符
database.createQuery(ListItem.TABLE, ListItem.QUERY)
.flatMap(query -> query.asRows(Item.MAPPER)
.filter(item -> item.description.startsWith(query))
.toList())
这将是一个 Observable<List<Item>>
,其中仅包含与查询匹配的项目。
但是您最初的方法是最理想的方法,您只是错误地使用了 LIKE
。您可能希望包含通配符(%
或 _
)以包含与查询不完全匹配的结果。例如:
"SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \"%" + query + "%\" LIMIT 5”;
将匹配描述中包含查询的任何结果。 %
通配符匹配 0 个或多个任意字符。 _
通配符匹配单个字符。
您提到的另一部分是列表未正确更新。你想在这里使用 RxAndroid 而不是 textChangedListeners 最有可能。在你看来,你会有类似
的东西
RxTextView.textChanges(editText)
.switchMap(query -> database.createQuery(ListItem.TABLE, ListItem.query(query))
.mapToList(ListItem.MAPPER))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
adapter.setData(list);
adapter.notifyDataSetChanged();
});
这样您就不必担心查询执行和适配器获得通知调用之间的任何竞争。 ListItem.query(query)
方法只是将 sqlite 字符串与查询组合在一起。
我正在使用 the sample 提供的代码,并根据我的要求调整了代码。我正在学习 Rx,但并不完全熟悉它的工作原理。所以,在我的片段中我有
private static String LIST_QUERY = "SELECT * FROM " + TodoItem.TABLE;
我的 onResume 看起来像:
@Override
public void onResume() {
super.onResume();
subscription = db.createQuery(TodoItem.TABLE, LIST_QUERY)
.mapToList(TodoItem.MAPPER)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter);
}
适配器在列表视图中显示数据并且工作正常,但现在我在列表顶部添加了一个 TextView 并希望在用户输入文本时过滤结果。我相信 RxAndroid 提供了一个去抖动运算符来提高效率(而不是立即查询数据库),但我不确定如何组合这个订阅代码来监听 textView 的变化并根据该文本进行过滤。
我已经尝试将 LIST_QUERY 更改为 "SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \"" + query + "\" LIMIT 5”;
然后尝试:
etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence searchQuery, int i, int i1, int i2) {
query = searchQuery.toString();
adapter.notifyDataSetChanged();
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { //ignore }
@Override
public void afterTextChanged(Editable editable) { //ignore }
});
这导致列表为空(我认为这没问题,因为初始查询是空字符串)但是当我输入一些文本时,查询根本没有 update/filter 列表。
我尝试了另一种方法并在 mapToList 之后添加了 take(5)(因为我想要 LIMIT 5)并且它起作用并且只显示了 5 个项目。然后我添加了 .filter(,键入 new 并让 Android Studio 生成 Func1 代码,它看起来像:
subscription = db.createQuery(ListItem.TABLE, LIST_QUERY)
.mapToList(Item.MAPPER)
.filter(new Func1<List<ListItem>, Boolean>() {
@Override
public Boolean call(List<ListItem> items) {
return null;
}
})
所以问题是它要求我过滤整个列表。我试过这个:
public Boolean call(List<ListItem> items) {
for(ListItem i: items)
if(!i.description().startsWith(query))
items.remove(i);
return true;
}
但仍然显示了整个列表,并且文本的更改根本没有使列表发生变化。我相信我错过了一些东西,我必须订阅 EditText 但我不确定我将如何做到这一点并将它与现有的数据库订阅代码结合起来。寻找解决方案。
有几种不同的方法可以做到这一点。如果您希望过滤器存在于 java 中,那么您可以在 QueryObservable
:
asRows
运算符
database.createQuery(ListItem.TABLE, ListItem.QUERY)
.flatMap(query -> query.asRows(Item.MAPPER)
.filter(item -> item.description.startsWith(query))
.toList())
这将是一个 Observable<List<Item>>
,其中仅包含与查询匹配的项目。
但是您最初的方法是最理想的方法,您只是错误地使用了 LIKE
。您可能希望包含通配符(%
或 _
)以包含与查询不完全匹配的结果。例如:
"SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \"%" + query + "%\" LIMIT 5”;
将匹配描述中包含查询的任何结果。 %
通配符匹配 0 个或多个任意字符。 _
通配符匹配单个字符。
您提到的另一部分是列表未正确更新。你想在这里使用 RxAndroid 而不是 textChangedListeners 最有可能。在你看来,你会有类似
的东西RxTextView.textChanges(editText)
.switchMap(query -> database.createQuery(ListItem.TABLE, ListItem.query(query))
.mapToList(ListItem.MAPPER))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
adapter.setData(list);
adapter.notifyDataSetChanged();
});
这样您就不必担心查询执行和适配器获得通知调用之间的任何竞争。 ListItem.query(query)
方法只是将 sqlite 字符串与查询组合在一起。