Gần đây mình có tìm hiểu về thư viện Paging – một phần của Android Jetpack. Mặc dù đã có một số resource về cách triển khai thư viện này trong một ứng dụng, nhưng mình đã phải đối mặt với rất nhiều vấn đề và phải tìm hiểu kỹ hơn về nó. Vì vậy mình nghĩ mình sẽ viết về 7 bước cơ bản để triển khai thư viện Paging trong ứng dụng Android.
Thư viện Paging giúp cho việc tải dữ liệu phân trang trong ứng dụng của bạn dễ dàng hơn. Thư viện Paging cũng hỗ trợ cả list dữ liệu có giới hạn lớn, list dữ liệu không bị ràng buộc (ví dụ như cập nhật liên tục các nguồn cấp dữ liệu). Thư viện phân trang dựa trên ý tưởng gửi list dữ liệu tới giao diện người dùng dưới dạng livedata của list đó và được quan sát bởi RecyclerView.Adapter.
Mình đã lựa chọn xây dựng một ứng dụng cung cấp tin tức để test thư viện Paging. Nào chúng ta quay lại với chủ đề chính thôi, 7 bước để cài đặt thư viện.
I. Thêm thư viện Paging vào trong ứng dụng
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | apply plugin: 'com.android.application' android { compileSdkVersion 27 defaultConfig { applicationId "com.an.paginglibrary.sample" minSdkVersion 14 targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } /* * This is just to enable Java 8 in the app */ compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } /* * I prefer using data binding so we need to enable it first */ android { dataBinding { enabled = true } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.android.support:support-v4:27.1.1' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.android.support:design:27.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' /* * Step 1: Add the paging library */ implementation 'android.arch.paging:runtime:1.0.0' /* * Step 2: Adding ViewModel and Lifecycle */ implementation 'android.arch.lifecycle:extensions:1.1.1' /* * Step 3: Adding rxJava to the app */ implementation 'io.reactivex.rxjava2:rxjava:2.1.9' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' /* * Step 4: Adding retrofit to the app */ implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1' /* * Step 5: We would be needing an image loading library. * So we are going to use Picasso */ implementation 'com.squareup.picasso:picasso:2.71828' } |
II. Cài đặt Retrofit để lấy dữ liệu từ api
Tạo 1 interface **RestApi.java ** :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">RestApi</span> <span class="token punctuation">{</span> <span class="token comment">/* * We would be using the below url: * https://newsapi.org/v2/everything?q=movies&apiKey=079dac74a5f94ebdb990ecf61c8854b7&pageSize=20&page=2 * The url has four query parameters. * We would be changing the pageSize and the page */</span> <span class="token annotation punctuation">@GET</span><span class="token punctuation">(</span><span class="token string">"/v2/everything"</span><span class="token punctuation">)</span> <span class="token class-name">Call</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> <span class="token function">fetchFeed</span><span class="token punctuation">(</span><span class="token annotation punctuation">@Query</span><span class="token punctuation">(</span><span class="token string">"q"</span><span class="token punctuation">)</span> <span class="token class-name">String</span> q<span class="token punctuation">,</span> <span class="token annotation punctuation">@Query</span><span class="token punctuation">(</span><span class="token string">"apiKey"</span><span class="token punctuation">)</span> <span class="token class-name">String</span> apiKey<span class="token punctuation">,</span> <span class="token annotation punctuation">@Query</span><span class="token punctuation">(</span><span class="token string">"page"</span><span class="token punctuation">)</span> <span class="token keyword">long</span> page<span class="token punctuation">,</span> <span class="token annotation punctuation">@Query</span><span class="token punctuation">(</span><span class="token string">"pageSize"</span><span class="token punctuation">)</span> <span class="token keyword">int</span> pageSize<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Sau đó tạo class RestApiFactory.java :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">String</span> BASE_URL <span class="token operator">=</span> <span class="token string">"https://newsapi.org"</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">RestApi</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">HttpLoggingInterceptor</span> logging <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpLoggingInterceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logging<span class="token punctuation">.</span><span class="token function">setLevel</span><span class="token punctuation">(</span><span class="token class-name">HttpLoggingInterceptor</span><span class="token punctuation">.</span><span class="token class-name">Level</span><span class="token punctuation">.</span>BODY<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">OkHttpClient</span><span class="token punctuation">.</span><span class="token class-name">Builder</span> httpClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OkHttpClient</span><span class="token punctuation">.</span><span class="token class-name">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> httpClient<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>logging<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Retrofit</span> retrofit <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Retrofit</span><span class="token punctuation">.</span><span class="token class-name">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">baseUrl</span><span class="token punctuation">(</span>BASE_URL<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">addConverterFactory</span><span class="token punctuation">(</span><span class="token class-name">GsonConverterFactory</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">addCallAdapterFactory</span><span class="token punctuation">(</span><span class="token class-name">RxJava2CallAdapterFactory</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">client</span><span class="token punctuation">(</span>httpClient<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> retrofit<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">RestApi</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
III. Cài đặt DataSource
DataSource – Đây là base class để tải dữ liệu, được sử dụng trong phân trang danh sách. DataSource có thể được triển khai bằng cách sử dụng bất kỳ lớp nào trong 3 lớp sau:
PageKeyedDataSource: Chúng ta có thể sử dụng lớp này nếu chúng ta cần tải dữ liệu dựa trên số lượng trang trong DataSource. Ví dụ chúng ta truyền số trang làm tham số truy vấn trong request, số trang sẽ tăng tuần tự cho đến khi tất cả các trang được lấy về và hiển thị.
ItemKeyedDataSource: Bước đầu tiên là định nghĩa khóa, mục đích là để load dữ liệu ở page kế tiếp dựa trên khóa đó. Ví dụ như data class User thì trường id thường sẽ có key là userId. Kích thức của list là không xác định và việc lấy dữ liệu tiếp theo thường phụ thuộc vào id được biết gần đây nhất. Vì vậy nếu chúng ta lấy dữ liệu các mục từ 1 đến 10 trong kết quả đầu tiên thì ItemKeyedDataSource sẽ tự động lấy dữ liệu tiếp theo từ 11-20.
PositionalDataSource: Lớp này sẽ hữu ích cho các nguồn cung cấp danh sách có kích thước cố định, có thể lấy về với vị trí và kích thước tùy ý.
Trong bài viết này, mình sẽ sử dụng PageKeyedDataSource. Hãy cùng xem đoạn code dưới đây:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedDataSource</span> <span class="token keyword">extends</span> <span class="token class-name">PageKeyedDataSource</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">,</span> <span class="token class-name">Article</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> TAG <span class="token operator">=</span> <span class="token class-name">FeedDataSource</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Step 1: Initialize the restApiFactory. * The networkState and initialLoading variables * are for updating the UI when data is being fetched * by displaying a progress bar */</span> <span class="token keyword">private</span> <span class="token class-name">RestApiFactory</span> restApiFactory<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">MutableLiveData</span> networkState<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">MutableLiveData</span> initialLoading<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">FeedDataSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>restApiFactory <span class="token operator">=</span> <span class="token class-name">RestApiFactory</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> networkState <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutableLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> initialLoading <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutableLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">MutableLiveData</span> <span class="token function">getNetworkState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> networkState<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">MutableLiveData</span> <span class="token function">getInitialLoading</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> initialLoading<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* * Step 2: This method is responsible to load the data initially * when app screen is launched for the first time. * We are fetching the first page data from the api * and passing it via the callback method to the UI. */</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">loadInitial</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadInitialParams</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> params<span class="token punctuation">,</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadInitialCallback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">,</span> <span class="token class-name">Article</span><span class="token punctuation">></span></span> callback<span class="token punctuation">)</span> <span class="token punctuation">{</span> initialLoading<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADING<span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADING<span class="token punctuation">)</span><span class="token punctuation">;</span> appController<span class="token punctuation">.</span><span class="token function">getRestApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">fetchFeed</span><span class="token punctuation">(</span>QUERY<span class="token punctuation">,</span> API_KEY<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> params<span class="token punctuation">.</span>requestedLoadSize<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Callback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onResponse</span><span class="token punctuation">(</span><span class="token class-name">Call</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> call<span class="token punctuation">,</span> <span class="token class-name">Response</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> response<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">isSuccessful</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> callback<span class="token punctuation">.</span><span class="token function">onResult</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getArticles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">2l</span><span class="token punctuation">)</span><span class="token punctuation">;</span> initialLoading<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADED<span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADED<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> initialLoading<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">NetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">,</span> response<span class="token punctuation">.</span><span class="token function">message</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">NetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">,</span> response<span class="token punctuation">.</span><span class="token function">message</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onFailure</span><span class="token punctuation">(</span><span class="token class-name">Call</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> call<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> t<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">String</span> errorMessage <span class="token operator">=</span> t <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">?</span> <span class="token string">"unknown error"</span> <span class="token operator">:</span> t<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">NetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">,</span> errorMessage<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">loadBefore</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadParams</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> params<span class="token punctuation">,</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadCallback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">,</span> <span class="token class-name">Article</span><span class="token punctuation">></span></span> callback<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">/* * Step 3: This method it is responsible for the subsequent call to load the data page wise. * This method is executed in the background thread * We are fetching the next page data from the api * and passing it via the callback method to the UI. * The "params.key" variable will have the updated value. */</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">loadAfter</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadParams</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> params<span class="token punctuation">,</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">LoadCallback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Long</span><span class="token punctuation">,</span> <span class="token class-name">Article</span><span class="token punctuation">></span></span> callback<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Log</span><span class="token punctuation">.</span><span class="token function">i</span><span class="token punctuation">(</span>TAG<span class="token punctuation">,</span> <span class="token string">"Loading Rang "</span> <span class="token operator">+</span> params<span class="token punctuation">.</span>key <span class="token operator">+</span> <span class="token string">" Count "</span> <span class="token operator">+</span> params<span class="token punctuation">.</span>requestedLoadSize<span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADING<span class="token punctuation">)</span><span class="token punctuation">;</span> appController<span class="token punctuation">.</span><span class="token function">getRestApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">fetchFeed</span><span class="token punctuation">(</span>QUERY<span class="token punctuation">,</span> API_KEY<span class="token punctuation">,</span> params<span class="token punctuation">.</span>key<span class="token punctuation">,</span> params<span class="token punctuation">.</span>requestedLoadSize<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Callback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onResponse</span><span class="token punctuation">(</span><span class="token class-name">Call</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> call<span class="token punctuation">,</span> <span class="token class-name">Response</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> response<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* * If the request is successful, then we will update the callback * with the latest feed items and * "params.key+1" -> set the next key for the next iteration. */</span> <span class="token keyword">if</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">isSuccessful</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> nextKey <span class="token operator">=</span> <span class="token punctuation">(</span>params<span class="token punctuation">.</span>key <span class="token operator">==</span> response<span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTotalResults</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token keyword">null</span> <span class="token operator">:</span> params<span class="token punctuation">.</span>key<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> callback<span class="token punctuation">.</span><span class="token function">onResult</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getArticles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> nextKey<span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADED<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">NetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">,</span> response<span class="token punctuation">.</span><span class="token function">message</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onFailure</span><span class="token punctuation">(</span><span class="token class-name">Call</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Feed</span><span class="token punctuation">></span></span> call<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> t<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">String</span> errorMessage <span class="token operator">=</span> t <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">?</span> <span class="token string">"unknown error"</span> <span class="token operator">:</span> t<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> networkState<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">NetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">,</span> errorMessage<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
IV. Cài đặt DataSourceFactory
DataSourceFactory chịu trách nhiệm truy xuất dữ liệu bằng cách sử dụng config của DataSource và PagedList mà mình sẽ tạo sau ở trong bài viết này trong class ViewModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedDataFactory</span> <span class="token keyword">extends</span> <span class="token class-name">DataSource</span><span class="token punctuation">.</span><span class="token class-name">Factory</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">MutableLiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">FeedDataSource</span><span class="token punctuation">></span></span> mutableLiveData<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">FeedDataSource</span> feedDataSource<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">FeedDataFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>mutableLiveData <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutableLiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">FeedDataSource</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token class-name">DataSource</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> feedDataSource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FeedDataSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> mutableLiveData<span class="token punctuation">.</span><span class="token function">postValue</span><span class="token punctuation">(</span>feedDataSource<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> feedDataSource<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">MutableLiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">FeedDataSource</span><span class="token punctuation">></span></span> <span class="token function">getMutableLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> mutableLiveData<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
V. Cài đặt lớp ViewModel
ViewModel sẽ chịu trách nhiệm tạo PagedList cùng với các config của nó và gửi đến Activity để Activity có thể quan sát dữ liệu khi dữ liệu thay đổi và chuyển nó đến adapter.
Chắc hẳn mọi người đang thắc mắc vậy PagedList là gì ? PagedList là một danh sách chứa các mục dữ liệu của bạn và gọi DataSource để tải các phần tử. Nó thường bao gồm một công việc thực hiện ở background (tải dữ liệu) và một công việc thực hiện ở foreground (cập nhật UI dựa trên dữ liệu tải về).
Ví dụ: giả sử có một số dữ liệu mà chúng ta thêm vào DataSource trong background thread. DataSource làm mất hiệu lực của PagedList và cập nhật giá trị. Sau đó trên main thread, PagedList sẽ thông báo cho những phần tử observe nó là nó đã có giá trị mới. Bây giờ PagedListAdapter đã biết về giá trị mới.
PageList có 4 tham số mà chúng ta cần chú ý:
a. setEnablePlaceHolder (boolean enablePlaceholders)
b. setInitialLoadSizeHint(int initialLoadSizeHint)
c. setPageSize(int pageSize)
d. setPageSize(int pageSize)
Dưới đây là class ViewModel của project:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedViewModel</span> <span class="token keyword">extends</span> <span class="token class-name">ViewModel</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Executor</span> executor<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">LiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">NetworkState</span><span class="token punctuation">></span></span> networkState<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">LiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">PagedList</span><span class="token punctuation"><</span><span class="token class-name">Article</span><span class="token punctuation">></span><span class="token punctuation">></span></span> articleLiveData<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">FeedViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* * Step 1: We are initializing an Executor class * Step 2: We are getting an instance of the DataSourceFactory class * Step 3: We are initializing the network state liveData as well. * This will update the UI on the network changes that take place * For instance, when the data is getting fetched, we would need * to display a loader and when data fetching is completed, we * should hide the loader. * Step 4: We need to configure the PagedList.Config. * Step 5: We are initializing the pageList using the config we created * in Step 4 and the DatasourceFactory we created from Step 2 * and the executor we initialized from Step 1. */</span> <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> executor <span class="token operator">=</span> <span class="token class-name">Executors</span><span class="token punctuation">.</span><span class="token function">newFixedThreadPool</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">FeedDataFactory</span> feedDataFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FeedDataFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> networkState <span class="token operator">=</span> <span class="token class-name">Transformations</span><span class="token punctuation">.</span><span class="token function">switchMap</span><span class="token punctuation">(</span>feedDataFactory<span class="token punctuation">.</span><span class="token function">getMutableLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> dataSource <span class="token operator">-></span> dataSource<span class="token punctuation">.</span><span class="token function">getNetworkState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">PagedList</span><span class="token punctuation">.</span><span class="token class-name">Config</span> pagedListConfig <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">PagedList</span><span class="token punctuation">.</span><span class="token class-name">Config</span><span class="token punctuation">.</span><span class="token class-name">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setEnablePlaceholders</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setInitialLoadSizeHint</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setPageSize</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> articleLiveData <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">LivePagedListBuilder</span><span class="token punctuation">(</span>feedDataFactory<span class="token punctuation">,</span> pagedListConfig<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setFetchExecutor</span><span class="token punctuation">(</span>executor<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* * Getter method for the network state */</span> <span class="token keyword">public</span> <span class="token class-name">LiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">NetworkState</span><span class="token punctuation">></span></span> <span class="token function">getNetworkState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> networkState<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* * Getter method for the pageList */</span> <span class="token keyword">public</span> <span class="token class-name">LiveData</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">PagedList</span><span class="token punctuation"><</span><span class="token class-name">Article</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">getArticleLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> articleLiveData<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
VI. Cài đặt PagedListAdapter
PagedListAdapter là một triển khai của RecyclerView. Adapter lấy dữ liệu từ PagedList, nó sử dụng DiffUtil làm tham số để tính toán sự khác biệt dữ liệu và thực hiện tất các các cập nhật cho bạn. DiffUtil được định nghĩa trong Model Class trong trường hợp này:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Article</span> <span class="token keyword">implements</span> <span class="token class-name">Parcelable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">DiffUtil</span><span class="token punctuation">.</span><span class="token class-name">ItemCallback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Article</span><span class="token punctuation">></span></span> DIFF_CALLBACK <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DiffUtil</span><span class="token punctuation">.</span><span class="token class-name">ItemCallback</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Article</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">areItemsTheSame</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">Article</span> oldItem<span class="token punctuation">,</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">Article</span> newItem<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> oldItem<span class="token punctuation">.</span>id <span class="token operator">==</span> newItem<span class="token punctuation">.</span>id<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">areContentsTheSame</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">Article</span> oldItem<span class="token punctuation">,</span> <span class="token annotation punctuation">@NonNull</span> <span class="token class-name">Article</span> newItem<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> oldItem<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>newItem<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">equals</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>obj <span class="token operator">==</span> <span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token class-name">Article</span> article <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Article</span><span class="token punctuation">)</span> obj<span class="token punctuation">;</span> <span class="token keyword">return</span> article<span class="token punctuation">.</span>id <span class="token operator">==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Class PagedListAdapter :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedListAdapter</span> <span class="token keyword">extends</span> <span class="token class-name">PagedListAdapter</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Article</span><span class="token punctuation">,</span> <span class="token class-name">RecyclerView</span><span class="token punctuation">.</span><span class="token class-name">ViewHolder</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token comment">/* * There are two layout types we define * in this adapter: * 1. progrss view * 2. data view */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> TYPE_PROGRESS <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> TYPE_ITEM <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">Context</span> context<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">NetworkState</span> networkState<span class="token punctuation">;</span> <span class="token comment">/* * The DiffUtil is defined in the constructor */</span> <span class="token keyword">public</span> <span class="token class-name">FeedListAdapter</span><span class="token punctuation">(</span><span class="token class-name">Context</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token class-name">Article</span><span class="token punctuation">.</span>DIFF_CALLBACK<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>context <span class="token operator">=</span> context<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* * Default method of RecyclerView.Adapter */</span> <span class="token annotation punctuation">@NonNull</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token class-name">RecyclerView</span><span class="token punctuation">.</span><span class="token class-name">ViewHolder</span> <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">ViewGroup</span> parent<span class="token punctuation">,</span> <span class="token keyword">int</span> viewType<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">LayoutInflater</span> layoutInflater <span class="token operator">=</span> <span class="token class-name">LayoutInflater</span><span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>viewType <span class="token operator">==</span> TYPE_PROGRESS<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">NetworkItemBinding</span> headerBinding <span class="token operator">=</span> <span class="token class-name">NetworkItemBinding</span><span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">NetworkStateItemViewHolder</span> viewHolder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NetworkStateItemViewHolder</span><span class="token punctuation">(</span>headerBinding<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> viewHolder<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token class-name">FeedItemBinding</span> itemBinding <span class="token operator">=</span> <span class="token class-name">FeedItemBinding</span><span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ArticleItemViewHolder</span> viewHolder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArticleItemViewHolder</span><span class="token punctuation">(</span>itemBinding<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> viewHolder<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/* * Default method of RecyclerView.Adapter */</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onBindViewHolder</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NonNull</span> <span class="token class-name">RecyclerView</span><span class="token punctuation">.</span><span class="token class-name">ViewHolder</span> holder<span class="token punctuation">,</span> <span class="token keyword">int</span> position<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>holder <span class="token keyword">instanceof</span> <span class="token class-name">ArticleItemViewHolder</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">ArticleItemViewHolder</span><span class="token punctuation">)</span>holder<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bindTo</span><span class="token punctuation">(</span><span class="token function">getItem</span><span class="token punctuation">(</span>position<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">NetworkStateItemViewHolder</span><span class="token punctuation">)</span> holder<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bindView</span><span class="token punctuation">(</span>networkState<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/* * Default method of RecyclerView.Adapter */</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getItemViewType</span><span class="token punctuation">(</span><span class="token keyword">int</span> position<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">hasExtraRow</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> position <span class="token operator">==</span> <span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> TYPE_PROGRESS<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> TYPE_ITEM<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">boolean</span> <span class="token function">hasExtraRow</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>networkState <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> networkState <span class="token operator">!=</span> <span class="token class-name">NetworkState</span><span class="token punctuation">.</span>LOADED<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setNetworkState</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span> newNetworkState<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">NetworkState</span> previousState <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>networkState<span class="token punctuation">;</span> <span class="token keyword">boolean</span> previousExtraRow <span class="token operator">=</span> <span class="token function">hasExtraRow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>networkState <span class="token operator">=</span> newNetworkState<span class="token punctuation">;</span> <span class="token keyword">boolean</span> newExtraRow <span class="token operator">=</span> <span class="token function">hasExtraRow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>previousExtraRow <span class="token operator">!=</span> newExtraRow<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>previousExtraRow<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">notifyItemRemoved</span><span class="token punctuation">(</span><span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">notifyItemInserted</span><span class="token punctuation">(</span><span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>newExtraRow <span class="token operator">&&</span> previousState <span class="token operator">!=</span> newNetworkState<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">notifyItemChanged</span><span class="token punctuation">(</span><span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/* * We define A custom ViewHolder for the list item */</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ArticleItemViewHolder</span> <span class="token keyword">extends</span> <span class="token class-name">RecyclerView</span><span class="token punctuation">.</span><span class="token class-name">ViewHolder</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">FeedItemBinding</span> binding<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">ArticleItemViewHolder</span><span class="token punctuation">(</span><span class="token class-name">FeedItemBinding</span> binding<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span><span class="token function">getRoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>binding <span class="token operator">=</span> binding<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">bindTo</span><span class="token punctuation">(</span><span class="token class-name">Article</span> article<span class="token punctuation">)</span> <span class="token punctuation">{</span> binding<span class="token punctuation">.</span>itemImage<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>VISIBLE<span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>itemDesc<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>VISIBLE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">String</span> author <span class="token operator">=</span> article<span class="token punctuation">.</span><span class="token function">getAuthor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> article<span class="token punctuation">.</span><span class="token function">getAuthor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> context<span class="token punctuation">.</span><span class="token function">getString</span><span class="token punctuation">(</span><span class="token class-name">R</span><span class="token punctuation">.</span>string<span class="token punctuation">.</span>author_name<span class="token punctuation">)</span> <span class="token operator">:</span> article<span class="token punctuation">.</span><span class="token function">getAuthor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">String</span> titleString <span class="token operator">=</span> <span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">getString</span><span class="token punctuation">(</span><span class="token class-name">R</span><span class="token punctuation">.</span>string<span class="token punctuation">.</span>item_title<span class="token punctuation">)</span><span class="token punctuation">,</span> author<span class="token punctuation">,</span> article<span class="token punctuation">.</span><span class="token function">getTitle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">SpannableString</span> spannableString <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SpannableString</span><span class="token punctuation">(</span>titleString<span class="token punctuation">)</span><span class="token punctuation">;</span> spannableString<span class="token punctuation">.</span><span class="token function">setSpan</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ForegroundColorSpan</span><span class="token punctuation">(</span><span class="token class-name">ContextCompat</span><span class="token punctuation">.</span><span class="token function">getColor</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">getApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">R</span><span class="token punctuation">.</span>color<span class="token punctuation">.</span>secondary_text<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> titleString<span class="token punctuation">.</span><span class="token function">lastIndexOf</span><span class="token punctuation">(</span>author<span class="token punctuation">)</span> <span class="token operator">+</span> author<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> titleString<span class="token punctuation">.</span><span class="token function">lastIndexOf</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getTitle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token class-name">Spannable</span><span class="token punctuation">.</span>SPAN_EXCLUSIVE_EXCLUSIVE<span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>itemTitle<span class="token punctuation">.</span><span class="token function">setText</span><span class="token punctuation">(</span>spannableString<span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>itemTime<span class="token punctuation">.</span><span class="token function">setText</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">getString</span><span class="token punctuation">(</span><span class="token class-name">R</span><span class="token punctuation">.</span>string<span class="token punctuation">.</span>item_date<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">AppUtils</span><span class="token punctuation">.</span><span class="token function">getDate</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getPublishedAt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">AppUtils</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getPublishedAt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>itemDesc<span class="token punctuation">.</span><span class="token function">setText</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Picasso</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getUrlToImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">resize</span><span class="token punctuation">(</span><span class="token number">250</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>itemImage<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/* * We define A custom ViewHolder for the progressView */</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">NetworkStateItemViewHolder</span> <span class="token keyword">extends</span> <span class="token class-name">RecyclerView</span><span class="token punctuation">.</span><span class="token class-name">ViewHolder</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">NetworkItemBinding</span> binding<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">NetworkStateItemViewHolder</span><span class="token punctuation">(</span><span class="token class-name">NetworkItemBinding</span> binding<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span><span class="token function">getRoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>binding <span class="token operator">=</span> binding<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">bindView</span><span class="token punctuation">(</span><span class="token class-name">NetworkState</span> networkState<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>networkState <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> networkState<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>RUNNING<span class="token punctuation">)</span> <span class="token punctuation">{</span> binding<span class="token punctuation">.</span>progressBar<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>VISIBLE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> binding<span class="token punctuation">.</span>progressBar<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>GONE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>networkState <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> networkState<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token class-name">NetworkState</span><span class="token punctuation">.</span><span class="token class-name">Status</span><span class="token punctuation">.</span>FAILED<span class="token punctuation">)</span> <span class="token punctuation">{</span> binding<span class="token punctuation">.</span>errorMsg<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>VISIBLE<span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>errorMsg<span class="token punctuation">.</span><span class="token function">setText</span><span class="token punctuation">(</span>networkState<span class="token punctuation">.</span><span class="token function">getMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> binding<span class="token punctuation">.</span>errorMsg<span class="token punctuation">.</span><span class="token function">setVisibility</span><span class="token punctuation">(</span><span class="token class-name">View</span><span class="token punctuation">.</span>GONE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
VII. Cài đặt Activity
Bước cuối cùng là cài đặt class Activity với ViewModel, RecyclerView, PagedListAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FeedActivity</span> <span class="token keyword">extends</span> <span class="token class-name">AppCompatActivity</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">FeedListAdapter</span> adapter<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">FeedViewModel</span> feedViewModel<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">FeedActivityBinding</span> binding<span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">onCreate</span><span class="token punctuation">(</span><span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Bundle</span> savedInstanceState<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Step 1: Using DataBinding, we setup the layout for the activity * * */</span> binding <span class="token operator">=</span> <span class="token class-name">DataBindingUtil</span><span class="token punctuation">.</span><span class="token function">setContentView</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token class-name">R</span><span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Step 2: Initialize the ViewModel * * */</span> feedViewModel <span class="token operator">=</span> <span class="token class-name">ViewModelProviders</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">FeedViewModel</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Step 2: Setup the adapter class for the RecyclerView * * */</span> binding<span class="token punctuation">.</span>listFeed<span class="token punctuation">.</span><span class="token function">setLayoutManager</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">LinearLayoutManager</span><span class="token punctuation">(</span><span class="token function">getApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> adapter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FeedListAdapter</span><span class="token punctuation">(</span><span class="token function">getApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Step 4: When a new page is available, we call submitList() method * of the PagedListAdapter class * * */</span> feedViewModel<span class="token punctuation">.</span><span class="token function">getArticleLiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> pagedList <span class="token operator">-></span> <span class="token punctuation">{</span> adapter<span class="token punctuation">.</span><span class="token function">submitList</span><span class="token punctuation">(</span>pagedList<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> feedViewModel<span class="token punctuation">.</span><span class="token function">getNetworkState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> networkState <span class="token operator">-></span> <span class="token punctuation">{</span> adapter<span class="token punctuation">.</span><span class="token function">setNetworkState</span><span class="token punctuation">(</span>networkState<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>listFeed<span class="token punctuation">.</span><span class="token function">setAdapter</span><span class="token punctuation">(</span>adapter<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Với bài viết này mình mong mọi người có 1 trải nghiệm tốt và nó sẽ giúp ích cho mọi người. Happy coding ! Thanks for reading.
Tài liệu tham khảo:
https://proandroiddev.com/8-steps-to-implement-paging-library-in-android-d02500f7fffe
https://github.com/anitaa1990/PagingLibrary-Sample