How to use AAC paging library with list size different than list size returned by Room database
Asked Answered
C

2

11

I'm trying to use the new Paging Library and Room as database but I'm facing a problem, the PagedList returned by the database is not supposed to be the same list sent to the UI, I map some entities before showing it to the user and during this map operation I change the list size (add items), apparently the Paging Library doesn't support this kind of operation because when I try to run the app I get this exception:

Caused by: java.lang.IllegalStateException: Invalid Function 'function_name' changed return size. This is not supported.

Looking at the paging library source code you see this method:

static <A, B> List<B> convert(Function<List<A>, List<B>> function, List<A> source) {
    List<B> dest = function.apply(source);
    if (dest.size() != source.size()) {
        throw new IllegalStateException("Invalid Function " + function
            + " changed return size. This is not supported.");
    }
    return dest;
}

Is there a workaround or something for dealing with when you add dynamic items to the PagedList before using it?

This is what I'm doing

DAO

@Query("SELECT * FROM table_name")
fun getItems(): DataSource.Factory<Int, Item>

LocalSource

fun getItems(): DataSource.Factory<Int, Item> {
    return database.dao().getItems()
        .mapByPage { map(it) } // This map operation changes the list size
}
Catholicism answered 19/9, 2018 at 14:6 Comment(2)
I'm facing problem like yours. If you find any solution for it, please answer your questionTalishatalisman
I filed an issue on the Android bug tracker for this. Please star it if this affects you issuetracker.google.com/issues/142890117Phosphocreatine
G
0

I face same problem and still looking for better solution.
In my case, I have to display 1 section before each users load from API and here is my workaround.

class UsersViewModel : ViewModel() {
    var items: LiveData<PagedList<RecyclerItem>>

    init {
        ...
        items = LivePagedListBuilder<Long, RecyclerItem>(
            sourceFactory.mapByPage { it -> mapUsersToRecyclerItem(it) }, config).build()
    }

    private fun mapUsersToRecyclerItem(users: MutableList<User>): List<RecyclerItem> {
        val numberOfSection = 1
        for (i in 0 until numberOfSection) {
            users.add(0, User()) // workaround, add empty user here
        }

        val newList = arrayListOf<RecyclerItem>()
        newList.add(SectionItem())
        for (i in numberOfSection until users.size) {
            val user = users[i]
            newList.add(UserItem(user.login, user.avatarUrl))
        }
        return newList
    }
}

My current user class

data class User(
    @SerializedName("login")
    val login: String,
    @SerializedName("id")
    val id: Long = 0,
    @SerializedName("avatar_url")
    val avatarUrl: String
) {
    constructor() : this("", 0, "")
}

Of course, to display Section, I will have another way without add it to RecyclerView data list (like using position only) but in my case user can remove item from the list so using position may hard to handle

Actually, I rollbacked to use old load more way (using EndlessRecyclerViewScrollListener) but hope it help

Gramercy answered 24/9, 2018 at 7:35 Comment(1)
Is your output newList gonna same size as initial input users? I thought exception will be thrown, if the returned output newList isn't same size as input users?Scoundrelly
A
0

I think I've found a solution..

Although it's a workaround it worked for me.

In my case, I was trying to create an alphabet sectioned list for a names like this:

**A - HeaderItem**
Aaron - Item
Anny - Item
**B - HeaderItem**
Bob - Item
Bil
**C - HeaderItem**
....

The items in ROOM are only the names of course, when I am trying to map the paged items and add the sections headers it change the list size and I am getting the same error.

What I did is, the HeaderItem object wraps an Item like this:

First, all Items are implementing the ListItem interface

interface ListItem{
 const val HEADER = 0
 const val ITEM = 1
 fun getItemType() : Int
}

Then the header items looks like this

class HeaderItem(val headerTitle : String, val item : Item) : ListItem {
  @override
  fun getItemType() : Int {
    return ListItem.HEADER
  }
}

Then when I map the items, when adding a HeaderItem, it takes an Item in it, this way the mapped PagedList size doesn't change. Now I am not getting this exception.

However, This creates some additional work as I had to set the HeaderItem decoration explicitly and also in the adapter, when bind the header item I had to take care the internal Item + all its logic such as click listeners etc'.

I will be happy if there will be support for changes in the list size out of the box.

Aubyn answered 4/7, 2019 at 6:48 Comment(1)
My preferred way doing this, is not adding items, but instead use developer.android.com/reference/androidx/recyclerview/widget/… So, your HeaderItems are not items of the adapter and don't change the size of the list.Malediction

© 2022 - 2024 — McMap. All rights reserved.