android - 使用 RxAndroid 过滤数据库项目的正确方法

标签 android rx-java rx-android sqlbrite

我正在使用 the sample提供并根据我的要求调整了代码。我正在学习 Rx,但并不完全熟悉它的工作原理。所以,在我的 fragment 中我有

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);
}

适配器在 ListView 中显示数据并且工作正常,但现在我在列表顶部添加了一个 TextView 并希望在用户输入文本时过滤结果。我相信 RxAndroid 提供了一个 debounce 运算符来提高效率(而不是立即查询数据库),但我不确定如何组合这个订阅代码来监听 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 }
    });

这导致列表为空(我认为这没问题,因为初始查询是空字符串)但是当我输入一些文本时,查询根本没有更新/过滤列表。

我尝试了另一种方法并在 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 中,那么您可以使用 asRows QueryObservable 上的接线员:

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 字符串与查询组合起来。

关于android - 使用 RxAndroid 过滤数据库项目的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42113931/

相关文章:

java - 安卓编程。振动/LED灯功能

scala infinite rx observable creation - 如何正确地做到这一点?

android - RxJava : Reduce or Combine or Merge

android - RxAndroid 可观察循环

Android + RxJava - 从数据库和网络服务加载数据

java - 将值从 onResponse 传递到 onPostExecute 不断变为 null

android - 更改 View 寻呼机高度以将内容与 fragment 的内容高度一起换行

java - 跨 JVM 分配订阅者

android - 错误转换 observable.subscribe

android - 如何正确设计/设计 Android 抽屉导航