Ten przewodnik stanowi uzupełnienie przeglądu biblioteki na potrzeby wczytywania i opisuje, jak dostosować rozwiązanie do wczytywania danych w aplikacji pod kątem jej architektury.
Utwórz listę możliwą do obserwowania
Zwykle kod interfejsu obserwuje obiekt LiveData<PagedList>
(lub, jeśli używasz RxJava2, obiekt Flowable<PagedList>
lub Observable<PagedList>
), który znajduje się w elemencie ViewModel
aplikacji. Ten możliwy do obserwowania obiekt stanowi połączenie między prezentacją a zawartością danych listy aplikacji.
Aby utworzyć jeden z tych obserwowalnych obiektów PagedList
, przekaż wystąpienie DataSource.Factory
do obiektu LivePagedListBuilder
lub RxPagedListBuilder
. Obiekt DataSource
wczytuje strony dla pojedynczego elementu PagedList
. Klasa fabryczna tworzy nowe instancje PagedList
w odpowiedzi na aktualizacje treści, takie jak unieważnienia tabel bazy danych i odświeżenia sieci. Biblioteka trwałości sal może dostarczać obiekty DataSource.Factory
za Ciebie lub możesz utworzyć własne.
Ten fragment kodu pokazuje, jak utworzyć nowe wystąpienie LiveData<PagedList>
w klasie ViewModel
aplikacji za pomocą funkcji budowania DataSource.Factory
pokoju:
Kotlin
@Dao interface ConcertDao { // The Int type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") fun concertsByDate(): DataSource.Factory<Int, Concert> }
Java
@Dao public interface ConcertDao { // The Integer type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") DataSource.Factory<Integer, Concert> concertsByDate(); }
Kotlin
// The Int type argument corresponds to a PositionalDataSource object. val myConcertDataSource : DataSource.Factory<Int, Concert> = concertDao.concertsByDate() val concertList = myConcertDataSource.toLiveData(pageSize = 50)
Java
// The Integer type argument corresponds to a PositionalDataSource object. DataSource.Factory<Integer, Concert> myConcertDataSource = concertDao.concertsByDate(); LiveData<PagedList<Concert>> concertList = LivePagedListBuilder(myConcertDataSource, /* page size */ 50).build();
Zdefiniuj własną konfigurację stronicowania
Aby dokładniej skonfigurować LiveData<PagedList>
na potrzeby zaawansowanych przypadków, możesz też zdefiniować własną konfigurację stronicowania. Możesz zdefiniować takie atrybuty:
- Rozmiar strony: liczba elementów na każdej stronie.
- Odległość pobierania z wyprzedzeniem: biorąc pod uwagę ostatni element widoczny w interfejsie aplikacji, liczba elementów poza tym ostatnim elementem, które biblioteka stronicowania powinna próbować pobrać z wyprzedzeniem. Ta wartość powinna być kilkakrotnie większa od rozmiaru strony.
- Obecność obiektu zastępczego: określa, czy interfejs użytkownika wyświetla obiekty zastępcze elementów listy, które nie zostały jeszcze wczytane. Aby poznać zalety i wady używania obiektów zastępczych, dowiedz się, jak postawić je w interfejsie.
Jeśli chcesz mieć większą kontrolę nad tym, kiedy biblioteka stronicowania wczytuje listę z bazy danych aplikacji, przekaż do LivePagedListBuilder
niestandardowy obiekt Executor
, jak pokazano w tym fragmencie kodu:
Kotlin
val myPagingConfig = Config( pageSize = 50, prefetchDistance = 150, enablePlaceholders = true ) // The Int type argument corresponds to a PositionalDataSource object. val myConcertDataSource : DataSource.Factory<Int, Concert> = concertDao.concertsByDate() val concertList = myConcertDataSource.toLiveData( pagingConfig = myPagingConfig, fetchExecutor = myExecutor )
Java
PagedList.Config myPagingConfig = new PagedList.Config.Builder() .setPageSize(50) .setPrefetchDistance(150) .setEnablePlaceholders(true) .build(); // The Integer type argument corresponds to a PositionalDataSource object. DataSource.Factory<Integer, Concert> myConcertDataSource = concertDao.concertsByDate(); LiveData<PagedList<Concert>> concertList = new LivePagedListBuilder<>(myConcertDataSource, myPagingConfig) .setFetchExecutor(myExecutor) .build();
Wybierz prawidłowy typ źródła danych
Ważne jest, aby połączyć się ze źródłem danych, które najlepiej obsługuje strukturę danych źródłowych:
- Użyj
PageKeyedDataSource
, jeśli strony wczytujesz poprzedni/następny klucz umieszczony na stronie. Jeśli na przykład pobierasz z sieci posty w mediach społecznościowych, może być konieczne przekazanie tokenanextPage
podczas jednego ładowania do kolejnego. - Użyj operatora
ItemKeyedDataSource
, jeśli chcesz użyć danych z elementu N do pobrania elementu N+1. Jeśli na przykład pobierasz komentarze w wątkach do aplikacji do obsługi dyskusji, to aby pobrać treść następnego komentarza, konieczne może być podanie identyfikatora ostatniego komentarza. - Użyj metody
PositionalDataSource
, jeśli chcesz pobierać strony z danymi z dowolnej lokalizacji wybranej w magazynie danych. Ta klasa obsługuje żądania zbioru elementów danych rozpoczynające się od wybranej lokalizacji. Żądanie może na przykład zwrócić 50 elementów danych, których lokalizacja zaczyna się od lokalizacji 1500.
Powiadamiaj, gdy dane są nieprawidłowe
Gdy używasz biblioteki stronicowania, to warstwa danych powiadamia inne warstwy aplikacji, gdy tabela lub wiersz stają się nieaktualne. Aby to zrobić, wywołaj invalidate()
z klasy DataSource
wybranej dla aplikacji.
Tworzenie własnych źródeł danych
Jeśli korzystasz z niestandardowego rozwiązania do obsługi danych lokalnych lub wczytujesz dane bezpośrednio z sieci, możesz wdrożyć jedną z podklas DataSource
. Ten fragment kodu pokazuje źródło danych powiązane z godziną rozpoczęcia danego koncertu:
Kotlin
class ConcertTimeDataSource() : ItemKeyedDataSource<Date, Concert>() { override fun getKey(item: Concert) = item.startTime override fun loadInitial( params: LoadInitialParams<Date>, callback: LoadInitialCallback<Concert>) { val items = fetchItems(params.requestedInitialKey, params.requestedLoadSize) callback.onResult(items) } override fun loadAfter( params: LoadParams<Date>, callback: LoadCallback<Concert>) { val items = fetchItemsAfter( date = params.key, limit = params.requestedLoadSize) callback.onResult(items) } }
Java
public class ConcertTimeDataSource extends ItemKeyedDataSource<Date, Concert> { @NonNull @Override public Date getKey(@NonNull Concert item) { return item.getStartTime(); } @Override public void loadInitial(@NonNull LoadInitialParams<Date> params, @NonNull LoadInitialCallback<Concert> callback) { List<Concert> items = fetchItems(params.key, params.requestedLoadSize); callback.onResult(items); } @Override public void loadAfter(@NonNull LoadParams<Date> params, @NonNull LoadCallback<Concert> callback) { List<Concert> items = fetchItemsAfter(params.key, params.requestedLoadSize); callback.onResult(items); }
Następnie możesz wczytać te dostosowane dane do obiektów PagedList
, tworząc konkretną podklasę DataSource.Factory
. Ten fragment kodu pokazuje, jak generować nowe wystąpienia niestandardowego źródła danych zdefiniowanego w poprzednim fragmencie kodu:
Kotlin
class ConcertTimeDataSourceFactory : DataSource.Factory<Date, Concert>() { val sourceLiveData = MutableLiveData<ConcertTimeDataSource>() var latestSource: ConcertDataSource? override fun create(): DataSource<Date, Concert> { latestSource = ConcertTimeDataSource() sourceLiveData.postValue(latestSource) return latestSource } }
Java
public class ConcertTimeDataSourceFactory extends DataSource.Factory<Date, Concert> { private MutableLiveData<ConcertTimeDataSource> sourceLiveData = new MutableLiveData<>(); private ConcertDataSource latestSource; @Override public DataSource<Date, Concert> create() { latestSource = new ConcertTimeDataSource(); sourceLiveData.postValue(latestSource); return latestSource; } }
Jak działają aktualizacje treści
Tworząc dostrzegalne obiekty PagedList
, zastanów się, jak działają aktualizacje treści. Jeśli wczytujesz dane bezpośrednio z bazy danych sal, są one automatycznie przekazywane do interfejsu aplikacji.
Gdy korzystasz z interfejsu API z obsługą stron, zwykle dochodzi do interakcji użytkownika, na przykład „przesuń palcem, aby odświeżyć”, co wskazuje na unieważnienie ostatnio używanego interfejsu DataSource
. Następnie poproś o nową instancję tego źródła danych. Ten fragment kodu ilustruje odpowiednie działanie:
Kotlin
class ConcertActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // ... concertTimeViewModel.refreshState.observe(this, Observer { // Shows one possible way of triggering a refresh operation. swipeRefreshLayout.isRefreshing = it == MyNetworkState.LOADING }) swipeRefreshLayout.setOnRefreshListener { concertTimeViewModel.invalidateDataSource() } } } class ConcertTimeViewModel(firstConcertStartTime: Date) : ViewModel() { val dataSourceFactory = ConcertTimeDataSourceFactory(firstConcertStartTime) val concertList: LiveData<PagedList<Concert>> = dataSourceFactory.toLiveData( pageSize = 50, fetchExecutor = myExecutor ) fun invalidateDataSource() = dataSourceFactory.sourceLiveData.value?.invalidate() }
Java
public class ConcertActivity extends AppCompatActivity { @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... viewModel.getRefreshState() .observe(this, new Observer<NetworkState>() { // Shows one possible way of triggering a refresh operation. @Override public void onChanged(@Nullable MyNetworkState networkState) { swipeRefreshLayout.isRefreshing = networkState == MyNetworkState.LOADING; } }; swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshListener() { @Override public void onRefresh() { viewModel.invalidateDataSource(); } }); } } public class ConcertTimeViewModel extends ViewModel { private LiveData<PagedList<Concert>> concertList; private DataSource<Date, Concert> mostRecentDataSource; public ConcertTimeViewModel(Date firstConcertStartTime) { ConcertTimeDataSourceFactory dataSourceFactory = new ConcertTimeDataSourceFactory(firstConcertStartTime); mostRecentDataSource = dataSourceFactory.create(); concertList = new LivePagedListBuilder<>(dataSourceFactory, 50) .setFetchExecutor(myExecutor) .build(); } public void invalidateDataSource() { mostRecentDataSource.invalidate(); } }
Udostępnij mapowanie danych
Biblioteka stronicowania obsługuje przekształcenia na podstawie elementów i stron w przypadku elementów wczytywanych przez DataSource
.
W tym fragmencie kodu połączenie nazwy i daty koncertu jest mapowane na pojedynczy ciąg znaków zawierający zarówno nazwę, jak i datę:
Kotlin
class ConcertViewModel : ViewModel() { val concertDescriptions : LiveData<PagedList<String>> init { val concerts = database.allConcertsFactory() .map { "${it.name} - ${it.date}" } .toLiveData(pageSize = 50) } }
Java
public class ConcertViewModel extends ViewModel { private LiveData<PagedList<String>> concertDescriptions; public ConcertViewModel(MyDatabase database) { DataSource.Factory<Integer, Concert> factory = database.allConcertsFactory().map(concert -> concert.getName() + "-" + concert.getDate()); concertDescriptions = new LivePagedListBuilder<>( factory, /* page size */ 50).build(); } }
Jest to przydatne, jeśli chcesz pakować, przekonwertować lub przygotować elementy po ich wczytaniu. Ponieważ ta praca jest wykonywane na wykonawcy pobierania, możesz wykonać potencjalnie kosztowną pracę, taką jak odczyt z dysku lub wysyłanie zapytania do innej bazy danych.
Prześlij opinię
Podziel się z nami swoją opinią i pomysłami, korzystając z tych zasobów:
- Śledzenie problemów
- Zgłoś problemy, żebyśmy mogli naprawić błędy.
Dodatkowe materiały
Więcej informacji o bibliotece stronicowania znajdziesz w tych materiałach:
Próbki
Ćwiczenia z programowania
Filmy
- Android Jetpack: zarządzaj nieskończonymi listami za pomocą RecyclerView i Paging (Google I/O 2018)
- Android Jetpack: pager
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy JavaScript jest wyłączony
- Migrate to Paging 3 (Migracja do strony 3)
- Omówienie biblioteki na etapie 2
- Wyświetlanie list z podziałem na strony