有了android Paging库,从数据库中分块加载数据确实很容易,ViewModel提供了自动UI更新和数据保存功能。所有这些框架模块都可以帮助我们在android平台上创建出色的应用。
典型的android应用必须显示项目列表,并允许用户搜索该列表。这就是我想通过我的应用程序实现的目标。因此,我通过阅读许多文档,教程甚至stackoverflow答案来完成实现。 但是我不确定我是正确执行还是应该执行该操作。因此,在下面,我展示了使用ViewModel和RecyclerView实现分页库的方式。
请检查我的实现并纠正我的错误之处,或者告诉我应该怎么做。我认为,像我这样的许多新的android开发人员仍对如何正确执行感到困惑,因为没有单一来源可以回答您有关此类实现的所有问题。
我只展示我认为重要的内容。我正在使用Room。这是我正在使用的实体。
@Entity(tableName = "event")
public class Event {
@PrimaryKey(autoGenerate = true)
public int id;
public String title;
}
这里是事件实体的DAO。
@Dao
public interface EventDao {
@Query("SELECT * FROM event WHERE event.title LIKE :searchTerm")
DataSource.Factory<Integer, Event> getFilteredEvent(String searchTerm);
}
这里是 ViewModel 扩展了 AndroidViewModel ,它允许通过提供所有事件或已过滤事件的 LiveData
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中,我编写了以下代码来搜索或显示未提供搜索词(表示搜索已完成或取消)时的所有事件。
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;
}
答案 0 :(得分:5)
感谢George Machibya出色的answer。但我喜欢对其做如下修改:
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();
}
});
DebouncedLiveData
类开发为波纹管,并从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);
}
});
}
}
然后像下面一样使用它:
listAllFood = Transformations.switchMap(new DebouncedLiveData<>(filterFoodName, 400), input -> {
...
});
TextWatcher
,您可以将TextView直接绑定到viewModel。顺便说一句,我修改了George Machibya解决方案并将其推送到我的Github中。有关更多详细信息,您可以查看它here。
答案 1 :(得分:4)
我将强烈建议您开始使用RxJava,您可以简化整个搜索逻辑的问题。
我建议在Dao Room Class中实现两种方法,一种是在搜索为空时查询所有数据,另一种是按以下方式查询搜索到的项目。数据源用于在页面列表中加载数据
@Query("SELECT * FROM food order by food_name")
DataSource.Factory<Integer, Food> loadAllFood();
@Query("SELECT * FROM food where food_name LIKE :name order by food_name")
DataSource.Factory<Integer, Food> loadAllFoodFromSearch(String name);
在ViewModel类中,我们需要两个参数,其中一个将用于观察搜索到的文本,而我们使用MutableLiveData来在OnChange期间通知视图。然后用LiveData观察Items列表并更新UI。 SwitchMap应用接受输入LiveData并生成相应LiveData输出的功能。请找到以下代码
public LiveData<PagedList<Food>> listAllFood;
public MutableLiveData<String> filterFoodName = new MutableLiveData<>();
public void initialFood(final FoodDao foodDao) {
this.foodDao = foodDao;
PagedList.Config config = (new PagedList.Config.Builder())
.setPageSize(10)
.build();
listAllFood = Transformations.switchMap(filterFoodName, outputLive -> {
if (outputLive == null || outputLive.equals("") || input.equals("%%")) {
//check if the current value is empty load all data else search
return new LivePagedListBuilder<>(
foodDao.loadAllFood(), config)
.build();
} else {
return new LivePagedListBuilder<>(
foodDao.loadAllFoodFromSearch(input),config)
.build();
}
});
}
然后,viewModel将把LiveData传播到Views并观察数据的变化。然后在MainActivity中,调用将使用SwitchMap函数的initialFood方法。
viewModel = ViewModelProviders.of(this).get(FoodViewModel.class);
viewModel.initialFood(FoodDatabase.getINSTANCE(this).foodDao());
viewModel.listAllFood.observe(this, foodlistPaging -> {
try {
Log.d(LOG_TAG, "list of all page number " + foodlistPaging.size());
foodsactivity = foodlistPaging;
adapter.submitList(foodlistPaging);
} catch (Exception e) {
}
});
recyclerView.setAdapter(adapter);
对于第一个onCreate,将filterFoodName初始化为Null,以便检索所有项目。 viewModel.filterFoodName.setValue(“”);
然后将TextChangeListener应用于EditText并调用MutableLiveData,它将观察Change并使用搜索到的Item更新UI。
searchFood.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i,
int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int
i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
//just set the current value to search.
viewModel.filterFoodName.
setValue("%" + editable.toString() + "%");
}
});
}
下面是我的完整代码的github存储库。
https://github.com/muchbeer/PagingSearchFood
希望有帮助