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

getChangePayload in DiffUtil

getChangePayload in DiffUtil

DiffUtilのgetChangePayloadを利用して、リストのアイテムの更新をより細かく制御しようというお話です。

▼サンプルコードはこちら
https://github.com/rmakiyama/recyclerview-playground

rmakiyama

July 10, 2020
Tweet

More Decks by rmakiyama

Other Decks in Programming

Transcript

  1. getChangePayload

    in DiffUtil
    potatotips #70

    2020/07/10 rmakiyama

    View full-size slide

  2. ɹɹࣗݾ঺հ
    •຀ࢁྎ

    •Radiotalkגࣜձࣾ

    •AndroidΤϯδχΞ

    • @_rmakiyama

    • rmakiyama
    ref. https:"//github.com/rmakiyama/recyclerview-playground

    View full-size slide

  3. Ϧετͷࠩ෼ߋ৽

    View full-size slide

  4. Androidʹ͓͚ΔϦετUIͷදࣔ
    • RecyclerViewΛϨΠΞ΢τʹ௥Ճ

    • LayoutManagerΛઃఆ͠഑ஔํ๏Λࢦఆ

    • RecyclerView.AdapterΛηοτ

    View full-size slide

  5. RecyclerViewͷߋ৽

    View full-size slide

  6. RecyclerView.AdapterͰͷϦετߋ৽
    • notifyDataSetChanged

    • notifyItemChanged

    • notifyItemInserted

    • notifyItemMoved

    • bla bla bla…

    View full-size slide

  7. DiffUtil
    • ̎ͭͷϦετͷࠩ෼Λޮ཰Α͘ܭࢉ

    • ܭࢉ݁Ռ͔ΒRecyclerViewΛ͍͍ײ͡ʹߋ৽

    • ద੾ͳnotifyϝιουΛݺΜͰ͘ΕΔ

    • ద੾ͳΞχϝʔγϣϯ͕ݺ͹ΕΔ

    • ίʔϧόοΫΛ࣮૷ͯ͠࢖͏

    View full-size slide

  8. DiffUtil.Callback
    public abstract static class Callback {

    public abstract int getOldListSize();

    public abstract int getNewListSize();

    public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);

    public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);

    public Object getChangePayload(int oldItemPosition, int newItemPosition) {

    return null;

    }

    }

    View full-size slide

  9. DiffUtil.ItemCallback
    public abstract static class ItemCallback {

    public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);

    public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);

    public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {

    return null;

    }

    }

    View full-size slide

  10. DiffUtil.ItemCallback
    • areItemsTheSame

    • ΞΠςϜ͕ಉ͔͡൑ఆ݁͠ՌΛฦ͢

    • areContentsTheSame

    • ΞΠςϜͷத਎͕ಉ͔͡൑ఆ݁͠ՌΛฦ͢

    View full-size slide

  11. areItemsTheSame
    • ΞΠςϜ͕ಉ͔͡൑ఆ

    • ҰҙͳࣝผࢠͰൺֱ͢Δͱྑ͍

    • ྫͰ͸UserΛஔ͖׵͍͑ͯΔ

    • notifyItemRangeRemoved

    • notifyItemRangeInserted

    View full-size slide

  12. areContentsTheSame
    • ΞΠςϜͷத਎͕ಉ͔͡൑ఆ

    • areItemsTheSame͕trueͷͱ͖ධՁ

    • ྫͰ͸ImageUrl / isFavoriteΛมߋ

    • notifyItemRangeChanged

    View full-size slide

  13. DiffUtil.ItemCallback
    public abstract static class ItemCallback {

    public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);

    public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);

    public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {

    return null;

    }

    }

    View full-size slide

  14. getChangePayload
    • areItemsTheSame͕true͔ͭ

    areContentsTheSame͕falseͰݺ͹ΕΔ

    • มߋʹؔ͢ΔϖΠϩʔυΛΦϒδΣΫτͰฦͤΔ

    • ࡉ͔͍ߋ৽ͷ੍ޚ͕Մೳ

    View full-size slide

  15. ྫʣUserDiff.kt
    object UserDiff : DiffUtil.ItemCallback() {

    override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {

    return oldItem.id "== newItem.id

    }

    override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {

    return oldItem "== newItem

    }



    }
    data class User(

    val id: String,

    val imageUrl: String,

    val isFavorite: Boolean

    )

    View full-size slide

  16. ྫʣUserDiff.kt
    object UserDiff : DiffUtil.ItemCallback() {



    override fun getChangePayload(oldItem: User, newItem: User): Any? {

    return when {

    oldItem.imageUrl "!= newItem.imageUrl "-> Payload.ImageUrl(newItem.imageUrl)

    oldItem.isFavorite "!= newItem.isFavorite "-> Payload.IsFavorite(newItem.isFavorite)

    else "-> null

    }

    }

    sealed class Payload {

    data class ImageUrl(val value: String) : Payload()

    data class IsFavorite(val value: Boolean) : Payload()

    }

    }

    View full-size slide

  17. ListAdapterΛ࢖ͬͨ৔߹
    class UserListAdapter() : ListAdapter(UserDiff) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {



    return UserViewHolder(…)

    }

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {

    holder.bind(getItem(position))

    }



    }

    View full-size slide

  18. ListAdapterΛ࢖ͬͨ৔߹
    class UserListAdapter() : ListAdapter(UserDiff) {



    override fun onBindViewHolder(holder: UserViewHolder, position: Int, payloads: MutableList) {

    if (payloads.isEmpty()) {

    onBindViewHolder(holder, position)

    } else {

    payloads.distinct().forEach { payload "->

    when (payload) {

    is UserDiff.Payload.ImageUrl "-> holder.updateImageUrl(payload.value)

    is UserDiff.Payload.IsFavorite "-> holder.updateFavorite(payload.value)

    }

    }

    }

    }

    }

    View full-size slide

  19. ListAdapterΛ࢖ͬͨ৔߹
    class UserViewHolder(binding: ItemDummyBinding) : RecyclerView.ViewHolder(binding.root) {

    fun bind(user: User) {

    "// ViewʹUser৘ใΛදࣔ

    val binding = ItemDummyBinding.bind(itemView)

    binding.uuid.text = user.id

    loadImage(user.imageUrl)

    setImageRes(user.isFavorite)

    }

    fun updateImageUrl(imageUrl: String) = loadImage(imageUrl)

    fun updateFavorite(isFavorite: Boolean) = setImageRes(isFavorite)



    }

    View full-size slide

  20. w BGUFS
    isFavoriteมߋͰը૾͕ͪΒ͔ͭͳ͍
    • before

    View full-size slide

  21. ͓·͚ɿGroupieΛར༻͍ͯ͠Δ৔߹
    • Item#isSameAs

    • areItemsTheSame

    • Item#hasSameContentAs

    • areContentsTheSame

    • Item#getChangePayload

    • getChangePayload

    View full-size slide

  22. ·ͱΊ
    • DiffUtilΛ࢖͏͜ͱͰࠩ෼ߋ৽ָ͕ʹͳΔ

    • getChangePayloadΛ࢖͏ͱࡉ੍͔͘ޚՄೳ

    View full-size slide

  23. Appendix
    • https:"//developer.android.com/guide/topics/ui/layout/recyclerview

    • https:"//developer.android.com/reference/androidx/recyclerview/widget/DiffUtil

    • https:"//medium.com/tech-insider/diffutil-handling-recyclerview-smartly-
    ac3401d22903

    • https:"//satoshun.github.io/2019/11/groupie-diffutil/

    • https:"//github.com/DroidKaigi/conference-app-2020/pull/331

    • https:"//github.com/rmakiyama/recyclerview-playground

    View full-size slide