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.

Avatar for Ryota Takemoto

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