简体   繁体   English

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

[英]Proper way to filter database items using RxAndroid

I'm using the sample provided and have tweaked the code to my requirement. 我正在使用提供的示例 ,并根据我的要求调整了代码。 I'm learning Rx and not completely familiar with how it works. 我正在学习Rx,并不完全熟悉它是如何工作的。 So, in my fragment I have 所以,在我的片段中,我有

private static String LIST_QUERY = "SELECT * FROM " + TodoItem.TABLE;

And my onResume looks like: 我的onResume看起来像:

@Override
public void onResume() {
    super.onResume();

    subscription = db.createQuery(TodoItem.TABLE, LIST_QUERY)
            .mapToList(TodoItem.MAPPER)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(adapter);
}

The adapter showed the data in a listview and it works fine but now I have added a TextView on top of the list and want to filter results whenever the user enters text. 适配器在列表视图中显示数据并且它工作正常,但现在我在列表顶部添加了一个TextView,并希望在用户输入文本时过滤结果。 I believe RxAndroid provides a debounce operator to make this efficient (and not query the DB instantly) but I'm unsure how I can combine this subscription code to listen to the textView changes and filter according to that text. 我相信RxAndroid提供了一个debounce运算符来提高效率(而不是立即查询数据库),但我不确定如何结合此订阅代码来收听textView更改并根据该文本进行过滤。

I already tried changing LIST_QUERY to "SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \\"" + query + "\\" LIMIT 5”; 我已经尝试将LIST_QUERY更改为"SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \\"" + query + "\\" LIMIT 5”;

and then in tried: 然后尝试:

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

This resulted in the list being empty (I thought this was okay since the initial query is empty string) but when I typed some text the query didn't update/filter the list at all. 这导致列表为空(我认为这是好的,因为初始查询是空字符串)但是当我输入一些文本时,查询根本没有更新/过滤列表。

I tried an alternate approach and added take(5) (since I want LIMIT 5) after mapToList and that worked and showed only 5 items. 我尝试了另一种方法,并在mapToList之后添加了take(5)(因为我想要LIMIT 5),并且只显示了5个项目。 And then I added .filter(, typed new and let Android Studio generate the Func1 code and it looked like: 然后我添加了.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;
                }
            })

So problem is that it asks me to filter the whole list. 所以问题是它要求我过滤整个列表。 I tried this: 我试过这个:

public Boolean call(List<ListItem> items) {
   for(ListItem i: items)
      if(!i.description().startsWith(query))
          items.remove(i);
   return true;
}

But still the entire list shows and the changes in the text do not make the list change at all. 但仍然显示整个列表,并且文本中的更改不会使列表发生变化。 I believe I missed something, that I have to subscribe to the EditText but I'm not sure how I would do this and combine it with this existing database subscribe code. 我相信我错过了一些东西,我必须订阅EditText,但我不知道如何做到这一点并将它与现有的数据库订阅代码结合起来。 Looking for solutions. 寻找解决方案。

There's a few different ways you can do this. 有几种不同的方法可以做到这一点。 If you want the filter to exist in java then you can use the asRows operator on QueryObservable : 如果你想过滤器在Java中存在,那么你可以使用asRows运营商QueryObservable

database.createQuery(ListItem.TABLE, ListItem.QUERY)
  .flatMap(query -> query.asRows(Item.MAPPER)
    .filter(item -> item.description.startsWith(query))
    .toList())

This will be an Observable<List<Item>> with only items that match the query. 这将是一个Observable<List<Item>> ,只包含与查询匹配的项目。

However your original approach is the ideal way to do it, you were just using LIKE incorrectly. 但是,您的原始方法是理想的方法,您只是错误地使用LIKE You probably want to include wildcards ( % or _ ) to include results that dont match the query exactly. 您可能希望包含通配符( %_ )以包含与查询不完全匹配的结果。 For example: 例如:

"SELECT * FROM " + TodoItem.TABLE + " WHERE " + TodoItem.DESCRIPTION + " LIKE \"%" + query + "%\" LIMIT 5”;

will match any results where the query is contained in the description. 将匹配描述中包含查询的任何结果。 The % wildcard matches 0 or more of any character. %通配符匹配任何字符的0或更多。 The _ wildcard matches a single character. _通配符匹配单个字符。

The other part you mentioned is that the list isn't updating properly. 您提到的另一部分是列表未正确更新。 You want to use RxAndroid here and not textChangedListeners most likely. 你想在这里使用RxAndroid而不是textChangedListeners。 In your view you will have something like 在你看来,你会有类似的东西

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

This way you're not worrying about any races between the query executing and the adapter getting the notify call. 这样,您就不必担心执行查询和获取通知调用的适配器之间的任何竞争。 The ListItem.query(query) method just composes that sqlite string with the query. ListItem.query(query)方法只是用查询编写sqlite字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM