Preamble
Loading data lists with more load and refreshing is a very common task in the android dev process, today I introduce you how to do it with jetpack paging lib and create bases to reuse for different screens. .
Proceed to the code base
PagedListAdapter
To paging lib can detect the load more then we need to use PagedListAdapter
, you can create an adapter or extend from BasePagedListAdapter
below.
Note: paging lib uses getItem()
function to detect the load more so you need to call this function in onBindViewHolder()
, if you use another way to get item, for example adapter.currentList.getPosition
, paging lib will Could not detect loading more list.
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 | <span class="token keyword">abstract</span> <span class="token keyword">class</span> BasePagedListAdapter <span class="token operator"><</span> Item <span class="token punctuation">,</span> ViewBinding <span class="token operator">:</span> ViewDataBinding <span class="token operator">></span> <span class="token punctuation">(</span> callBack <span class="token operator">:</span> DiffUtil <span class="token punctuation">.</span> ItemCallback <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token operator">:</span> PagedListAdapter <span class="token operator"><</span> Item <span class="token punctuation">,</span> BaseViewHolder <span class="token operator"><</span> ViewBinding <span class="token operator">></span> <span class="token operator">></span> <span class="token punctuation">(</span> callBack <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreateViewHolder</span> <span class="token punctuation">(</span> parent <span class="token operator">:</span> ViewGroup <span class="token punctuation">,</span> viewType <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token operator">:</span> BaseViewHolder <span class="token operator"><</span> ViewBinding <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">BaseViewHolder</span> <span class="token punctuation">(</span> DataBindingUtil <span class="token punctuation">.</span> inflate <span class="token operator"><</span> ViewBinding <span class="token operator">></span> <span class="token punctuation">(</span> LayoutInflater <span class="token punctuation">.</span> <span class="token function">from</span> <span class="token punctuation">(</span> parent <span class="token punctuation">.</span> context <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token function">getLayoutRes</span> <span class="token punctuation">(</span> viewType <span class="token punctuation">)</span> <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 function">apply</span> <span class="token punctuation">{</span> <span class="token function">bindFirstTime</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 punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onBindViewHolder</span> <span class="token punctuation">(</span> holder <span class="token operator">:</span> BaseViewHolder <span class="token operator"><</span> ViewBinding <span class="token operator">></span> <span class="token punctuation">,</span> position <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> item <span class="token operator">:</span> Item <span class="token operator">?</span> <span class="token operator">=</span> <span class="token function">getItem</span> <span class="token punctuation">(</span> position <span class="token punctuation">)</span> holder <span class="token punctuation">.</span> binding <span class="token punctuation">.</span> <span class="token function">setVariable</span> <span class="token punctuation">(</span> BR <span class="token punctuation">.</span> item <span class="token punctuation">,</span> item <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> item <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">bindView</span> <span class="token punctuation">(</span> holder <span class="token punctuation">.</span> binding <span class="token punctuation">,</span> item <span class="token punctuation">,</span> position <span class="token punctuation">)</span> <span class="token punctuation">}</span> holder <span class="token punctuation">.</span> binding <span class="token punctuation">.</span> <span class="token function">executePendingBindings</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">/** * get layout res based on view type */</span> <span class="token keyword">protected</span> <span class="token keyword">abstract</span> <span class="token keyword">fun</span> <span class="token function">getLayoutRes</span> <span class="token punctuation">(</span> viewType <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token comment">/** * bind first time * use for set item onClickListener, something only set one time */</span> <span class="token keyword">protected</span> <span class="token keyword">open</span> <span class="token keyword">fun</span> <span class="token function">bindFirstTime</span> <span class="token punctuation">(</span> binding <span class="token operator">:</span> ViewBinding <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">/** * bind view */</span> <span class="token keyword">protected</span> <span class="token keyword">open</span> <span class="token keyword">fun</span> <span class="token function">bindView</span> <span class="token punctuation">(</span> binding <span class="token operator">:</span> ViewBinding <span class="token punctuation">,</span> item <span class="token operator">:</span> Item <span class="token punctuation">,</span> position <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
BasePageKeyedDataSource
Paging lib currently supports 3 different types of data srouce, but PageKeyedDataSource
is the most commonly used type so I will make an example of this type, the other 2 types you can do the same.
Currently PageKeyedDataSource
has 3 functions loadInitial()
, loadBefore()
, loadAfter()
to define the load status according to the page, but in fact I see that we only need a load function, so I have shortened the code. and returns a unique abstract loadDataSource()
function.
Note: As far as I know, paging 3 (currently in preview) has combined the above functions into 1, really very reasonable. I will introduce it to you after it is more stable.
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 | <span class="token comment">/** * page keyed data source uses page number to request data */</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> BasePageKeyedDataSource <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token keyword">private</span> <span class="token keyword">val</span> viewModel <span class="token operator">:</span> BasePagedRefreshViewModel <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token operator">:</span> PageKeyedDataSource <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/** * load first page */</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">loadInitial</span> <span class="token punctuation">(</span> params <span class="token operator">:</span> LoadInitialParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token punctuation">,</span> callback <span class="token operator">:</span> LoadInitialCallback <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> viewModelScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">showLoading</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> itemList <span class="token operator">=</span> <span class="token function">loadDataSource</span> <span class="token punctuation">(</span> loadInitialParams <span class="token operator">=</span> params <span class="token punctuation">)</span> viewModel <span class="token punctuation">.</span> isEmptyList <span class="token punctuation">.</span> value <span class="token operator">=</span> itemList <span class="token punctuation">.</span> <span class="token function">isEmpty</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> itemList <span class="token punctuation">,</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token comment">// last page</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> itemList <span class="token punctuation">.</span> size <span class="token operator"><</span> viewModel <span class="token punctuation">.</span> pageSize <span class="token punctuation">)</span> <span class="token keyword">null</span> <span class="token comment">// load next page</span> <span class="token keyword">else</span> viewModel <span class="token punctuation">.</span> firstPage <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">catch</span> <span class="token punctuation">(</span> e <span class="token operator">:</span> Throwable <span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">onError</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">hideLoadMoreRefresh</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 comment">/** * load previous page */</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">loadBefore</span> <span class="token punctuation">(</span> params <span class="token operator">:</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token punctuation">,</span> callback <span class="token operator">:</span> LoadCallback <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> viewModelScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> itemList <span class="token operator">=</span> <span class="token function">loadDataSource</span> <span class="token punctuation">(</span> loadParams <span class="token operator">=</span> params <span class="token punctuation">)</span> callback <span class="token punctuation">.</span> <span class="token function">onResult</span> <span class="token punctuation">(</span> itemList <span class="token punctuation">,</span> <span class="token comment">// is first page</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> params <span class="token punctuation">.</span> key <span class="token operator">==</span> viewModel <span class="token punctuation">.</span> firstPage <span class="token punctuation">)</span> <span class="token keyword">null</span> <span class="token keyword">else</span> <span class="token punctuation">(</span> params <span class="token punctuation">.</span> key <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 keyword">catch</span> <span class="token punctuation">(</span> e <span class="token operator">:</span> Throwable <span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">onError</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">hideLoadMoreRefresh</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 comment">/** * load next page */</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">loadAfter</span> <span class="token punctuation">(</span> params <span class="token operator">:</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token punctuation">,</span> callback <span class="token operator">:</span> LoadCallback <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// disable load more when loading or refresh</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> viewModel <span class="token punctuation">.</span> isLoading <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token operator">||</span> viewModel <span class="token punctuation">.</span> isRefresh <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> viewModel <span class="token punctuation">.</span> viewModelScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> isLoadMore <span class="token punctuation">.</span> value <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token keyword">val</span> itemList <span class="token operator">=</span> <span class="token function">loadDataSource</span> <span class="token punctuation">(</span> loadParams <span class="token operator">=</span> params <span class="token punctuation">)</span> callback <span class="token punctuation">.</span> <span class="token function">onResult</span> <span class="token punctuation">(</span> itemList <span class="token punctuation">,</span> <span class="token comment">// last page</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> itemList <span class="token punctuation">.</span> size <span class="token operator"><</span> viewModel <span class="token punctuation">.</span> pageSize <span class="token punctuation">)</span> <span class="token keyword">null</span> <span class="token comment">// load next page</span> <span class="token keyword">else</span> <span class="token punctuation">(</span> params <span class="token punctuation">.</span> key <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 keyword">catch</span> <span class="token punctuation">(</span> e <span class="token operator">:</span> Throwable <span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">onError</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">hideLoadMoreRefresh</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 comment">/** * load data */</span> <span class="token keyword">abstract</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">loadDataSource</span> <span class="token punctuation">(</span> loadInitialParams <span class="token operator">:</span> LoadInitialParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> loadParams <span class="token operator">:</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span> <span class="token punctuation">)</span> <span class="token operator">:</span> List <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">}</span> |
BasePagedRefreshViewModel
After having the data source, we will define the viewmodel, the viewmodel will have the basic logic of loading more, refreshing, PageKeyedDataSource
parameters for PageKeyedDataSource
and a loadData()
function to get data.
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 | <span class="token keyword">abstract</span> <span class="token keyword">class</span> BasePagedRefreshViewModel <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token operator">:</span> <span class="token function">BaseViewModel</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// first page number of item list</span> <span class="token keyword">open</span> <span class="token keyword">val</span> firstPage <span class="token operator">=</span> Constants <span class="token punctuation">.</span> DEFAULT_FIRST_PAGE <span class="token comment">// number visible threshold</span> <span class="token keyword">open</span> <span class="token keyword">val</span> prefetchDistance <span class="token operator">=</span> Constants <span class="token punctuation">.</span> DEFAULT_NUM_VISIBLE_THRESHOLD <span class="token comment">// number item per page</span> <span class="token keyword">open</span> <span class="token keyword">val</span> pageSize <span class="token operator">=</span> Constants <span class="token punctuation">.</span> DEFAULT_ITEM_PER_PAGE <span class="token comment">// refresh flag</span> <span class="token keyword">val</span> isRefresh <span class="token operator">=</span> <span class="token function">MutableLiveData</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token comment">// load more flag</span> <span class="token keyword">val</span> isLoadMore <span class="token operator">=</span> <span class="token function">MutableLiveData</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token comment">// enable refresh when not loading or not load more</span> <span class="token keyword">val</span> enableRefresh <span class="token operator">=</span> MediatorLiveData <span class="token operator"><</span> Boolean <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token function">addSource</span> <span class="token punctuation">(</span> isLoading <span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token operator">!</span> <span class="token punctuation">(</span> isLoading <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token operator">||</span> isLoadMore <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">addSource</span> <span class="token punctuation">(</span> isLoadMore <span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token operator">!</span> <span class="token punctuation">(</span> isLoading <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token operator">||</span> isLoadMore <span class="token punctuation">.</span> value <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// data source</span> <span class="token keyword">private</span> <span class="token keyword">var</span> dataSource <span class="token operator">:</span> BasePageKeyedDataSource <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span> <span class="token comment">// paged list config</span> <span class="token keyword">private</span> <span class="token keyword">val</span> pagedListConfig <span class="token operator">:</span> PagedList <span class="token punctuation">.</span> Config <span class="token keyword">by</span> lazy <span class="token punctuation">{</span> PagedList <span class="token punctuation">.</span> Config <span class="token punctuation">.</span> <span class="token function">Builder</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">2</span> <span class="token operator">*</span> pageSize <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setPageSize</span> <span class="token punctuation">(</span> pageSize <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setPrefetchDistance</span> <span class="token punctuation">(</span> prefetchDistance <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 comment">// item list</span> <span class="token keyword">val</span> itemList <span class="token operator">:</span> LiveData <span class="token operator"><</span> PagedList <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token operator">></span> <span class="token keyword">by</span> lazy <span class="token punctuation">{</span> <span class="token function">LivePagedListBuilder</span> <span class="token punctuation">(</span> <span class="token keyword">object</span> <span class="token operator">:</span> DataSource <span class="token punctuation">.</span> Factory <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> DataSource <span class="token operator"><</span> Int <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">createDataSource</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> pagedListConfig <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 comment">// empty list flag</span> <span class="token keyword">val</span> isEmptyList <span class="token operator">=</span> <span class="token function">MutableLiveData</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token comment">/** * refresh data */</span> <span class="token keyword">fun</span> <span class="token function">doRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> isRefresh <span class="token punctuation">.</span> value <span class="token operator">=</span> <span class="token boolean">true</span> dataSource <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">invalidate</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">/** * create page keyed data source */</span> <span class="token keyword">open</span> <span class="token keyword">fun</span> <span class="token function">createDataSource</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> BasePageKeyedDataSource <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">object</span> <span class="token operator">:</span> BasePageKeyedDataSource <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">(</span> viewModel <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">loadDataSource</span> <span class="token punctuation">(</span> loadInitialParams <span class="token operator">:</span> LoadInitialParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">,</span> loadParams <span class="token operator">:</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token operator">:</span> List <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">loadData</span> <span class="token punctuation">(</span> loadInitialParams <span class="token punctuation">,</span> loadParams <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">{</span> dataSource <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/** * load and return item list from server */</span> <span class="token keyword">abstract</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">loadData</span> <span class="token punctuation">(</span> loadInitialParams <span class="token operator">:</span> PageKeyedDataSource <span class="token punctuation">.</span> LoadInitialParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">,</span> loadParams <span class="token operator">:</span> PageKeyedDataSource <span class="token punctuation">.</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token operator">:</span> List <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token comment">/** * handler error */</span> <span class="token keyword">override</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">onError</span> <span class="token punctuation">(</span> throwable <span class="token operator">:</span> Throwable <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">onError</span> <span class="token punctuation">(</span> throwable <span class="token punctuation">)</span> <span class="token comment">// reset load</span> <span class="token function">hideLoadMoreRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">/** * hide loading, load more and refresh indicator */</span> <span class="token keyword">fun</span> <span class="token function">hideLoadMoreRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">hideLoading</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> isRefresh <span class="token punctuation">.</span> value <span class="token operator">=</span> <span class="token boolean">false</span> isLoadMore <span class="token punctuation">.</span> value <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
BasePagedRefreshFragment
The base code fragment for the monitor uses the BasePagedRefreshViewModel so we can reuse it for different screens that have the same mechanism.
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 | <span class="token keyword">abstract</span> <span class="token keyword">class</span> BasePagedRefreshFragment <span class="token operator"><</span> ViewBinding <span class="token operator">:</span> ViewDataBinding <span class="token punctuation">,</span> ViewModel <span class="token operator">:</span> BasePagedRefreshViewModel <span class="token operator"><</span> Item <span class="token operator">></span> <span class="token punctuation">,</span> Item <span class="token operator">></span> <span class="token operator">:</span> BaseFragment <span class="token operator"><</span> ViewBinding <span class="token punctuation">,</span> ViewModel <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">val</span> layoutId <span class="token operator">:</span> Int <span class="token operator">=</span> R <span class="token punctuation">.</span> layout <span class="token punctuation">.</span> fragment_paged_refresh <span class="token keyword">abstract</span> <span class="token keyword">val</span> pagedListAdapter <span class="token operator">:</span> BasePagedListAdapter <span class="token operator"><</span> Item <span class="token punctuation">,</span> <span class="token keyword">out</span> ViewDataBinding <span class="token operator">></span> <span class="token keyword">open</span> <span class="token keyword">fun</span> <span class="token function">getLayoutManager</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView <span class="token punctuation">.</span> LayoutManager <span class="token operator">=</span> <span class="token function">LinearLayoutManager</span> <span class="token punctuation">(</span> context <span class="token punctuation">,</span> LinearLayoutManager <span class="token punctuation">.</span> VERTICAL <span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onActivityCreated</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <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">onActivityCreated</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> <span class="token function">setupPagedRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">/** * setup default paged refresh */</span> <span class="token keyword">open</span> <span class="token keyword">fun</span> <span class="token function">setupPagedRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> refresh_layout <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">setOnRefreshListener</span> <span class="token punctuation">{</span> viewModel <span class="token punctuation">.</span> <span class="token function">doRefresh</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> recycler_view <span class="token operator">?</span> <span class="token punctuation">.</span> layoutManager <span class="token operator">=</span> <span class="token function">getLayoutManager</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> recycler_view <span class="token operator">?</span> <span class="token punctuation">.</span> adapter <span class="token operator">=</span> pagedListAdapter viewModel <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">{</span> itemList <span class="token punctuation">.</span> <span class="token function">observe</span> <span class="token punctuation">(</span> viewLifecycleOwner <span class="token punctuation">,</span> Observer <span class="token punctuation">{</span> pagedListAdapter <span class="token punctuation">.</span> <span class="token function">submitList</span> <span class="token punctuation">(</span> it <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">override</span> <span class="token keyword">fun</span> <span class="token function">handleLoading</span> <span class="token punctuation">(</span> isLoading <span class="token operator">:</span> Boolean <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// use progress bar</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Use
PagedMovieAdapter
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 | <span class="token keyword">class</span> <span class="token function">PagedMovieAdapter</span> <span class="token punctuation">(</span> <span class="token keyword">val</span> itemClickListener <span class="token operator">:</span> <span class="token punctuation">(</span> Movie <span class="token punctuation">)</span> <span class="token operator">-></span> Unit <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token operator">:</span> BasePagedListAdapter <span class="token operator"><</span> Movie <span class="token punctuation">,</span> ItemMovieBinding <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token keyword">object</span> <span class="token operator">:</span> DiffUtil <span class="token punctuation">.</span> ItemCallback <span class="token operator"><</span> Movie <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">areItemsTheSame</span> <span class="token punctuation">(</span> oldItem <span class="token operator">:</span> Movie <span class="token punctuation">,</span> newItem <span class="token operator">:</span> Movie <span class="token punctuation">)</span> <span class="token operator">:</span> Boolean <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 keyword">override</span> <span class="token keyword">fun</span> <span class="token function">areContentsTheSame</span> <span class="token punctuation">(</span> oldItem <span class="token operator">:</span> Movie <span class="token punctuation">,</span> newItem <span class="token operator">:</span> Movie <span class="token punctuation">)</span> <span class="token operator">:</span> Boolean <span class="token punctuation">{</span> <span class="token keyword">return</span> oldItem <span class="token operator">==</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 keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getLayoutRes</span> <span class="token punctuation">(</span> viewType <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token operator">:</span> Int <span class="token punctuation">{</span> <span class="token keyword">return</span> R <span class="token punctuation">.</span> layout <span class="token punctuation">.</span> item_movie <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">bindFirstTime</span> <span class="token punctuation">(</span> binding <span class="token operator">:</span> ItemMovieBinding <span class="token punctuation">)</span> <span class="token punctuation">{</span> binding <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">{</span> root <span class="token punctuation">.</span> <span class="token function">setSingleClick</span> <span class="token punctuation">{</span> item <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">{</span> <span class="token function">itemClickListener</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 punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
PagedMovieFragment
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">class</span> PagedMovieFragment <span class="token operator">:</span> BasePagedRefreshFragment <span class="token operator"><</span> FragmentPagedRefreshBinding <span class="token punctuation">,</span> PagedMovieViewModel <span class="token punctuation">,</span> Movie <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">val</span> viewModel <span class="token operator">:</span> PagedMovieViewModel <span class="token keyword">by</span> <span class="token function">viewModel</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">override</span> <span class="token keyword">val</span> pagedListAdapter <span class="token keyword">by</span> lazy <span class="token punctuation">{</span> <span class="token function">PagedMovieAdapter</span> <span class="token punctuation">(</span> itemClickListener <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">toMovieDetail</span> <span class="token punctuation">(</span> it <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getLayoutManager</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">GridLayoutManager</span> <span class="token punctuation">(</span> context <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onActivityCreated</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <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">onActivityCreated</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> container <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">setBackgroundColor</span> <span class="token punctuation">(</span> Color <span class="token punctuation">.</span> BLACK <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">toMovieDetail</span> <span class="token punctuation">(</span> movie <span class="token operator">:</span> Movie <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">findNavController</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">navigate</span> <span class="token punctuation">(</span> PagedMovieFragmentDirections <span class="token punctuation">.</span> <span class="token function">toGraphMovieDetail</span> <span class="token punctuation">(</span> movie <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
PagedMovieViewModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">class</span> <span class="token function">PagedMovieViewModel</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> <span class="token keyword">val</span> userRepository <span class="token operator">:</span> UserRepository <span class="token punctuation">)</span> <span class="token operator">:</span> BasePagedRefreshViewModel <span class="token operator"><</span> Movie <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">loadData</span> <span class="token punctuation">(</span> loadInitialParams <span class="token operator">:</span> PageKeyedDataSource <span class="token punctuation">.</span> LoadInitialParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">,</span> loadParams <span class="token operator">:</span> PageKeyedDataSource <span class="token punctuation">.</span> LoadParams <span class="token operator"><</span> Int <span class="token operator">></span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token operator">:</span> List <span class="token operator"><</span> Movie <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">val</span> apiParams <span class="token operator">=</span> HashMap <span class="token operator"><</span> String <span class="token punctuation">,</span> String <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> apiParams <span class="token punctuation">[</span> ApiParams <span class="token punctuation">.</span> PAGE <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span> loadParams <span class="token operator">?</span> <span class="token punctuation">.</span> key <span class="token operator">?:</span> firstPage <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toString</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> userRepository <span class="token punctuation">.</span> <span class="token function">getMovieList</span> <span class="token punctuation">(</span> apiParams <span class="token punctuation">)</span> <span class="token punctuation">.</span> results <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">toList</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">?:</span> <span class="token function">listOf</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">val</span> pageSize <span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">20</span> <span class="token punctuation">}</span> |
Conclusion
You see, with the use of base code, now making screens with more refresh loads become much faster and more convenient, we can easily use for different screens with similar structure.
You can refer to the full source code at ddaay: https://github.com/dangquanuet/The-Movie-DB-Kotlin
See you in the next post .