使用 paging3 在 jetpack compose 中实现聊天应用程序接口, 该列表包含消息和日期模块,问题是当我创建一条新消息时,我收到该消息的响应,但现在我想将该消息添加到列表的开头。
@HiltViewModel
class TasklogsViewModel @Inject constructor(
private val tasklogsRepository: TasklogsRepository,
savedStateHandle: SavedStateHandle
) : BaseViewModel() {
private val taskId: Int? = savedStateHandle[TASK_ID_KEY]
val tasklogsPager = Pager(
PagingConfig(pageSize = 10)
) {
TasklogsPagingSource(taskId = taskId, tasklogsRepository)
}.flow.cachedIn(viewModelScope)
private val _uiState = MutableStateFlow<TasklogsUiState>(TasklogsUiState())
val uiState: StateFlow<TasklogsUiState> = _uiState
fun createTasklogs(message: String) = launchIO({
}) {
try {
val request = CreateTasklogRequest(
content = message,
type = "TEXT_MESSAGE",
taskId = taskId!!,
dateCreated = System.currentTimeMillis()
)
when (val res = tasklogsRepository.createTasklog(request)) {
is NetworkResponse.Error -> {
_uiState.update { currentState ->
currentState.copy(errorMessage = res.body.message)
}
}
is NetworkResponse.Success -> {
}
}
} catch (e: Exception) {
_uiState.update { currentState ->
currentState.copy(errorMessage = e.message)
}
}
}
}
class TasklogsPagingSource(
private val taskId: Int?,
private val tasklogsRepo: TasklogsRepository
) : PagingSource<Int, TasklogsComponentViewData>() {
override fun getRefreshKey(state: PagingState<Int, TasklogsComponentViewData>): Int? {
return state.anchorPosition?.let { position ->
val page = state.closestPageToPosition(position)
page?.prevKey?.minus(1) ?: page?.nextKey?.plus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TasklogsComponentViewData> {
return try {
val page = params.key ?: 0
if (taskId == null) throw Exception("TaskId is invalid")
when (val response = tasklogsRepo.getTasklogs(taskId, page)) {
is NetworkResponse.Error -> {
LoadResult.Error(Exception(response.body.message))
}
is NetworkResponse.Success -> {
val tasklogsList = mutableListOf<TasklogsComponentViewData>()
response.successBody.data.forEach { moduleData ->
when (moduleData.moduleId) {
"task_logs" -> {
val taskLog =
(moduleData.data as LogEntity.TasklogEntity).toTasklogViewData()
tasklogsList.add(taskLog)
}
"log_date" -> {
val logDate =
(moduleData.data as LogEntity.LogDateEntity).toLogDateViewData()
tasklogsList.add(logDate)
}
}
}
LoadResult.Page(
data = tasklogsList,
prevKey = null,
nextKey = if (response.successBody.data.isNotEmpty()) response.successBody.paginationKey else null
)
}
}
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
实际上无法找到解决方案,但想到再次强制获取列表,但我不想这样做。
您面临的问题是您直接从网络上进行分页,只有当页面在网络上发生更改时,您才必须再次拉取页面才有意义。
您应该研究类似
RemoteMediator
https://developer.android.com/reference/kotlin/androidx/paging/RemoteMediator
解决方案是添加本地数据库作为
PagingSource
,而不是直接添加 Web 服务。
来自 Web 服务的响应将填充数据库,对数据库的任何更改都会引发寻呼机运行。
当用户首次进入包含所有消息的页面时,您将使用
RemoteMediator
,这将填充数据库,从而将数据分页到屏幕。
每当您确定用户有新消息时,您都会执行相同的操作,当您收到消息时,将其插入数据库,寻呼机就会执行其操作。
此解决方案允许您的寻呼机仅关注数据库中的内容,这使得下游的一切变得更加简单。