android / views-widgets-samples

Multiple samples showing the best practices in views-widgets on Android.
Apache License 2.0
5.04k stars 3.01k forks source link

DiffUtill not notifydata correctly #98

Open kingvhit opened 4 years ago

kingvhit commented 4 years ago

Hi, I have migrated to ViewPager2 as the main widget on my application. This time, I have some problems that can't find any idea.

  1. Firstly, I created the adapter using a list of GroupData, something like.
    
    data class GroupData(var groupName: String?, val tags: MutableList<TagData>)

@Parcelize data class TagData( var id: String?, var table: String?, var groupName: String?, var tagName: String, var display: String?, var isCanWritable: Boolean = false, var type: String? = null, var swipeOffSet: Int? = 0, var inputType: Int? = null, var defaultListValues: List? = null ) : Parcelable

This is my adapter

class GroupTabPagerAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {

private val differ = AsyncListDiffer(this, DIFF_CALLBACK)

companion object {
    val DIFF_CALLBACK = object : DiffUtil.ItemCallback<GroupData>() {
        override fun areItemsTheSame(oldItem: GroupData, newItem: GroupData) =
            oldItem.groupName == newItem.groupName

        override fun areContentsTheSame(oldItem: GroupData, newItem: GroupData): Boolean {
            val oldTags = oldItem.tags
            val newTags = newItem.tags
            // check collections aren't same
            if (oldTags.size != newTags.size) return false
            newTags.forEachIndexed { index, newTag ->
                val oldTag = oldTags.getOrNull(index) ?: return false
                if (newTag != oldTag) { return false }
            }
            return true
        }
    }
}

/**
 * Get Current list data of pager
 */
fun getCurrentList(): MutableList<GroupData> = differ.currentList

/**
 * Use DiffUtil to calculate and notify data
 */
fun summitList(newData: List<GroupData>) {
    differ.submitList(newData)
}

override fun getItemCount() = differ.currentList.size

override fun createFragment(position: Int) = GroupTabFragment.newInstance(
    differ.currentList[position].groupName,
    differ.currentList[position].tags
)

// override fun getItemId(position: Int) = differ.currentList[position].hashCode().toLong() // // override fun containsItem(itemId: Long) = // differ.currentList.map { it.hashCode().toLong() }.contains(itemId) }



OK, the problems are.
Our app has the `TabLayout`  and `ViewPager2`, that the tab will show the list of GroupName -> groupName
And GroupTabFragment is an item per page of ViewPager2, in this sample, I will put all the data of TagData list into `GroupTabFragment`
This fragment contains only `RecyclerView` and displays the list of TagData correctly on the first time summit list.

When the data has changed, something like I want to delete some specific group data, I remove some item of `List<GroupData>`, then `summitList` again.  It makes a tab layout change correctly 

Ex: 
ListGroupData : [{"A",tags: ["Tag A", "Tag B", "Tag C"]}, {"B",tags: ["Tag BB", "Tag CC", "Tag EE"]} ]

changed to 
ListGroupData : [{"B",tags: ["Tag BB", "Tag CC", "Tag EE"]} ]

It must display 1 tab named "B" and show the list inside Fragment with "Tag BB", "Tag CC", "Tag EE" data. But, now it displays correctly tab name, but still, keep the old list inside Fragment "Tag A", "Tag B", "Tag C"

-----
I have made another solution is comment-out 2 override-methods`getItemId` and `containsItem` and it can work correctly, checking the log means that the Fragment will be detached and re-attacted again. However, this Fragment is in not yet attached Activity state, I mean sometimes, when called getActivity(), it always returns `null` 

Does anyone have the same this issue?