通过观察ViewModel在RecyclerView中搜索PagedList的LiveData [英] Searching a LiveData of PagedList in RecyclerView by Observing ViewModel

查看:132
本文介绍了通过观察ViewModel在RecyclerView中搜索PagedList的LiveData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有了android Paging库,从数据库中分块加载数据确实很容易,ViewModel提供了自动UI更新和数据保存功能。所有这些框架模块都可以帮助我们在android平台上创建出色的应用。

With android Paging library it is really easy to load data from Database in chunks and ViewModel provides automatic UI update and data survival. All these frameworks modules help us create a great app in android platform.

一个典型的android应用必须显示项目列表,并允许用户搜索该列表。这就是我想通过我的应用程序实现的目标。因此,我通过阅读许多文档,教程甚至stackoverflow答案来完成实现。 但是我不确定我是正确执行还是应该执行该操作。因此,在下面,我展示了使用ViewModel和RecyclerView实现分页库的方式。

A typical android app has to show a list of items and allows user to search that list. And this what I want to achieve with my app. So I have done an implementation by reading many documentations, tutorials and even stackoverflow answers. But I am not so sure whether I am doing it correctly or how I supposed to do it. So below, I have shown my way of implementing paging library with ViewModel and RecyclerView.

请检查我的实现并纠正我错了的地方,或者告诉我应该怎么做。我认为有很多像我这样的新android开发人员仍然对如何正确执行感到困惑,因为没有单一来源可以回答您有关此类实现的所有问题。

Please, review my implementation and correct me where I am wrong or show me how I supposed to do it. I think there are many new android developers like me are still confused how to do it correctly as there is no single source to have answers to all your questions on such implementation.

我只展示我认为重要的内容。我正在使用Room。这是我正在使用的实体。

I am only showing what I think is important to show. I am using Room. Here is my Entity that I am working with.

@Entity(tableName = "event")
public class Event {
    @PrimaryKey(autoGenerate = true)
    public int id;

    public String title;
}

这里是事件实体的DAO。

Here is DAO for Event entity.

@Dao
public interface EventDao {
    @Query("SELECT * FROM event WHERE event.title LIKE :searchTerm")
    DataSource.Factory<Integer, Event> getFilteredEvent(String searchTerm);
}

这里是 ViewModel 扩展了 AndroidViewModel ,通过提供 LiveData< PagedList<所有事件或已过滤事件的事件>> (根据搜索文字)。 我真的很挣扎,每当filterEvent发生变化时,我都会创建新的LiveData,它可能是多余的,也可能是坏的。

Here is ViewModel extends AndroidViewModel which allows reading and searching by providing LiveData< PagedList< Event>> of either all events or filtered event according to search text. I am really struggling with the idea that every time when there is a change in filterEvent, I'm creating new LiveData which can be redundant or bad.

private MutableLiveData<Event> filterEvent = new MutableLiveData<>();
private LiveData<PagedList<Event>> data;

private MeDB meDB;

public EventViewModel(Application application) {
    super(application);
    meDB = MeDB.getInstance(application);

    data = Transformations.switchMap(filterEvent, new Function<Event, LiveData<PagedList<Event>>>() {
        @Override
        public LiveData<PagedList<Event>> apply(Event event) {
            if (event == null) {
                // get all the events
                return new LivePagedListBuilder<>(meDB.getEventDao().getAllEvent(), 5).build();
            } else {
                // get events that match the title
                return new LivePagedListBuilder<>(meDB.getEventDao()
                          .getFilteredEvent("%" + event.title + "%"), 5).build();
            }
        }
    });
}

public LiveData<PagedList<Event>> getEvent(Event event) {
    filterEvent.setValue(event);
    return data;
}

对于搜索事件,我使用的是 SearchView 。在onQueryTextChange中,我编写了以下代码来搜索或显示没有提供搜索词的所有事件,这表示搜索已完成或已取消。

For searching event, I am using SearchView. In onQueryTextChange, I wrote the following code to search or to show all the events when no search terms is supplied meaning searching is done or canceled.

Event dumpEvent;

@Override
public boolean onQueryTextChange(String newText) {

    if (newText.equals("") || newText.length() == 0) {
        // show all the events
        viewModel.getEvent(null).observe(this, events -> adapter.submitList(events));
    }

    // don't create more than one object of event; reuse it every time this methods gets called
    if (dumpEvent == null) {
        dumpEvent = new Event(newText, "", -1, -1);
    }

    dumpEvent.title = newText;

    // get event that match search terms
    viewModel.getEvent(dumpEvent).observe(this, events -> adapter.submitList(events));

    return true;
}


推荐答案

感谢乔治·马奇比亚的出色表现 answer 。但是我喜欢对其进行一些修改,如下所示:

Thanks to George Machibya for his great answer. But I prefer to do some modifications on it as bellow:


  1. 在内存中不保留任何过滤数据以使其更快的速度之间存在折衷或每次都加载它们以优化内存。我希望将它们保留在内存中,所以我将部分代码更改为以下代码:



listAllFood = Transformations.switchMap(filterFoodName), input -> {
            if (input == null || input.equals("") || input.equals("%%")) {
                //check if the current value is empty load all data else search
                synchronized (this) {
                    //check data is loaded before or not
                    if (listAllFoodsInDb == null)
                        listAllFoodsInDb = new LivePagedListBuilder<>(
                                foodDao.loadAllFood(), config)
                                .build();
                }
                return listAllFoodsInDb;
            } else {
                return new LivePagedListBuilder<>(
                        foodDao.loadAllFoodFromSearch("%" + input + "%"), config)
                        .build();
            }
        });




  1. 使用去抖器有助于减少查询数量数据库并提高性能。因此,我开发了如下的 DebouncedLiveData 类,并从 filterFoodName 制作了一个去抖动的实时数据。

  1. Having a debouncer helps to reduce number of queries to database and improves performance. So I developed DebouncedLiveData class as bellow and make a debounced livedata from filterFoodName.



public class DebouncedLiveData<T> extends MediatorLiveData<T> {

    private LiveData<T> mSource;
    private int mDuration;
    private Runnable debounceRunnable = new Runnable() {
        @Override
        public void run() {
            DebouncedLiveData.this.postValue(mSource.getValue());
        }
    };
    private Handler handler = new Handler();

    public DebouncedLiveData(LiveData<T> source, int duration) {
        this.mSource = source;
        this.mDuration = duration;

        this.addSource(mSource, new Observer<T>() {
            @Override
            public void onChanged(T t) {
                handler.removeCallbacks(debounceRunnable);
                handler.postDelayed(debounceRunnable, mDuration);
            }
        });
    }
}

然后像下面这样使用它:

And then used it like bellow:

listAllFood = Transformations.switchMap(new DebouncedLiveData<>(filterFoodName, 400), input -> {
...
});




  1. 我通常更喜欢使用 DataBiding 。通过使用两种方式的数据绑定,您不再需要使用 TextWatcher ,您可以将TextView直接绑定到viewModel。

  1. I usually prefer to use DataBiding in android. By using two way Data Binding you don't need to use TextWatcher any more and you can bind your TextView to the viewModel directly.

顺便说一句,我修改了George Machibya解决方案并将其推送到我的Github中。有关更多详细信息,您可以在此处看到。

BTW, I modified George Machibya solution and pushed it in my Github. For more details you can see it here.

这篇关于通过观察ViewModel在RecyclerView中搜索PagedList的LiveData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆