ここで説明されているようなAndroidページングライブラリを使用しています: https://developer.Android.com/topic/libraries/architecture/paging.html
しかし、名前でユーザーを検索するためのEditTextもあります。
一致するユーザーのみを表示するために、ページングライブラリからの結果をフィルター処理するにはどうすればよいですか?
2019年以降の編集:お待ちください、MediatorLiveDataでこれを解決できるかもしれません。
特にTransformations.switchMap
といくつかの追加の魔法。
現在使用していた
public void reloadTasks() {
if(liveResults != null) {
liveResults.removeObserver(this);
}
liveResults = getFilteredResults();
liveResults.observeForever(this);
}
しかし、考えてみると、observeForever
を使用せずにこれを解決できるはずです。特にswitchMap
も同様のことをしていると考える場合はそうです。
必要なのは、必要なLiveData<SelectedOption>
にスイッチマッピングされるLiveData<PagedList<T>>
です。
private MutableLiveData<String> filterText = new MutableLiveData<>();
private final LiveData<List<T>> data;
public MyViewModel() {
data = Transformations.switchMap(
filterText,
(input) -> {
if(input == null || input.equals("")) {
return repository.getData();
} else {
return repository.getFilteredData(input); }
}
});
}
public LiveData<List<T>> getData() {
return data;
}
このようにして、あるものから別のものへの実際の変更はMediatorLiveDataによって処理されます。 LiveDataをキャッシュする場合は、メソッドに渡す匿名インスタンスで実行できます。
data = Transformations.switchMap(
filterText, new Function<String, LiveData<List<T>>>() {
private Map<String, LiveData<List<T>>> cachedLiveData = new HashMap<>();
@Override
public LiveData<List<T>> apply(String input) {
// ...
}
}
元の回答(古い)
編集:実際。これは通常のLiveData<?>
には意味がありますが、ページングでは、実際にファクトリをパラメーター化し、データソースを無効にして、新しいデータソースを無料で評価できます。クエリホルダー自体を再作成せずに。
したがって、ページングを使用している場合は、他の回答で言及されている方法がより良いオプションです。
元の回答:
次のようなアダプタをどのように持っているか知っています。
public class TaskAdapter
extends PagedListAdapter<Task, TaskAdapter.ViewHolder> {
public TaskAdapter() {
super(Task.DIFF_ITEM_CALLBACK);
}
ViewModelで、ライブページリストを設定して公開します。
private LiveData<PagedList<Task>> liveResults;
public TaskViewModel() {
liveResults = new LivePagedListBuilder<>(taskDao.tasksSortedByDate(),
new PagedList.Config.Builder() //
.setPageSize(20) //
.setPrefetchDistance(20) //
.setEnablePlaceholders(true) //
.build())
.setInitialLoadKey(0)
.build();
次に、ViewModelのページリストを確認し、アダプターに設定します。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
viewModel.getTasks().observe(this, pagedList -> {
//noinspection Convert2MethodRef
taskAdapter.submitList(pagedList); //used to be `setList`
});
まあ、トリッキーなことは、パラメトリックにしたい場合は、ここで次のものを置き換え、ビューでそれを観察できるようにする必要があるということです。
liveResults = new LivePagedListBuilder<>(userDao.usersByName(input) // <-- !!
したがって、LiveDataを置き換える必要があります。 o_o
その場合にできることは、既存のLiveDataからオブザーバーを削除し、新しいLiveDataに置き換えて、監視を開始することです。
private void startListening() {
viewModel.getTasks().observe(this, pagedList -> {
//noinspection Convert2MethodRef
taskAdapter.submitList(pagedList); // used to be `setList`
});
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
startListening();
}
@OnTextChanged(R.id.edit_text)
public void onTextChanged(Editable editable) {
String username = editable.toString();
replaceSubscription(userName);
}
private void replaceSubscription(String userName) {
viewModel.replaceSubscription(this, userName);
startListening();
}
そして
public class UserViewModel extends ViewModel {
private LiveData<PagedList<User>> liveResults;
private String userName;
private LiveData<PagedList<User>> createFilteredUsers(String userName) {
// TODO: handle if `null` and load all data instead
return new LivePagedListBuilder<>(userDao.usersByName(userName),
new PagedList.Config.Builder() //
.setPageSize(20) //
.setPrefetchDistance(20) //
.setEnablePlaceholders(true) //
.build())
.setInitialLoadKey(0)
.build();
}
public UserViewModel(UserDao userDao, @Nullable String userName) { // null or restored, from ViewModelProviders.of(Factory)
liveResults = createFilteredUsers(userName);
}
public void replaceSubscription(LifecycleOwner lifecycleOwner, String userName) {
this.userName = userName;
liveResults.removeObservers(lifecycleOwner);
liveResults = createFilteredUsers(userName);
}
}
私は、EpicPandaForceが答えたのと同様のアプローチを使用しました。動作している間、このサブスクライブ/サブスクライブ解除は退屈に思えます。 Roomとは別のDBを使い始めたので、とにかく独自のDataSource.Factoryを作成する必要がありました。どうやら現在のDataSourceを無効化することが可能で、DataSource.Factoryは新しいDataSourceを作成します。ここで検索パラメーターを使用します。
私のDataSource.Factory:
class SweetSearchDataSourceFactory(private val box: Box<SweetDb>) :
DataSource.Factory<Int, SweetUi>() {
var query = ""
override fun create(): DataSource<Int, SweetUi> {
val lazyList = box.query().contains(SweetDb_.name, query).build().findLazyCached()
return SweetSearchDataSource(lazyList).map { SweetUi(it) }
}
fun search(text: String) {
query = text
}
}
ここではObjectBoxを使用していますが、作成時にルームDAOクエリを返すことができます(既にDataSourceFactoryであるため、独自のcreateを呼び出します)。
私はそれをテストしませんでしたが、これはうまくいくかもしれません:
class SweetSearchDataSourceFactory(private val dao: SweetsDao) :
DataSource.Factory<Int, SweetUi>() {
var query = ""
override fun create(): DataSource<Int, SweetUi> {
return dao.searchSweets(query).map { SweetUi(it) }.create()
}
fun search(text: String) {
query = text
}
}
もちろん、daoからのクエリで既にFactoryを渡すことができます。
ViewModel:
class SweetsSearchListViewModel
@Inject constructor(
private val dataSourceFactory: SweetSearchDataSourceFactory
) : BaseViewModel() {
companion object {
private const val INITIAL_LOAD_KEY = 0
private const val PAGE_SIZE = 10
private const val PREFETCH_DISTANCE = 20
}
lateinit var sweets: LiveData<PagedList<SweetUi>>
init {
val config = PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setPrefetchDistance(PREFETCH_DISTANCE)
.setEnablePlaceholders(true)
.build()
sweets = LivePagedListBuilder(dataSourceFactory, config).build()
}
fun searchSweets(text: String) {
dataSourceFactory.search(text)
sweets.value?.dataSource?.invalidate()
}
}
ただし、検索クエリを受信した場合は、ViewModelでsearchSweetsを呼び出すだけです。 Factoryで検索クエリを設定し、DataSourceを無効にします。次に、ファクトリでcreateが呼び出され、DataSourceの新しいインスタンスが新しいクエリで作成され、内部の既存のLiveDataに渡されます。