2

I'm making an application that displays a list of all GitHub users. For this I use Retrofit, Paging Library 3 + Room. Everything works well, however, after the very first data load, the list jumps to the pageSize size, this does not happen when you re-enter the application, only on the first load (this is what it looks like). How to fix it?

Dao

@Dao
interface UsersDao {    
    @Query("SELECT * FROM ${UserListItem.TABLE_NAME}")
    fun getUsers(): PagingSource<Int, UserListItem>
}

ViewModel

private const val DEFAULT_PAGE_SIZE = 30

class UserListViewModel(
    private val dao: UsersDao,
    remoteMediator: UserListRemoteMediator,
) : ViewModel() {

    @OptIn(ExperimentalPagingApi::class)
    private val pager = Pager(
        config = PagingConfig(
            pageSize = DEFAULT_PAGE_SIZE,
            enablePlaceholders = true,
            maxSize = DEFAULT_PAGE_SIZE * 3,
        ),
        remoteMediator = remoteMediator,
        pagingSourceFactory = { dao.getUsers() },
    )

    val users: Flow<PagingData<UserListItem>> = pager.flow.cachedIn(viewModelScope)
}

RemoteMediator

@OptIn(ExperimentalPagingApi::class)
class UserListRemoteMediator(
    private val database: UsersDatabase,
    private val backend: UsersApi,
    private val network: Utils.Network,
) : RemoteMediator<Int, UserListItem>() {

    private val dao = database.getUsersDao()

    override suspend fun initialize(): InitializeAction {
        return if (network.isInternetConnected)
            InitializeAction.LAUNCH_INITIAL_REFRESH
        else
            InitializeAction.SKIP_INITIAL_REFRESH
    }

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, UserListItem>
    ): MediatorResult {
        try {
            val maxId = when (loadType) {
                LoadType.REFRESH -> {
                    null
                }
                LoadType.PREPEND -> {
                    return MediatorResult.Success(endOfPaginationReached = true)
                }
                LoadType.APPEND -> {
                    dao.getMaxId()
                }
            }

            val response = backend.getUsers(
                after = maxId,
                amount = state.config.pageSize
            )

            if (response.isSuccessful) {
                return handleSuccessState(loadType, response)
            }

            if (response.isRequestsLimitExceeded) {
                return handleRequestLimitExceededState(response)
            }

            return errorWithMessage(message = "Unhandled response code: ${response.code()}")
        } catch (e: IOException) {
            return MediatorResult.Error(e)
        } catch (e: HttpException) {
            return MediatorResult.Error(e)
        }
    }
}

adapter setting method in fragment:

private fun setUpAdapter(binding: FragmentUserListBinding, adapter: UserPagingDataAdapter) {
    binding.list.adapter = adapter.withLoadStateHeaderAndFooter(
        header = LoadStateAdapter(adapter::retry),
        footer = LoadStateAdapter(adapter::retry)
    )

    viewLifecycleOwner.lifecycleScope.launch {
        viewModel.users.collectLatest {
            adapter.submitData(it)
        }
    }
 }

fragment layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/user_list_swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:context=".view.userlist.UserListFragment" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

LoadStateAdapter

class LoadStateAdapter(
    private val retry: () -> Unit,
) : LoadStateAdapter<LoadStateViewHolder>() {

    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        holder.bind(loadState)
    }

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = LoadStateItemBinding.inflate(layoutInflater, parent, false)
        return LoadStateViewHolder(binding, retry)
    }
}

LoadStateViewHolder

class LoadStateViewHolder(
    private val binding: LoadStateItemBinding,
    retry: () -> Unit,
) : RecyclerView.ViewHolder(binding.root) {

    init {
        binding.retryButton.setOnClickListener { retry() }
    }

    fun bind(loadState: LoadState) {
        if (loadState is LoadState.Error) {
            binding.errorMessage.text = loadState.error.localizedMessage
        }

        binding.errorMessage.isVisible = loadState is LoadState.Error
        binding.progressBar.isVisible = loadState is LoadState.Loading
        binding.retryButton.isVisible = loadState is LoadState.Error
    }
}
Empty
  • 23
  • 4

0 Answers0