Kotlin中Room插入数据后如何刷新Paging3列表?
解决Paging3在Room数据变更时刷新列表的方案
方法一:利用Room数据监听自动触发PagingSource失效
1. 修改自定义ContactPagingSource,添加表监听
通过Room的InvalidationTracker监听ContactLocalModel表的变化,数据变更时标记PagingSource失效,触发重新加载:
class ContactPagingSource( private val contactDao: ContactDao, private val query: String, private val sort: SortingEnum = SortingEnum.ByName, private val appDatabase: AppDatabase // 传入Room数据库实例 ) : PagingSource<Int, ContactLocalModel>() { private val tableObserver = object : InvalidationTracker.Observer("ContactLocalModel") { override fun onInvalidated(tables: MutableSet<String>) { invalidate() // 数据变更时触发Paging重新加载 } } init { appDatabase.invalidationTracker.addObserver(tableObserver) } override fun getRefreshKey(state: PagingState<Int, ContactLocalModel>): Int? { // 优化刷新位置,回到当前页面附近 return state.anchorPosition?.let { anchorPos -> state.closestPageToPosition(anchorPos)?.prevKey?.plus(1) ?: state.closestPageToPosition(anchorPos)?.nextKey?.minus(1) } } override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ContactLocalModel> { val page = params.key ?: 0 return try { Timber.d("load: + $page") Timber.d("onQueryTextChange ContactPagingSource $query") val entities = if (query.isNotBlank()) { contactDao.getContactPagination( params.loadSize, page * params.loadSize, query = query, isOrderByName = sort.ordinal == SortingEnum.ByName.ordinal ) } else { contactDao.getContactPagination( params.loadSize, page * params.loadSize, query = "%", isOrderByName = sort.ordinal == SortingEnum.ByName.ordinal ) } if (page != 0) delay(1000) LoadResult.Page( data = entities, prevKey = if (page == 0) null else page - 1, nextKey = if (entities.isEmpty()) null else page + 1 ) } catch (e: Exception) { LoadResult.Error(e) } } override fun invalidate() { appDatabase.invalidationTracker.removeObserver(tableObserver) super.invalidate() } }
2. ViewModel中传入Database实例
创建Pager时传入AppDatabase,让PagingSource能监听表变化:
val randomContactPostFlow = Pager( PagingConfig( pageSize = 10, enablePlaceholders = false, initialLoadSize = 10, ) ) { val text = searchPaginationName.value ?: "" Timber.d("onQueryTextChange text $text") ContactPagingSource(contactDao, query = text, sort = enumVal, appDatabase = appDatabase) } .flow .cachedIn(viewModelScope)
方法二:通过触发查询条件变化刷新列表
如果不想监听数据库表,可在数据插入完成后,通过更新查询条件LiveData,让switchMap重新创建Pager:
1. ViewModel中添加刷新方法
确保searchPaginationName是MutableLiveData,提供刷新触发方法:
val searchPaginationName = MutableLiveData("") // 插入数据完成后调用此方法 fun refreshContacts() { searchPaginationName.value = searchPaginationName.value ?: "" }
2. 页面插入数据后触发刷新
从网页插入数据到Room完成后(比如返回联系人页面时),调用ViewModel的刷新方法:
// 示例:网页返回后执行 viewModel.refreshContacts()
此时searchPaginationName值变化会触发switchMap重新创建PagingSource,加载最新数据。
方法三:使用Room原生分页支持(推荐)
Room支持直接返回PagingSource,自动监听数据变化,无需自定义PagingSource:
1. 修改Room Dao查询
移除LIMIT和OFFSET,让Room自动处理分页:
@Query("SELECT * FROM ContactLocalModel WHERE name LIKE '%'||:query||'%' " + "OR pttNo LIKE :query " + "OR furigana LIKE :query ORDER BY " + "CASE WHEN :isOrderByName = 1 THEN LOWER(name) END ASC, " + "CASE WHEN :isOrderByName = 0 THEN pttNo END ASC, " + "id ASC" ) fun getContactPagingSource(query: String, isOrderByName: Boolean? = true): PagingSource<Int, ContactLocalModel>
2. ViewModel中直接使用Room的PagingSource
简化代码,Room会自动监听数据变更:
val randomContactPostFlow = searchPaginationName.switchMap { query -> Pager(PagingConfig( pageSize = 10, enablePlaceholders = false, initialLoadSize = 10, )) { contactDao.getContactPagingSource(query, sort.ordinal == SortingEnum.ByName.ordinal) }.flow }.cachedIn(viewModelScope)
内容的提问来源于stack exchange,提问作者varrun
相关产品推荐
相关产品推荐

