Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to notify Dataset changed for RecyclerView

How to notify Dataset changed for RecyclerView

Think about a better way to notify dataset changed for RecyclerView.

Ryota Takemoto

February 27, 2018
Tweet

More Decks by Ryota Takemoto

Other Decks in Programming

Transcript

  1. adapter.getDataSet().removeAt(position) adapter.notifyItemRemoved(position) adapter.notifyItemRangeChanged( position, adapter.itemCount - position ) // ok

    adapter.getDataSet().removeAt(position2) adapter.notifyItemRemoved(position2) adapter.notifyItemRangeChanged(…) // ok notifyItem~()
  2. Ideal • Easy to use • Redraw only changed items

    • Multiple item notifying • Animation Adapter.notify~()
  3. DiffUtil • Support Lib 25.1.0~ • Calculate diff between 2

    list • Notify only changed items • Animation DiffUtil
  4. DiffUtil.Callback override fun getOldListSize() = oldList.size override fun getNewListSize() =

    newList.size // Same item? override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean { return oldList[oldPos].id == newList[newPos].id } // Any changes? override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean { val old = oldList[oldPos] val new = newList[newPos] return old == new } Implementation For DiffUtil
  5. Full Implementation val diffCallback = object : DiffUtil.Callback() { val

    oldList = currentDataSet val newList = newDataSet override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean = oldList[oldPos].id == newList[newPos].id override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean = oldList[oldPos] == newList[newPos] } val diffResult = DiffUtil.calculateDiff(diffCallback) diffResult.dispatchUpdatesTo(adapter)
  6. Calculate diff on background “If the lists are large, this

    operation may take significant time” “run this on a background thread, get the DiffUtil.DiffResult then apply it on the main thread” Implementation For DiffUtil
  7. Calculate diff on background Thread({ val diffCallback = object :

    DiffUtil.Callback() { … } // Calculate diff on background thread. val diffResult = DiffUtil.calculateDiff(diffCallback) handler.post { // Update on main thread. diffResult.dispatchUpdatesTo(adapter) } }).start()
  8. Calculate diff on background Thread({ val diffCallback = object :

    DiffUtil.Callback() { … } // Calculate diff on background thread. val diffResult = DiffUtil.calculateDiff(diffCallback) handler.post { // Update on main thread. diffResult.dispatchUpdatesTo(adapter) } }).start()
  9. Calculate diff on background Thread({ val diffCallback = object :

    DiffUtil.Callback() { … } // Calculate diff on background thread. val diffResult = DiffUtil.calculateDiff(diffCallback) handler.post { // Update on main thread. diffResult.dispatchUpdatesTo(adapter) } }).start()
  10. Paging Library Paging Library • Part of AAC • Having

    same feature as DiffUtil • Paging
  11. class PageKeyedRepoDataSource: PageKeyedDataSource<Int, Repo>() { override fun loadInitial(params: LoadInitialParams<Int>, callback:

    LoadInitialCallback<Int, Repo>) { // Initial data fetching } override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Repo>) { // no-op } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Repo>) { // Paged data fetching } }
  12. override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Repo>) { val page

    = 1 val nextPageKey = 2 val response = repoApiClient .getRepos(USER_NAME, page, params.requestedLoadSize) .execute() callback.onResult(response.body(), null, nextPageKey) }
  13. override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Repo>) { val page

    = params.key val nextPageKey = params.key + 1 val response = repoApiClient .getRepos(USER_NAME, page, params.requestedLoadSize) .execute() callback.onResult(response.body(), nextPageKey) }
  14. val dataSourceFactory = RepoDataFactory(repoApiClient) val config = PagedList.Config.Builder() .setInitialLoadSizeHint(10) .setPageSize(10)

    .build() return LivePagedListBuilder(dataSourceFactory, config) .setBackgroundThreadExecutor(NETWORK_IO) .build()
  15. private fun getRepos(): LiveData<PagedList<Repo>> { ... } getRepos() .observe(context, Observer

    { pagedList -> // Items are drawn pagedListAdapter.setList(pagedList) }) Observe PagedList
  16. Update DataSet Implementation for Paging Library • Re-fetch API with

    sort condition • Update items only changed by setting PagedList again
  17. private fun getRepos(condition: SortCondition) : LiveData<PagedList<Repo>> { ... } getRepos(SortCondition.UPDATED)

    .observe(context, Observer { pagedList -> // Only new items are drawn repoAdapter.setList(pagedList) })
  18. private fun getRepos(condition: SortCondition) : LiveData<PagedList<Repo>> { ... } getRepos(SortCondition.UPDATED)

    .observe(context, Observer { pagedList -> repoAdapter.setList(null) // All new items are redrawn repoAdapter.setList(pagedList) })