Slide 1

Slide 1 text

getChangePayload in DiffUtil potatotips #70 2020/07/10 rmakiyama

Slide 2

Slide 2 text

ɹɹࣗݾ঺հ •຀ࢁྎ •Radiotalkגࣜձࣾ •AndroidΤϯδχΞ • @_rmakiyama • rmakiyama ref. https:"//github.com/rmakiyama/recyclerview-playground

Slide 3

Slide 3 text

Ϧετͷࠩ෼ߋ৽

Slide 4

Slide 4 text

Androidʹ͓͚ΔϦετUIͷදࣔ • RecyclerViewΛϨΠΞ΢τʹ௥Ճ • LayoutManagerΛઃఆ͠഑ஔํ๏Λࢦఆ • RecyclerView.AdapterΛηοτ

Slide 5

Slide 5 text

RecyclerViewͷߋ৽

Slide 6

Slide 6 text

RecyclerView.AdapterͰͷϦετߋ৽ • notifyDataSetChanged • notifyItemChanged • notifyItemInserted • notifyItemMoved • bla bla bla…

Slide 7

Slide 7 text

DiffUtil • ̎ͭͷϦετͷࠩ෼Λޮ཰Α͘ܭࢉ • ܭࢉ݁Ռ͔ΒRecyclerViewΛ͍͍ײ͡ʹߋ৽ • ద੾ͳnotifyϝιουΛݺΜͰ͘ΕΔ • ద੾ͳΞχϝʔγϣϯ͕ݺ͹ΕΔ • ίʔϧόοΫΛ࣮૷ͯ͠࢖͏

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

areItemsTheSame • ΞΠςϜ͕ಉ͔͡൑ఆ • ҰҙͳࣝผࢠͰൺֱ͢Δͱྑ͍ • ྫͰ͸UserΛஔ͖׵͍͑ͯΔ • notifyItemRangeRemoved • notifyItemRangeInserted

Slide 12

Slide 12 text

areContentsTheSame • ΞΠςϜͷத਎͕ಉ͔͡൑ఆ • areItemsTheSame͕trueͷͱ͖ධՁ • ྫͰ͸ImageUrl / isFavoriteΛมߋ • notifyItemRangeChanged

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

getChangePayload • areItemsTheSame͕true͔ͭ
 areContentsTheSame͕falseͰݺ͹ΕΔ • มߋʹؔ͢ΔϖΠϩʔυΛΦϒδΣΫτͰฦͤΔ • ࡉ͔͍ߋ৽ͷ੍ޚ͕Մೳ

Slide 15

Slide 15 text

ྫʣ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 )

Slide 16

Slide 16 text

ྫʣ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() } }

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

͓·͚ɿGroupieΛར༻͍ͯ͠Δ৔߹ • Item#isSameAs • areItemsTheSame • Item#hasSameContentAs • areContentsTheSame • Item#getChangePayload • getChangePayload

Slide 22

Slide 22 text

·ͱΊ • DiffUtilΛ࢖͏͜ͱͰࠩ෼ߋ৽ָ͕ʹͳΔ • getChangePayloadΛ࢖͏ͱࡉ੍͔͘ޚՄೳ

Slide 23

Slide 23 text

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