In my sample app I use Paging3 to show a list of students (by page), I want the RecyclerView
to show the last page and auto-scroll-to-bottom. My setup is like this:
PagingSource:
<code>class StudentPagingSource(
private val studentDao: StudentDao
) : PagingSource<Int, Student>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Student> {
val totalCount = studentDao.getStudentsCount()
val initialPage = totalCount / params.loadSize //<- the last page
val page = params.key ?: initialPage
return try {
val students = studentDao.getPagedStudents(params.loadSize, page * params.loadSize)
LoadResult.Page(
data = students,
prevKey = if (page == 0) null else page - 1,
nextKey = if (students.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, Student>): Int? {
return state.anchorPosition?.let {
val anchorPage = state.closestPageToPosition(it)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
</code>
<code>class StudentPagingSource(
private val studentDao: StudentDao
) : PagingSource<Int, Student>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Student> {
val totalCount = studentDao.getStudentsCount()
val initialPage = totalCount / params.loadSize //<- the last page
val page = params.key ?: initialPage
return try {
val students = studentDao.getPagedStudents(params.loadSize, page * params.loadSize)
LoadResult.Page(
data = students,
prevKey = if (page == 0) null else page - 1,
nextKey = if (students.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, Student>): Int? {
return state.anchorPosition?.let {
val anchorPage = state.closestPageToPosition(it)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
</code>
class StudentPagingSource(
private val studentDao: StudentDao
) : PagingSource<Int, Student>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Student> {
val totalCount = studentDao.getStudentsCount()
val initialPage = totalCount / params.loadSize //<- the last page
val page = params.key ?: initialPage
return try {
val students = studentDao.getPagedStudents(params.loadSize, page * params.loadSize)
LoadResult.Page(
data = students,
prevKey = if (page == 0) null else page - 1,
nextKey = if (students.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, Student>): Int? {
return state.anchorPosition?.let {
val anchorPage = state.closestPageToPosition(it)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
Repository:
<code>class StudentRepository(
database: StudentDatabase
) {
companion object {
const val PAGE_SIZE = 30
}
private val studentDao = database.studentDao
fun getPagedStudents(): Flow<PagingData<Student>> {
val pager = Pager(
config = PagingConfig(
pageSize = PAGE_SIZE,
enablePlaceholders = false,
initialLoadSize = PAGE_SIZE
),
pagingSourceFactory = { StudentPagingSource(studentDao) }
)
return pager.flow
}
suspend fun saveStudents(vararg student: Student) = studentDao.insert(*student)
}
</code>
<code>class StudentRepository(
database: StudentDatabase
) {
companion object {
const val PAGE_SIZE = 30
}
private val studentDao = database.studentDao
fun getPagedStudents(): Flow<PagingData<Student>> {
val pager = Pager(
config = PagingConfig(
pageSize = PAGE_SIZE,
enablePlaceholders = false,
initialLoadSize = PAGE_SIZE
),
pagingSourceFactory = { StudentPagingSource(studentDao) }
)
return pager.flow
}
suspend fun saveStudents(vararg student: Student) = studentDao.insert(*student)
}
</code>
class StudentRepository(
database: StudentDatabase
) {
companion object {
const val PAGE_SIZE = 30
}
private val studentDao = database.studentDao
fun getPagedStudents(): Flow<PagingData<Student>> {
val pager = Pager(
config = PagingConfig(
pageSize = PAGE_SIZE,
enablePlaceholders = false,
initialLoadSize = PAGE_SIZE
),
pagingSourceFactory = { StudentPagingSource(studentDao) }
)
return pager.flow
}
suspend fun saveStudents(vararg student: Student) = studentDao.insert(*student)
}
ViewModel:
<code>class FirstViewModel(
application: Application,
private val studentRepository: StudentRepository
) : AndroidViewModel(application) {
private val uiScope = viewModelScope
val students = studentRepository.getPagedStudents().cachedIn(uiScope)
}
</code>
<code>class FirstViewModel(
application: Application,
private val studentRepository: StudentRepository
) : AndroidViewModel(application) {
private val uiScope = viewModelScope
val students = studentRepository.getPagedStudents().cachedIn(uiScope)
}
</code>
class FirstViewModel(
application: Application,
private val studentRepository: StudentRepository
) : AndroidViewModel(application) {
private val uiScope = viewModelScope
val students = studentRepository.getPagedStudents().cachedIn(uiScope)
}
Fragment:
<code>override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
studentAdapter = getStudentAdapter()
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
setupOptionsMenu()
binding.recyclerView.adapter = studentAdapter
viewModel.students.collectLatestOnStarted(viewLifecycleOwner) {
studentAdapter.submitData(it)
}
return binding.root
}
</code>
<code>override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
studentAdapter = getStudentAdapter()
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
setupOptionsMenu()
binding.recyclerView.adapter = studentAdapter
viewModel.students.collectLatestOnStarted(viewLifecycleOwner) {
studentAdapter.submitData(it)
}
return binding.root
}
</code>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
studentAdapter = getStudentAdapter()
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
setupOptionsMenu()
binding.recyclerView.adapter = studentAdapter
viewModel.students.collectLatestOnStarted(viewLifecycleOwner) {
studentAdapter.submitData(it)
}
return binding.root
}
Now the paging feature works fine, it loads and shows the last page at startup but not scrolling to the bottom, same as when new data is added.
So how can I make it scoll to bottom at startup and auto-scroll-to-bottom when new data is added?