38 votes

Filtre / recherche de bibliothèque de pagination

J'utilise la bibliothèque de radiomessagerie Android comme décrit ici: https://developer.android.com/topic/libraries/architecture/paging.html

Mais j'ai aussi un EditText pour rechercher des utilisateurs par nom.

Comment puis-je filtrer les résultats de la bibliothèque de pagination pour afficher uniquement les utilisateurs correspondants?

42voto

Zhuinden Points 3074

EDIT partir de 2019: tenez-vous bien, je pense que vous pourriez être en mesure de résoudre ce avec un MediatorLiveData.

Plus précisément Transformations.switchMap et certains magiques supplémentaires.

Actuellement, j'ai été en utilisant

public void reloadTasks() {
    if(liveResults != null) {
        liveResults.removeObserver(this);
    }
    liveResults = getFilteredResults();
    liveResults.observeForever(this);
}

Mais si vous pensez cela, vous devriez être en mesure de résoudre ce sans utiliser d' observeForever, surtout si l'on considère que l' switchMap , c'est faire quelque chose de similaire.

Donc, ce que nous avons besoin est un LiveData<SelectedOption> c'est-commutateur-mappé à l' LiveData<PagedList<T>> que nous avons besoin.

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

De cette façon, le réel de l'un à l'autre, sont traitées par un MediatorLiveData. Si nous voulons mettre en cache les LiveDatas, alors nous pouvons faire dans l'anonymat de l'instance que l'on passe à la méthode.

    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) {
                    // ...
                }
            }



Réponse d'ORIGINE (ils sont dépassées)

EDIT: en fait. Tout cela a un sens pour un régulier LiveData<?>, avec Pagination, vous pouvez paramétrer l'Usine, puis invalider la source de données et d'obtenir une nouvelle source de données évaluées pour gratuit. Sans recréer de la requête du titulaire lui-même.

Donc, la méthode mentionnée dans l'autre réponse est une meilleure option si vous êtes à l'aide de la Pagination.


RÉPONSE ORIGINALE À CETTE QUESTION:

Vous savez comment vous avez l'adaptateur comme ceci:

public class TaskAdapter
        extends PagedListAdapter<Task, TaskAdapter.ViewHolder> {
    public TaskAdapter() {
        super(Task.DIFF_ITEM_CALLBACK);
    }

Dans le ViewModel vous définissez une liste paginée et de l'exposer:

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

Puis vous observez la liste paginée dans le ViewModel et mis à la carte:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    viewModel.getTasks().observe(this, pagedList -> {
        //noinspection Convert2MethodRef
        taskAdapter.submitList(pagedList); //used to be `setList`
    });

Eh bien, la chose la plus délicate, c'est que si vous voulez faire paramétrique, alors vous devez remplacer celui-ci ici, et de rendre la vue à être en mesure de l'observer:

    liveResults = new LivePagedListBuilder<>(userDao.usersByName(input) // <-- !!

Si vous devez remplacer la LiveData. o_o

Ce que vous pouvez faire dans ce cas est de supprimer les observateurs de l'existant LiveData, la remplacer par la nouvelle LiveData, et de commencer à l'observer.

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

et

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

25voto

Deividas Strioga Points 1139

J'ai utilisé une approche similaire à comme répondu par EpicPandaForce. Alors qu'il est au travail, cette abonnement/désabonnement semble fastidieux. J'ai commencé à utiliser une autre base de données de la Chambre, j'ai donc besoin de créer ma propre source de données.Usine de toute façon. Apparemment, il est possible d'invalider un courant de la source de données et de la source de données.Factory crée une nouvelle source de données, qui est l'endroit où je utiliser les paramètres de recherche.

Ma Source De Données.Usine:

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

Je suis à l'aide de ObjectBox ici, mais vous pouvez simplement retourner votre salle de DAO requête sur créer (je suppose que c'est déjà un DataSourceFactory, fait appel à son propre créer).

Je n'ai pas tester, mais cela peut fonctionner:

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

Bien sûr, on peut juste passer une Usine déjà avec la requête de dao.

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

Toutefois, la requête de recherche est reçu, il suffit d'appeler searchSweets sur ViewModel. Il définit la requête de recherche dans l'Usine, puis annule la source de données. À son tour, créer est appelé dans l'Usine et de la nouvelle instance de la source de données est créé avec la nouvelle requête et transmis à l'existant, LiveData sous le capot..

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X